diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..6a84935a2 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,59 @@ + +image: "harbor.rongcloud.net/library/rust/wcdb:0.1.4" + +variables: + CARGO_HOME: ${CI_PROJECT_DIR}/CargoHome + GIT_CLEAN_FLAGS: "-ffdx --exclude=CargoHome --exclude=src/rust/target/" + +cache: + paths: + - src/rust/Cargo.lock + +stages: + - check + - test + - clean_cache + +run_check_unsafe_keywords: + stage: check + script: | + echo "Check danger function..." + if find src/rust/wcdb/src -name '*.rs' | xargs -P4 grep -n '\bc_long\b|\.unwrap()\|\.expect(\|unreachable!\|panic!'; then + echo "Error: banned c_long/unwrap/expect/unreachable!/panic!" + exit 1 + fi + rules: + - changes: + - "src/rust/wcdb/src/**/*.rs" + +run_test: + stage: test + before_script: + - export http_proxy=http://172.19.23.87:7890 + - export https_proxy=http://172.19.23.87:7890 + - export NO_PROXY=localhost,127.0.0.1,rsproxy.cn + - git submodule update --init openssl sqlcipher zstd + - npm config set registry https://registry.npmmirror.com + - npm install --save-dev @commitlint/config-conventional @commitlint/cli + script: + - export CARGO_HOME=${CI_PROJECT_DIR}/CargoHome + - export RUSTFLAGS="-D warnings -A unused -A deprecated" + - export CC=`which cc` + - export CXX=`which c++` + - cd src/rust + - echo "${CI_COMMIT_MESSAGE}" | npx commitlint + - autocorrect --lint + - FILES=$(git ls-files '*.c' '*.cpp' '*.h') + - for file in $FILES; do + clang-format "$file" | colordiff -u "$file" -; + done + - cargo fmt -- --check + - cargo test -- --test-threads=1 + +clean_cache: + stage: clean_cache + script: + - echo "🧹 Cleaning build cache directories..." + - du -sh target + - rm -rf target + when: manual \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d5d013868..27ef0d347 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -55,6 +55,10 @@ file(GLOB_RECURSE WCDB_BRIDGE_SRC ${WCDB_SRC_DIR}/bridge/winqbridge/*.[ch]pp ) recursive_subdirs(WCDB_BRIDGE_INCLUDES ${WCDB_SRC_DIR}/bridge) +if (WIN32) + # Ignore symlinks in src/brideg/include for windows to avoid redefinition errors + list(FILTER WCDB_BRIDGE_INCLUDES EXCLUDE REGEX ".*[\\\\/]include") +endif() # Copy all headers to include folder file(GLOB_RECURSE WCDB_PUBLIC_HEADERS @@ -530,12 +534,21 @@ elseif (APPLE AND NOT WCONAN_MODE) target_sources(${TARGET_NAME} PUBLIC ${WCDB_PUBLIC_HEADERS}) file(STRINGS "../VERSION" WCDB_VERSION) message(STATUS "Xcode ${TARGET_NAME}.framework version ${WCDB_VERSION}") - set_target_properties(${TARGET_NAME} PROPERTIES - FRAMEWORK TRUE - FRAMEWORK_VERSION ${WCDB_VERSION} - MACOSX_FRAMEWORK_IDENTIFIER com.tencent.${TARGET_NAME} - PUBLIC_HEADER "${WCDB_PUBLIC_HEADERS}" - ) + if(BUILD_FROM_CARGO) + message(STATUS "---- BUILD FOR APPLE FROM CARGO ----") + if (BUILD_SHARED_LIBS) + target_link_libraries(${TARGET_NAME} PRIVATE crypto z) + else() + target_link_libraries(${TARGET_NAME} PUBLIC crypto z) + endif() + else() + set_target_properties(${TARGET_NAME} PROPERTIES + FRAMEWORK TRUE + FRAMEWORK_VERSION ${WCDB_VERSION} + MACOSX_FRAMEWORK_IDENTIFIER com.tencent.${TARGET_NAME} + PUBLIC_HEADER "${WCDB_PUBLIC_HEADERS}" + ) + endif() elseif (OHOS) message(STATUS "---- BUILD FOR OPENHARMONY ----") target_link_libraries(${TARGET_NAME} PUBLIC diff --git a/src/bridge/base/ErrorBridge.cpp b/src/bridge/base/ErrorBridge.cpp index e073405b0..cee3efd3c 100644 --- a/src/bridge/base/ErrorBridge.cpp +++ b/src/bridge/base/ErrorBridge.cpp @@ -29,6 +29,8 @@ #include "Notifier.hpp" #include "ObjectBridge.hpp" +#include + long WCDBErrorGetCode(CPPError error) { WCDBGetObjectOrReturnValue(error, WCDB::Error, cppError, INT_MAX); diff --git a/src/common/core/fts/AutoMergeFTSIndexConfig.hpp b/src/common/core/fts/AutoMergeFTSIndexConfig.hpp index a7c1c8bdc..60568a812 100644 --- a/src/common/core/fts/AutoMergeFTSIndexConfig.hpp +++ b/src/common/core/fts/AutoMergeFTSIndexConfig.hpp @@ -24,6 +24,7 @@ #include "Config.hpp" #include "Lock.hpp" +#include #include diff --git a/src/common/platform/CrossPlatform.c b/src/common/platform/CrossPlatform.c index 48216d229..2611adbf3 100644 --- a/src/common/platform/CrossPlatform.c +++ b/src/common/platform/CrossPlatform.c @@ -28,7 +28,7 @@ #include #include -#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30 +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 32 #include #define gettid() syscall(SYS_gettid) #endif diff --git a/src/rust/.cargo/config.toml b/src/rust/.cargo/config.toml new file mode 100644 index 000000000..c7113ec7d --- /dev/null +++ b/src/rust/.cargo/config.toml @@ -0,0 +1,13 @@ +[source.crates-io] +replace-with = 'rsproxy-sparse' +[source.rsproxy] +registry = "https://rsproxy.cn/crates.io-index" +[source.rsproxy-sparse] +registry = "sparse+https://rsproxy.cn/index/" +[registries.rsproxy] +index = "https://rsproxy.cn/crates.io-index" +[net] +git-fetch-with-cli = true + +[build] +rustflags = ["-D", "warnings", "-A", "unused", "-A", "deprecated"] diff --git a/src/rust/.gitignore b/src/rust/.gitignore new file mode 100644 index 000000000..df6af92cf --- /dev/null +++ b/src/rust/.gitignore @@ -0,0 +1,4 @@ +target/ +cmake-build-*/ + +*.sqlite3*1 \ No newline at end of file diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock new file mode 100644 index 000000000..b907cb195 --- /dev/null +++ b/src/rust/Cargo.lock @@ -0,0 +1,924 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc 0.2.175", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "bitflags", + "clap_lex", + "indexmap", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "criterion" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +dependencies = [ + "anes", + "atty", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "examples" +version = "0.1.0" +dependencies = [ + "criterion", + "lazy_static", + "once_cell", + "phf", + "rand", + "wcdb", + "wcdb_derive", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc 0.2.175", + "wasi", +] + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc 0.2.175", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "libc" +version = "1.0.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7222002e5385b4d9327755661e3847c970e8fbf9dea6da8c57f16e8cfbff53a8" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi 0.5.2", + "libc 0.2.175", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc 0.2.175", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.143" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wcdb" +version = "0.1.0" +dependencies = [ + "cmake", + "lazy_static", + "libc 1.0.0-alpha.1", + "num-derive", + "num-traits", + "num_cpus", +] + +[[package]] +name = "wcdb_derive" +version = "0.1.0" +dependencies = [ + "darling", + "once_cell", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml new file mode 100644 index 000000000..1b30563df --- /dev/null +++ b/src/rust/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["examples", "wcdb_derive", "wcdb"] +resolver = "2" diff --git a/src/rust/README.md b/src/rust/README.md new file mode 100644 index 000000000..82664ddad --- /dev/null +++ b/src/rust/README.md @@ -0,0 +1,54 @@ +# WCDB for Rust 项目说明文档 + +## 编码规范 + +Rust 语言接口适配以源仓库自带的 Java 接口适配为蓝本进行翻译,遵循以下原则: + +1. 如非必要,所有代码提交集中在 src/rust,也就是当前目录下,外面代码尽量保持不变。 +2. 核心工作是将 Java 代码翻译成 Rust 语言,同时将 JNI 翻译成 C/C++ 桥接。 +3. 目录结构、文件名、函数名、变量名、代码逻辑等,在遵循 Rust 语言风格基础上,仅做必要更名,其他尽量保持一致,以便于对照查看。 +4. 由于 Rust 不支持面向对象,原来的继承改为组合,将子类函数以 Trait 方式提供,尽量保持原有调用逻辑和多态性。 +5. 由于 Rust 不支持异常,原来的异常处理改为返回 Result 类型,尽量保持原有调用逻辑。 +6. 由于 Rust 有严格的所有权归属,对于 Java 的自引用、返回对象归属不明确的情况,可以进行必要的重构。 +7. 保持最小可见性,内部尽量使用 pub(crate/super) 修饰符,避免暴露不必要的接口、内部变量。 +8. 由于接口众多,所有 Rust 函数书写顺序需要跟 Java 保持一致,方便对照查看。 +9. 定义 struct 时,将 Java 对象的父类,以变量方式放在第一行,Trait 实现按照 系统 -> 先祖类 -> 父类 -> 自身实现 的顺序依次进行,如: + ``` + pub struct Database { + handle_orm_operation: HandleORMOperation, // 第一行 + // ... + } + + // 系统特征 + impl Display for Database { ... } + + // 先祖类特征 + impl CppObjectTrait for Database { ... } + + // 祖类特征 + impl HandleOperationTrait for Database { ... } + + // 父类特征 + impl HandleORMOperationTrait for Database { ... } + + // 自身实现 + impl Database { ... } + ``` +10. 依托 Demo/TestCase 逐步翻译,避免没有调用的逻辑实现出现,一来确保代码翻译正确,二来有不少接口调用频率不高,可以推迟实现或不实现,将有限精力用在核心接口上。 +11. 提交要求满足 `cargo fmt -- --check` 检查。除此以外,空行需要跟现有风格对齐,如函数之间有空行,逻辑块与块之间有空行,勿多勿少。 +12. 其余未详述细节,参照现有代码规范编写即可。 +13. 根目录下执行 `cargo bench` 开始执行性能测试,输出内容在控制台和 `wcdb_rust/src/rust/target/criterion` 目录中。 +14. 所有包含 CppObject 的类,不能实现 `Clone` 宏:`Clone` 宏在 clone 时底层的 C 指针被复制,出现两个 Rust 对象持有相同的 C 指针,会发生重复释放的崩溃。 + +## CI 检查点 +1. [Git Commit Lint](https://github.com/conventional-changelog/commitlint) (另附:结尾标点符号必须是 [.!?] 之一) +2. [中英文排版规范](https://github.com/huacnlee/autocorrect) +3. [clang-format](cpp/.clang-format) +4. [cargo fmt](https://github.com/rust-lang/rustfmt) +5. 执行 Rust 集成测试用例。 + +## 测试用例执行 +1. Rust 项目的单元测试默认采用单线程执行,命令为:cargo test -p examples -- --test-threads=1。原因有两方面:一是 Java 代码的单元测试通常是单线程执行;二是某些测试场景依赖数据库的打开/关闭状态等,导致无法并行执行。 + +## 数据表宏展开 +1. 可以使用 `cargo expand` 命令展开数据表宏,生成对应的 Rust 代码,方便理解和调试调用代码。如:`cargo expand -p examples --example demo`。 \ No newline at end of file diff --git a/src/rust/commitlint.config.js b/src/rust/commitlint.config.js new file mode 100644 index 000000000..f5a63bb1a --- /dev/null +++ b/src/rust/commitlint.config.js @@ -0,0 +1,13 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: { + 'header-max-length': [2, 'always', 100], + 'subject-full-stop': [2, 'always', '.'], + }, + parserPreset: { + parserOpts: { + headerPattern: /^(\w*)(?:\((.*)\))?: (.*[.!?])$/, + headerCorrespondence: ['type', 'scope', 'subject'], + }, + }, +}; diff --git a/src/rust/cpp/.clang-format b/src/rust/cpp/.clang-format new file mode 100644 index 000000000..25be4bbf0 --- /dev/null +++ b/src/rust/cpp/.clang-format @@ -0,0 +1,12 @@ +BasedOnStyle: Chromium +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^".*\.h"' + Priority: 1 + - Regex: '^<.*\.h>' + Priority: 2 +AllowShortFunctionsOnASingleLine: None +ColumnLimit: 100 diff --git a/src/rust/cpp/CMakeLists.txt b/src/rust/cpp/CMakeLists.txt new file mode 100644 index 000000000..5b570322c --- /dev/null +++ b/src/rust/cpp/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.21) +project(WCDBRust) + +set(CMAKE_CXX_STANDARD 14) + +include(../../utility.cmake) + +set(TARGET_NAME "WCDB") +set(WCDB_BRIDGE ON) +set(SKIP_WCONAN ON) +set(BUILD_SHARED_LIBS OFF) + +add_subdirectory(../../ ${CMAKE_CURRENT_BINARY_DIR}/wcdb) + +set(WCDB_RUST_SRC_DIR + ${CMAKE_CURRENT_LIST_DIR}/base + ${CMAKE_CURRENT_LIST_DIR}/core + ${CMAKE_CURRENT_LIST_DIR}/winq) + +set(WCDB_RUST_SRC) +set(WCDB_RUST_INCLUDE) + +foreach (DIR ${WCDB_RUST_SRC_DIR}) + file(GLOB_RECURSE DIR_SRC + ${DIR}/*.cpp + ${DIR}/*.c + ${DIR}/*.h) + list(APPEND WCDB_RUST_SRC ${DIR_SRC}) + + recursive_subdirs(DIR_INCLUDE ${DIR}) + list(APPEND WCDB_RUST_INCLUDE ${DIR_INCLUDE}) +endforeach () + +recursive_subdirs(WCDB_RUST_INCLUDES ${WCDB_RUST_SRC_DIR}) + +target_sources(${TARGET_NAME} PUBLIC ${WCDB_RUST_SRC}) +target_include_directories(${TARGET_NAME} PUBLIC ${WCDB_RUST_INCLUDE}) diff --git a/src/rust/cpp/base/ErrorRust.c b/src/rust/cpp/base/ErrorRust.c new file mode 100644 index 000000000..23a70fc5e --- /dev/null +++ b/src/rust/cpp/base/ErrorRust.c @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ErrorRust.h" + +#include "ErrorBridge.h" +#include "assert.h" + +extern void WCDBExceptionAddInfo(void* key_values_raw, + const char* key, + int value_type, + long long int_value, + double double_value, + const char* string_value); + +int WCDBRustErrorClassMethod(getLevel, void* error) { + WCDBRustBridgeStruct(CPPError, error); + return WCDBErrorGetLevel(errorStruct); +} + +int WCDBRustErrorClassMethod(getCode, void* error) { + WCDBRustBridgeStruct(CPPError, error); + return WCDBErrorGetCode(errorStruct); +} + +const char* WCDBRustErrorClassMethod(getMessage, void* error) { + WCDBRustBridgeStruct(CPPError, error); + return WCDBErrorGetMsg(errorStruct); +} + +void WCDBRustErrorEnumerateInfoCallback(void* context, const char* key, CPPCommonValue value) { + long long intValue = 0; + double doubleValue = 0; + const char* stringValue = NULL; + switch (value.type) { + case WCDBBridgedType_Int: + intValue = (long long)value.intValue; + break; + case WCDBBridgedType_Double: + doubleValue = value.doubleValue; + break; + case WCDBBridgedType_String: + stringValue = (const char*)value.intValue; + break; + default: + break; + } + WCDBExceptionAddInfo(context, key, value.type, intValue, doubleValue, stringValue); +} + +void WCDBRustErrorObjectMethod(enumerateInfo, void* map, void* error) { + WCDBRustBridgeStruct(CPPError, error); + WCDBErrorEnumerateAllInfo(errorStruct, map, + (StringViewMapEnumerator)&WCDBRustErrorEnumerateInfoCallback); +} diff --git a/src/rust/cpp/base/ErrorRust.h b/src/rust/cpp/base/ErrorRust.h new file mode 100644 index 000000000..dac92e008 --- /dev/null +++ b/src/rust/cpp/base/ErrorRust.h @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustErrorFuncName(funcName) WCDBRust(Error, funcName) +#define WCDBRustErrorObjectMethod(funcName, ...) WCDBRustObjectMethod(Error, funcName, __VA_ARGS__) +#define WCDBRustErrorClassMethodWithNoArg(funcName) WCDBRustClassMethodWithNoArg(Error, funcName) +#define WCDBRustErrorClassMethod(funcName, ...) WCDBRustClassMethod(Error, funcName, __VA_ARGS__) + +int WCDBRustErrorClassMethod(getLevel, void* error); + +int WCDBRustErrorClassMethod(getCode, void* error); + +const char* WCDBRustErrorClassMethod(getMessage, void* error); + +void WCDBRustErrorObjectMethod(enumerateInfo, void* map, void* error); diff --git a/src/rust/cpp/base/WCDBRust.c b/src/rust/cpp/base/WCDBRust.c new file mode 100644 index 000000000..1687fbff3 --- /dev/null +++ b/src/rust/cpp/base/WCDBRust.c @@ -0,0 +1,424 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WCDBRust.h" + +#include "ObjectBridge.h" +#include "assert.h" + +#include + +// #define LIKELY(exp) (__builtin_expect((exp) != 0, true)) +// #define UNLIKELY(exp) (__builtin_expect((exp) != 0, false)) +// +// JavaVM* g_vm = NULL; +// +// void WCDBRustDestructContext(jobject config) +//{ +// WCDBRustTryGetEnvOr(return ); +// (*env)->DeleteGlobalRef(env, config); +// WCDBRustTryDetach; +// } + +void WCDBRustBase_releaseObject(void* cppObject) { + WCDBReleaseCPPObject((CPPObject*)cppObject); +} + +void* WCDBRustCreateGlobalRef(size_t size) { + void* ptr = malloc(size); + memset(ptr, 0, size); + return ptr; +} + +// jclass g_databaseClass = NULL; +// jclass g_handleClass = NULL; +// jclass g_exceptionClass = NULL; +// +// void WCDBRustInitJClasses(JNIEnv* env) +//{ +// g_databaseClass = (*env)->FindClass(env, "com/tencent/wcdb/core/Database"); +// WCDBRustCreateGlobalRef(g_databaseClass); +// assert(g_databaseClass != NULL); +// +// g_handleClass = (*env)->FindClass(env, "com/tencent/wcdb/core/Handle"); +// WCDBRustCreateGlobalRef(g_handleClass); +// assert(g_handleClass != NULL); +// +// g_exceptionClass = (*env)->FindClass(env, "com/tencent/wcdb/base/WCDBException"); +// WCDBRustCreateGlobalRef(g_exceptionClass); +// assert(g_exceptionClass != NULL); +// } +// +// jclass WCDBRustGetDatabaseClass() +//{ +// return g_databaseClass; +// } +// +// jclass WCDBRustGetHandleClass() +//{ +// return g_handleClass; +// } +// +// jclass WCDBRustGetExceptionClass() +//{ +// return g_exceptionClass; +// } +// +// static jsize utf16_to_utf8_length(const jchar* src, jsize src_len); +// static void utf16_to_utf8(const jchar* src, jsize src_len, char* dst, jsize dst_len); +// static jsize utf8_to_utf16_length(const char* u8str, jsize u8len); +// static jchar* utf8_to_utf16(const char* u8str, jsize u8len, jchar* u16str, jsize u16len); +// +// void WCDBRustGetUTF8String(JNIEnv* env, jstring value, char** utf8String, const jchar** +// utf16String, bool critical) +//{ +// if (UNLIKELY(value == NULL)) { +// *utf8String = NULL; +// return; +// } +// jsize utf16Length = (*env)->GetStringLength(env, value); +// if (UNLIKELY(utf16Length == 0)) { +// *utf8String = NULL; +// return; +// } +// if (LIKELY(critical)) { +// *utf16String = (*env)->GetStringCritical(env, value, 0); +// } else { +// *utf16String = (*env)->GetStringChars(env, value, 0); +// } +// jsize utf8Length = utf16_to_utf8_length(*utf16String, utf16Length); +// char** preAllocSlot = WCDBPreAllocStringMemorySlot(1); +// if (UNLIKELY(preAllocSlot == NULL)) { +// *utf8String = NULL; +// return; +// } +// WCDBAllocStringMemory(preAllocSlot, (int) utf8Length); +// if (UNLIKELY(*preAllocSlot == NULL)) { +// *utf8String = NULL; +// return; +// } +// *utf8String = *preAllocSlot; +// utf16_to_utf8(*utf16String, utf16Length, *utf8String, utf8Length); +// } +// +// void WCDBRustGetUTF8StringArray(JNIEnv* env, jobjectArray value, char*** stringArray, int* +// length) +//{ +// if (UNLIKELY(value == NULL)) { +// return; +// } +// int valueLength = (*env)->GetArrayLength(env, value); +// if (UNLIKELY(valueLength <= 0)) { +// return; +// } +// char** preAllocSlot = WCDBPreAllocStringMemorySlot(valueLength); +// if (UNLIKELY(preAllocSlot == NULL)) { +// return; +// } +// for (int i = 0; i < valueLength; i++) { +// jstring curString = (jstring) (*env)->GetObjectArrayElement(env, value, i); +// if (UNLIKELY(curString == NULL)) { +// continue; +// } +// jsize utf16Length = (*env)->GetStringLength(env, curString); +// const jchar* utf16String = (*env)->GetStringCritical(env, curString, 0); +// jsize utf8Length = utf16_to_utf8_length(utf16String, utf16Length); +// char** curSlot = preAllocSlot + i; +// WCDBAllocStringMemory(curSlot, utf8Length); +// if (UNLIKELY(*curSlot == NULL)) { +// (*env)->ReleaseStringCritical(env, curString, utf16String); +// (*env)->DeleteLocalRef(env, curString); +// WCDBClearAllocatedMemory(valueLength); +// return; +// } +// utf16_to_utf8(utf16String, utf16Length, *curSlot, utf8Length); +// (*env)->ReleaseStringCritical(env, curString, utf16String); +// (*env)->DeleteLocalRef(env, curString); +// } +// *length = valueLength; +// *stringArray = preAllocSlot; +// } +// +// jstring WCDBRustCreateJString(JNIEnv* env, const char* utf8String) +//{ +// if (utf8String == NULL) { +// return NULL; +// } +// jsize u8len = (jsize) strlen(utf8String); +// jsize utf16Length = utf8_to_utf16_length(utf8String, u8len); +// jchar* utf16Buffer = NULL; +// bool needFree = false; +// if (LIKELY(utf16Length < 1000)) { +// utf16Buffer = alloca((utf16Length + 1) * sizeof(jchar)); +// } else { +// utf16Buffer = malloc((utf16Length + 1) * sizeof(jchar)); +// needFree = true; +// } +// if (UNLIKELY(utf16Buffer == NULL)) { +// return NULL; +// } +// jchar* utf16End = utf8_to_utf16(utf8String, u8len, utf16Buffer, utf16Length + 1); +// jstring ret; +// if (LIKELY(utf16End > utf16Buffer)) { +// ret = (*env)->NewString(env, utf16Buffer, utf16End - utf16Buffer); +// } else { +// ret = (*env)->NewString(env, utf16Buffer, 0); +// } +// if (UNLIKELY(needFree)) { +// free(utf16Buffer); +// } +// return ret; +// } +// +///* +// * The code below is copied from: +// * https://cs.android.com/android/platform/superproject/main/+/main:system/core/libutils/Unicode.cpp;l=1?q=Unicode.cpp&ss=android%2Fplatform%2Fsuperproject%2Fmain +// */ +// +//// is_any_surrogate() returns true if w is either a high or low surrogate +// static bool is_any_surrogate(jchar w) +//{ +// return (w & 0xf800) == 0xd800; +// } +// +//// is_surrogate_pair() returns true if w1 and w2 form a valid surrogate pair +// static bool is_surrogate_pair(jchar w1, jchar w2) +//{ +// return ((w1 & 0xfc00) == 0xd800) && ((w2 & 0xfc00) == 0xdc00); +// } +// +// static jsize utf16_to_utf8_length(const jchar* src, jsize src_len) +//{ +// if (src == NULL || src_len == 0) return 0; +// +// const jchar* const end = src + src_len; +// const jchar* in = src; +// jsize utf8_len = 0; +// +// while (in < end) { +// jchar w = *in++; +// if (LIKELY(w < 0x0080)) { +// utf8_len += 1; +// continue; +// } +// if (LIKELY(w < 0x0800)) { +// utf8_len += 2; +// continue; +// } +// if (LIKELY(!is_any_surrogate(w))) { +// utf8_len += 3; +// continue; +// } +// if (in < end && is_surrogate_pair(w, *in)) { +// utf8_len += 4; +// in++; +// continue; +// } +// /* skip if at the end of the string or invalid surrogate pair */ +// } +// return utf8_len; +// } +// +// static void utf16_to_utf8(const jchar* src, jsize src_len, char* dst, jsize dst_len) +//{ +// if (src == NULL || src_len == 0 || dst == NULL) { +// return; +// } +// +// const jchar* in = src; +// const jchar* const in_end = src + src_len; +// char* out = dst; +// const char* const out_end = dst + dst_len; +// jchar w2; +// +// while (in < in_end) { +// jchar w = *in++; +// if (LIKELY(w < 0x0080)) { +// if (out + 1 > out_end) abort(); +// *out++ = (char) (w & 0xff); +// continue; +// } +// if (LIKELY(w < 0x0800)) { +// if (out + 2 > out_end) abort(); +// *out++ = (char) (0xc0 | ((w >> 6) & 0x1f)); +// *out++ = (char) (0x80 | ((w >> 0) & 0x3f)); +// continue; +// } +// if (LIKELY(!is_any_surrogate(w))) { +// if (out + 3 > out_end) abort(); +// *out++ = (char) (0xe0 | ((w >> 12) & 0xf)); +// *out++ = (char) (0x80 | ((w >> 6) & 0x3f)); +// *out++ = (char) (0x80 | ((w >> 0) & 0x3f)); +// continue; +// } +// /* surrogate pair */ +// if (in < in_end && (w2 = *in, is_surrogate_pair(w, w2))) { +// if (out + 4 > out_end) abort(); +// jint dw = (jint) (0x10000 + ((w - 0xd800) << 10) + (w2 - 0xdc00)); +// *out++ = (char) (0xf0 | ((dw >> 18) & 0x07)); +// *out++ = (char) (0x80 | ((dw >> 12) & 0x3f)); +// *out++ = (char) (0x80 | ((dw >> 6) & 0x3f)); +// *out++ = (char) (0x80 | ((dw >> 0) & 0x3f)); +// in++; +// } +// /* We reach here in two cases: +// * 1) (in == in_end), which means end of the input string +// * 2) (w2 & 0xfc00) != 0xdc00, which means invalid surrogate pair +// * In either case, we intentionally do nothing and skip +// */ +// } +// *out = '\0'; +// } +// +// static uint32_t utf8_4b_to_utf32(uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4) +//{ +// return ((c1 & 0x07) << 18) | ((c2 & 0x3f) << 12) | ((c3 & 0x3f) << 6) | (c4 & 0x3f); +// } +// +//// TODO: current behavior of converting UTF8 to UTF-16 has a few issues below +//// +//// 1. invalid trailing bytes (i.e. not b'10xxxxxx) are treated as valid trailing +//// bytes and follows normal conversion rules +//// 2. invalid leading byte (b'10xxxxxx) is treated as a valid single UTF-8 byte +//// 3. invalid leading byte (b'11111xxx) is treated as a valid leading byte +//// (same as b'11110xxx) for a 4-byte UTF-8 sequence +//// 4. an invalid 4-byte UTF-8 sequence that translates to a codepoint < U+10000 +//// will be converted as a valid UTF-16 character +//// +//// We keep the current behavior as is but with warnings logged, so as not to +//// break compatibility. However, this needs to be addressed later. +// +// static jsize utf8_to_utf16_length(const char* u8str, jsize u8len) +//{ +// const char* const in_end = u8str + u8len; +// const char* in = u8str; +// jsize utf16_len = 0; +// +// while (in < in_end) { +// uint8_t c = *in; +// utf16_len++; +// if (LIKELY((c & 0x80) == 0)) { +// in++; +// continue; +// } +// if (UNLIKELY(c < 0xc0)) { +// in++; +// continue; +// } +// if (LIKELY(c < 0xe0)) { +// in += 2; +// continue; +// } +// if (LIKELY(c < 0xf0)) { +// in += 3; +// continue; +// } else { +// uint8_t c2, c3, c4; +// c2 = in[1]; +// c3 = in[2]; +// c4 = in[3]; +// if (utf8_4b_to_utf32(c, c2, c3, c4) >= 0x10000) { +// utf16_len++; +// } +// in += 4; +// continue; +// } +// } +// if (in == in_end) { +// return utf16_len; +// } +// return 0; +//} +// +// static jchar* +// utf8_to_utf16_no_null_terminator(const char* src, jsize srcLen, jchar* dst, jsize dstLen) +//{ +// if (src == NULL || srcLen == 0 || dstLen == 0) { +// return dst; +// } +// +// const char* const in_end = src + srcLen; +// const char* in = src; +// const jchar* const out_end = dst + dstLen; +// jchar* out = dst; +// uint8_t c, c2, c3, c4; +// uint32_t w; +// +// while (in < in_end && out < out_end) { +// c = *in++; +// if (LIKELY((c & 0x80) == 0)) { +// *out++ = (jchar) (c); +// continue; +// } +// if (UNLIKELY(c < 0xc0)) { +// ; +// *out++ = (jchar) (c); +// continue; +// } +// if (LIKELY(c < 0xe0)) { +// if (UNLIKELY(in + 1 > in_end)) { +// return out; +// } +// c2 = *in++; +// *out++ = (jchar) (((c & 0x1f) << 6) | (c2 & 0x3f)); +// continue; +// } +// if (LIKELY(c < 0xf0)) { +// if (UNLIKELY(in + 2 > in_end)) { +// return out; +// } +// c2 = *in++; +// c3 = *in++; +// *out++ = (jchar) (((c & 0x0f) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f)); +// continue; +// } else { +// if (UNLIKELY(in + 3 > in_end)) { +// return out; +// } +// if (UNLIKELY(c >= 0xf8)) { +// //error +// } +// // Multiple UTF16 characters with surrogates +// c2 = *in++; +// c3 = *in++; +// c4 = *in++; +// w = utf8_4b_to_utf32(c, c2, c3, c4); +// if (UNLIKELY(w < 0x10000)) { +// *out++ = (jchar) (w); +// } else { +// if (UNLIKELY(out + 2 > out_end)) { +// // Ooops.... not enough room for this surrogate pair. +// return out; +// } +// *out++ = (jchar) (((w - 0x10000) >> 10) + 0xd800); +// *out++ = (jchar) (((w - 0x10000) & 0x3ff) + 0xdc00); +// } +// continue; +// } +// } +// return out; +//} +// +// static jchar* utf8_to_utf16(const char* u8str, jsize u8len, jchar* u16str, jsize u16len) +//{ +// jchar* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str, u16len - 1); +// *end = 0; +// return end; +//} \ No newline at end of file diff --git a/src/rust/cpp/base/WCDBRust.h b/src/rust/cpp/base/WCDBRust.h new file mode 100644 index 000000000..29ec50b69 --- /dev/null +++ b/src/rust/cpp/base/WCDBRust.h @@ -0,0 +1,337 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "Macro.h" +#include "ObjectBridge.h" + +#include + +#define WCDBRust(className, funcName) WCDBRust##className##_##funcName + +#define WCDBRustObjectMethodWithNoArg(className, funcName) WCDBRust(className, funcName)() + +#define WCDBRustObjectMethod(className, funcName, ...) WCDBRust(className, funcName)(__VA_ARGS__) + +#define WCDBRustClassMethodWithNoArg(className, funcName) WCDBRust(className, funcName)() + +#define WCDBRustClassMethod(className, funcName, ...) WCDBRust(className, funcName)(__VA_ARGS__) + +#define WCDBRustBridgeStruct(type, value) type value##Struct = {(CPPObject*)value} + +#define WCDBRustGetString(value) \ + char* value##String = NULL; \ + const jchar* value##_utf16String = NULL; \ + WCDBRustGetUTF8String(env, value, &value##String, &value##_utf16String, false); + +#define WCDBRustReleaseString(value) \ + if (value##_utf16String != NULL) { \ + (*env)->ReleaseStringChars(env, value, value##_utf16String); \ + } \ + WCDBClearAllPreAllocatedMemory(); + +#define WCDBRustGetStringCritical(value) \ + char* value##String = NULL; \ + const char* value##_utf16String = NULL; + +#define WCDBRustReleaseStringCritical(value) WCDBClearAllPreAllocatedMemory(); + +#define WCDBRustGetByteArray(value) \ + const unsigned char* value##Array = NULL; \ + int value##Length = 0; \ + if (value != NULL) { \ + value##Length = (*env)->GetArrayLength(env, value); \ + value##Array = (const unsigned char*)(*env)->GetByteArrayElements(env, value, NULL); \ + } + +#define WCDBRustReleaseByteArray(value) \ + if (value##Array != NULL) { \ + (*env)->ReleaseByteArrayElements(env, value, (jbyte*)value##Array, 0); \ + } + +#define WCDBRustGetByteArrayCritical(value) \ + const unsigned char* value##Array = NULL; \ + int value##Length = 0; \ + if (value != NULL) { \ + value##Length = (*env)->GetArrayLength(env, value); \ + value##Array = (const unsigned char*)(*env)->GetPrimitiveArrayCritical(env, value, NULL); \ + } + +#define WCDBRustReleaseByteArrayCritical(value) \ + if (value##Array != NULL) { \ + (*env)->ReleasePrimitiveArrayCritical(env, value, (jbyte*)value##Array, 0); \ + } + +#define WCDBRustGetLongArray(value) \ + const jlong* value##Array = NULL; \ + int value##Length = 0; \ + if (value != NULL) { \ + value##Array = (*env)->GetLongArrayElements(env, value, NULL); \ + value##Length = (*env)->GetArrayLength(env, value); \ + } + +#define WCDBRustReleaseLongArray(value) \ + if (value##Array != NULL) { \ + (*env)->ReleaseLongArrayElements(env, value, (jlong*)value##Array, Rust_ABORT); \ + } + +#define WCDBRustGetCppPointerArrayCritical(value) \ + const void** value##Array = NULL; \ + const jlong* value##LongArray = NULL; \ + int value##Length = 0; \ + if (value != NULL) { \ + value##Length = (*env)->GetArrayLength(env, value); \ + value##LongArray = (const jlong*)(*env)->GetPrimitiveArrayCritical(env, value, NULL); \ + if (sizeof(void*) == sizeof(jlong) || value##Length == 0) { \ + value##Array = (const void**)value##LongArray; \ + } else { \ + value##Array = alloca(sizeof(void*) * value##Length); \ + for (int i = 0; i < value##Length; i++) { \ + value##Array[i] = (void*)value##LongArray[i]; \ + } \ + } \ + } + +#define WCDBRustReleaseCppPointerArrayCritical(value) \ + if (value##LongArray != NULL) { \ + (*env)->ReleasePrimitiveArrayCritical(env, value, (void*)value##LongArray, 0); \ + } + +#define WCDBRustGetIntArray(value) \ + const int* value##Array = NULL; \ + int value##Length = 0; \ + if (value != NULL) { \ + value##Array = (*env)->GetIntArrayElements(env, value, NULL); \ + value##Length = (*env)->GetArrayLength(env, value); \ + } + +#define WCDBRustReleaseIntArray(value) \ + if (value##Array != NULL) { \ + (*env)->ReleaseIntArrayElements(env, value, (jint*)value##Array, Rust_ABORT); \ + } + +#define WCDBRustGetDoubleArray(value) \ + const jdouble* value##Array = NULL; \ + int value##Length = 0; \ + if (value != NULL) { \ + value##Array = (*env)->GetDoubleArrayElements(env, value, NULL); \ + value##Length = (*env)->GetArrayLength(env, value); \ + } + +#define WCDBRustReleaseDoubleArray(value) \ + if (value##Array != NULL) { \ + (*env)->ReleaseDoubleArrayElements(env, value, (jdouble*)value##Array, Rust_ABORT); \ + } + +#define WCDBRustGetStringArray(value) \ + int value##Length = 0; \ + char** value##CharArray = NULL; \ + WCDBRustGetUTF8StringArray(env, value, &value##CharArray, &value##Length); + +#define WCDBRustReleaseStringArray(value) WCDBClearAllPreAllocatedMemory(); + +#define WCDBRustCommonValueParameter(parameter) \ + int parameter##_type, long long parameter##_long, double parameter##_double, \ + const char *parameter##_string + +#define WCDBRustCreateCommonValueWithIsCritical(parameter, isCritical) \ + CPPCommonValue parameter##_common; \ + parameter##_common.type = parameter##_type; \ + const bool parameter##_isCritical = isCritical; \ + switch (parameter##_type) { \ + case WCDBBridgedType_Bool: \ + case WCDBBridgedType_UInt: \ + case WCDBBridgedType_Int: \ + parameter##_common.intValue = parameter##_long; \ + break; \ + case WCDBBridgedType_Double: \ + parameter##_common.doubleValue = parameter##_double; \ + break; \ + case WCDBBridgedType_String: \ + parameter##_common.intValue = (long long)parameter##_string; \ + break; \ + default: \ + parameter##_common.intValue = parameter##_long; \ + break; \ + } + +#define WCDBRustCreateCommonValue(parameter) \ + CPPCommonValue parameter##_common; \ + parameter##_common.type = parameter##_type; \ + switch (parameter##_type) { \ + case WCDBBridgedType_Bool: \ + case WCDBBridgedType_UInt: \ + case WCDBBridgedType_Int: \ + parameter##_common.intValue = parameter##_long; \ + break; \ + case WCDBBridgedType_Double: \ + parameter##_common.doubleValue = parameter##_double; \ + break; \ + case WCDBBridgedType_String: \ + parameter##_common.intValue = (long long)parameter##_string; \ + break; \ + default: \ + parameter##_common.intValue = parameter##_long; \ + break; \ + } + +#define WCDBRustObjectOrStringParameter(parameter) \ + int parameter##_type, void *parameter##_object, const char *parameter##_string + +#define WCDBRustCreateObjectOrStringCommonValue(parameter, isCritical) \ + CPPCommonValue parameter##_common; \ + parameter##_common.type = parameter##_type; \ + if (parameter##_type == WCDBBridgedType_String) { \ + parameter##_common.intValue = (long long)parameter##_string; \ + } else { \ + parameter##_common.intValue = (long long)parameter##_object; \ + } + +#define WCDBRustObjectOrIntegerParameter(parameter) int parameter##_type, long long parameter##_long + +#define WCDBRustCreateObjectOrIntegerCommonValue(parameter) \ + CPPCommonValue parameter##_common; \ + parameter##_common.type = parameter##_type; \ + parameter##_common.intValue = parameter##_long; + +#define WCDBRustCommonArrayParameter(parameter) \ + int parameter##_type, void **parameter##_longArray, void **parameter##_doubleArray, \ + const char **parameter##_stringArray, int parameter##_arrayLen + +#define WCDBRustCreateCommonArrayWithAction(parameter, action) \ + CPPCommonArray parameter##_commonArray; \ + parameter##_commonArray.type = parameter##_type; \ + if (parameter##_type < WCDBBridgedType_Double || parameter##_type > WCDBBridgedType_String) { \ + parameter##_commonArray.length = parameter##_arrayLen; \ + parameter##_commonArray.buffer = (const void**)parameter##_longArray; \ + action; \ + } else if (parameter##_type == WCDBBridgedType_String) { \ + parameter##_commonArray.length = parameter##_arrayLen; \ + parameter##_commonArray.buffer = (const void**)parameter##_stringArray; \ + action; \ + } else { \ + parameter##_commonArray.length = parameter##_arrayLen; \ + parameter##_commonArray.buffer = (const void**)parameter##_doubleArray; \ + action; \ + } + +#define WCDBRustObjectOrStringArrayParameter(parameter) \ + int parameter##_type, void **parameter##_voidArray, const char **parameter##_stringArray, \ + int parameter##_arrayLen + +#define WCDBRustCreateObjectOrStringArrayCriticalWithAction(parameter, action) \ + CPPCommonArray parameter##_commonArray; \ + parameter##_commonArray.type = parameter##_type; \ + if (parameter##_type < WCDBBridgedType_Double || parameter##_type > WCDBBridgedType_String) { \ + parameter##_commonArray.length = parameter##_arrayLen; \ + parameter##_commonArray.buffer = (const void**)parameter##_voidArray; \ + action; \ + } else if (parameter##_type == WCDBBridgedType_String) { \ + parameter##_commonArray.length = parameter##_arrayLen; \ + parameter##_commonArray.buffer = (const void**)parameter##_stringArray; \ + action; \ + } + +#define WCDBRustMultiTypeArrayParameter(parameter) \ + int *parameter##_types, long *parameter##_longValues, double *parameter##_doubleValues, \ + void **parameter##_stringValues, int parameter##_arrayLen + +#define WCDBRustCreateMultiTypeArray(parameter) \ + CPPMultiTypeArray parameter##Array; \ + parameter##Array.totalLength = parameter##_arrayLen; \ + parameter##Array.types = (const enum WCDBBridgedType*)parameter##_types; \ + parameter##Array.intValues = (const long long*)parameter##_longValues; \ + parameter##Array.doubleValues = (const double*)parameter##_doubleValues; \ + parameter##Array.stringValues = (const char**)parameter##_stringValues; + +#define WCDBRustReleaseMultiTypeArray(parameter) \ + WCDBRustReleaseIntArray(parameter##_types); \ + WCDBRustReleaseLongArray(parameter##_longValues); \ + WCDBRustReleaseDoubleArray(parameter##_doubleValues); \ + WCDBRustReleaseStringArray(parameter##_stringValues); + +#define WCDBRustCreateJStringAndReturn(action) return WCDBRustCreateJString(env, action) + +#define WCDBRustCreateJavaString(value) jstring j##value = WCDBRustCreateJString(env, value) + +#define WCDBRustFindClass(valueName, signature, action) \ + static jclass valueName = NULL; \ + if (valueName == NULL) { \ + valueName = (*env)->FindClass(env, signature); \ + WCDBRustCreateGlobalRef(valueName); \ + } \ + assert(valueName != NULL); \ + if (valueName == NULL) { \ + action; \ + } + +#define WCDBRustGetObjectMethodId(valueName, class, methodName, signature) \ + static jmethodID valueName = NULL; \ + if (valueName == NULL) { \ + valueName = (*env)->GetMethodID(env, class, methodName, signature); \ + } \ + assert(valueName != NULL); + +void* WCDBRustCreateGlobalRef(size_t size); + +// extern JavaVM *g_vm; +// +// #def ine WCDBRustTryGetVM \ +// if (g_vm == NULL) { \ +// (*env)->GetJavaVM(env, &g_vm); \ +// assert(g_vm != NULL); \ +// } +// +// #def ine WCDB RustTryGetEnvOr(action) \ +// RustEnv *env; \ +// int getEnvStat = (*g_vm)->GetEnv(g_vm, (void **) &env, Rust_VERSION_1_6); \ +// bool needDetach = false; \ +// if (getEnvStat == Rust_EDETACHED) { \ +// if ((*g_vm)->AttachCurrentThread(g_vm, &env, NULL) != 0) { \ +// assert(0); \ +// action; \ +// } \ +// needDetach = Rust_TRUE; \ +// } +// +// #def ine WCDBRustTryDetach \ +// if (needDetach) { \ +// (*g_vm)->DetachCurrentThread(g_vm); \ +// } +// +WCDB_EXTERN_C_BEGIN +// +// void WCDBRustDestructContext(jobject config); + +void WCDBRustClassMethod(Base, releaseObject, void* cppObject); + +// void WCDBRustInitJClasses(RustEnv *env); +// +// jclass WCDBRustGetDatabaseClass(); +// jclass WCDBRustGetHandleClass(); +// jclass WCDBRustGetExceptionClass(); +// +// void WCDBRustGetUTF8String( +// RustEnv *env, jstring value, char **utf8String, const jchar **utf16String, bool critical); +// void WCDBRustGetUTF8StringArray(RustEnv *env, jobjectArray value, char ***stringArray, int +// *length); jstring WCDBRustCreateJString(RustEnv *env, const char *utf8String); + +WCDB_EXTERN_C_END diff --git a/src/rust/cpp/clang-format.sh b/src/rust/cpp/clang-format.sh new file mode 100755 index 000000000..fb2910dc3 --- /dev/null +++ b/src/rust/cpp/clang-format.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +FILES=$(git ls-files '*.c' '*.cpp' '*.h') +for file in $FILES; do + clang-format -i "$file" +done \ No newline at end of file diff --git a/src/rust/cpp/core/BindingRust.c b/src/rust/cpp/core/BindingRust.c new file mode 100644 index 000000000..29cb32126 --- /dev/null +++ b/src/rust/cpp/core/BindingRust.c @@ -0,0 +1,91 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BindingRust.h" + +#include "BindingBridge.h" + +void* WCDBRustBindingClassMethodWithNoArg(create) { + return (void*)WCDBBindingCreate().innerValue; +} + +void WCDBRustBindingClassMethod(addColumnDef, void* self, void* columnDef) { + WCDBRustBridgeStruct(CPPBinding, self); + WCDBRustBridgeStruct(CPPColumnDef, columnDef); + WCDBBindingAddColumnDef(selfStruct, columnDefStruct); +} + +void WCDBRustBindingClassMethod(enableAutoIncrementForExistingTable, void* self) { + WCDBRustBridgeStruct(CPPBinding, self); + WCDBBindingEnableAutoIncrementForExistingTable(selfStruct); +} + +void WCDBRustBindingClassMethod(addIndex, + void* self, + const char* indexNameOrSuffix, + bool isFullName, + void* createIndex) { + WCDBRustBridgeStruct(CPPBinding, self); + WCDBRustBridgeStruct(CPPStatementCreateIndex, createIndex); + WCDBBindingAddIndex(selfStruct, indexNameOrSuffix, isFullName, createIndexStruct); +} + +void WCDBRustBindingClassMethod(addTableConstraint, void* self, void* constraint) { + WCDBRustBridgeStruct(CPPBinding, self); + WCDBRustBridgeStruct(CPPTableConstraint, constraint); + WCDBBindingAddTableConstraint(selfStruct, constraintStruct); +} + +void WCDBRustBindingClassMethod(configVirtualModule, void* self, const char* moduleName) { + WCDBRustBridgeStruct(CPPBinding, self); + WCDBBindingConfigVirtualModule(selfStruct, moduleName); +} + +void WCDBRustBindingClassMethod(configVirtualModuleArgument, void* self, const char* argument) { + WCDBRustBridgeStruct(CPPBinding, self); + WCDBBindingConfigVirtualModuleArgument(selfStruct, argument); +} + +void WCDBRustBindingClassMethod(configWithoutRowId, void* self) { + WCDBRustBridgeStruct(CPPBinding, self); + WCDBBindingConfigWithoutRowId(selfStruct); +} + +bool WCDBRustBinding_createTable(void* self, const char* tableName, void* handle) { + WCDBRustBridgeStruct(CPPBinding, self); + WCDBRustBridgeStruct(CPPHandle, handle); + bool ret = WCDBBindingCreateTable(selfStruct, tableName, handleStruct); + return ret; +} + +bool WCDBRustBindingClassMethod(createVirtualTable, + void* self, + const char* tableName, + void* handle) { + WCDBRustBridgeStruct(CPPBinding, self); + WCDBRustBridgeStruct(CPPHandle, handle); + bool ret = WCDBBindingCreateVirtualTable(selfStruct, tableName, handleStruct); + return ret; +} + +void* WCDBRustBindingClassMethod(getBaseBinding, void* self) { + WCDBRustBridgeStruct(CPPBinding, self); + return (void*)WCDBBindingGetBaseBinding(selfStruct); +} diff --git a/src/rust/cpp/core/BindingRust.h b/src/rust/cpp/core/BindingRust.h new file mode 100644 index 000000000..abb8906ba --- /dev/null +++ b/src/rust/cpp/core/BindingRust.h @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustBindingFuncName(funcName) WCDBRust(Binding, funcName) +#define WCDBRustBindingObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(Binding, funcName, __VA_ARGS__) +#define WCDBRustBindingObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(Binding, funcName) +#define WCDBRustBindingClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(Binding, funcName) +#define WCDBRustBindingClassMethod(funcName, ...) \ + WCDBRustClassMethod(Binding, funcName, __VA_ARGS__) + +void* WCDBRustBindingClassMethodWithNoArg(create); + +void WCDBRustBindingClassMethod(addColumnDef, void* self, void* columnDef); +void WCDBRustBindingClassMethod(enableAutoIncrementForExistingTable, void* self); +void WCDBRustBindingClassMethod(addIndex, + void* self, + const char* indexNameOrSuffix, + bool isFullName, + void* createIndex); +void WCDBRustBindingClassMethod(addTableConstraint, void* self, void* constraint); +void WCDBRustBindingClassMethod(configVirtualModule, void* self, const char* moduleName); +void WCDBRustBindingClassMethod(configVirtualModuleArgument, void* self, const char* argument); +void WCDBRustBindingClassMethod(configWithoutRowId, void* self); +bool WCDBRustBindingClassMethod(createTable, void* self, const char* tableName, void* handle); +bool WCDBRustBindingClassMethod(createVirtualTable, + void* self, + const char* tableName, + void* handle); +void* WCDBRustBindingClassMethod(getBaseBinding, void* self); diff --git a/src/rust/cpp/core/CoreRust.c b/src/rust/cpp/core/CoreRust.c new file mode 100644 index 000000000..f2b9d7195 --- /dev/null +++ b/src/rust/cpp/core/CoreRust.c @@ -0,0 +1,30 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CoreRust.h" + +#include "CoreBridge.h" + +void* WCDBRustCoreClassMethod(createDatabase, const char* path, bool readonly, bool inMemory) { + return (void*)WCDBCoreCreateDatabase(path, readonly, inMemory).innerValue; +} +void WCDBRustCoreClassMethod(setDefaultCipherConfig, int version) { + WCDBCoreSetDefaultCipherConfig(version); +} \ No newline at end of file diff --git a/src/rust/cpp/core/CoreRust.h b/src/rust/cpp/core/CoreRust.h new file mode 100644 index 000000000..7a3d5733c --- /dev/null +++ b/src/rust/cpp/core/CoreRust.h @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +// #define WCDBRustCoreFuncName(funcName) WCDBRust(Core, funcName) +// #def ine WCDBRustCoreObjectMethod(funcName, ...) \ +// WCDBRustObjectMethod(Core, funcName, __VA_ARGS__) +// #def ine WCDBRustCoreObjectMethodWithNoArg(funcName) \ +// WCDBRustObjectMethodWithNoArg(Core, funcName) +// #def ine WCDBRustCoreClassMethodWithNoArg(funcName) \ +// WCDBRustClassMethodWithNoArg(Core, funcName) +#define WCDBRustCoreClassMethod(funcName, ...) WCDBRustClassMethod(Core, funcName, __VA_ARGS__) + +void* WCDBRustCoreClassMethod(createDatabase, const char* path, bool readonly, bool inMemory); +void WCDBRustCoreClassMethod(setDefaultCipherConfig, int version); +// void WCDBRustCoreClassMethodWithNoArg(purgeAllDatabase); +// void WCDBRustCoreClassMethod(releaseSQLiteMemory, jint bytes); +// void WCDBRustCoreClassMethod(setSoftHeapLimit, jlong limit); +// void WCDBRustCoreClassMethod(setAutoCheckpointMinFrames, jint frames); +// jlong WCDBRustCoreClassMethodWithNoArg(getThreadedError); \ No newline at end of file diff --git a/src/rust/cpp/core/DatabaseExtendRust.cpp b/src/rust/cpp/core/DatabaseExtendRust.cpp new file mode 100644 index 000000000..9e9222cb6 --- /dev/null +++ b/src/rust/cpp/core/DatabaseExtendRust.cpp @@ -0,0 +1,80 @@ +// Created by qiuwenchen on 2024/2/21. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DatabaseExtendRust.h" + +#include "BaseTokenizerUtil.hpp" + +void WCDBRustDatabaseClassMethod(configPinyinDict, + const char** keys, + const char*** values, + const size_t* values_len, + size_t keys_len) { + auto* cppPinyinDict = new WCDB::StringViewMap>(); + for (int i = 0; i < keys_len; i++) { + const char* key = keys[i]; + WCDB::StringView cppKey(key); + if (cppKey.empty()) { + continue; + } + + std::vector cppValues; + size_t row_len = values_len[i]; + const char** row_values = values[i]; + for (int j = 0; j < row_len; j++) { + const char* row = row_values[j]; + WCDB::StringView cppValue(row); + if (cppValue.empty()) { + continue; + } + cppValues.push_back(cppValue); + } + if (cppValues.empty()) { + continue; + } + cppPinyinDict->insert_or_assign(cppKey, cppValues); + } + WCDB::BaseTokenizerUtil::configPinyinDict(cppPinyinDict); +} + +void WCDBRustDatabaseClassMethod(configTraditionalChineseDict, + const char** keys, + const char** values, + size_t len) { + auto* cppTraditionalChineseDict = new WCDB::StringViewMap(); + for (int i = 0; i < len; i++) { + const char* key = keys[i]; + WCDB::StringView cppKey(key); + if (cppKey.empty()) { + continue; + } + + const char* value = values[i]; + WCDB::StringView cppValue(value); + if (cppValue.empty()) { + continue; + } + cppTraditionalChineseDict->insert_or_assign(cppKey, cppValue); + } + WCDB::BaseTokenizerUtil::configTraditionalChineseDict(cppTraditionalChineseDict); +} \ No newline at end of file diff --git a/src/rust/cpp/core/DatabaseExtendRust.h b/src/rust/cpp/core/DatabaseExtendRust.h new file mode 100644 index 000000000..133b560d6 --- /dev/null +++ b/src/rust/cpp/core/DatabaseExtendRust.h @@ -0,0 +1,42 @@ +// Created by qiuwenchen on 2024/2/21. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "DatabaseRust.h" + +#include + +WCDB_EXTERN_C_BEGIN + +void WCDBRustDatabaseClassMethod(configPinyinDict, + const char** keys, + const char*** values, + const size_t* values_len, + size_t keys_len); +void WCDBRustDatabaseClassMethod(configTraditionalChineseDict, + const char** keys, + const char** values, + size_t len); + +WCDB_EXTERN_C_END \ No newline at end of file diff --git a/src/rust/cpp/core/DatabaseRust.c b/src/rust/cpp/core/DatabaseRust.c new file mode 100644 index 000000000..a22994c6b --- /dev/null +++ b/src/rust/cpp/core/DatabaseRust.c @@ -0,0 +1,1069 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DatabaseRust.h" + +#include "CoreBridge.h" +#include "FTSBridge.h" + +#include +#include + +//#define WCDBRustTryGetDatabaseMethodId(name, signature, action) \ +// static jmethodID g_methodId = NULL; \ +// if (g_methodId == NULL) { \ +// g_methodId \ +// = (*env)->GetStaticMethodID(env, WCDBRustGetDatabaseClass(), name, signature); \ +// if (g_methodId == NULL) { \ +// assert(0); \ +// action; \ +// } \ +// } + +void* WCDBRustDatabaseClassMethod(getError, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return (void*)WCDBDatabaseGetError(selfStruct).innerValue; +} + +void* WCDBRustDatabaseClassMethod(getTag, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return (void*)WCDBDatabaseGetTag(selfStruct); +} + +void WCDBRustDatabaseClassMethod(setTag, void* self, long tag) { + WCDBRustBridgeStruct(CPPDatabase, self); + WCDBDatabaseSetTag(selfStruct, tag); +} + +const char* WCDBRustDatabaseClassMethod(getPath, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseGetPath(selfStruct); +} + +// typedef struct StringEnumeratorContext { +// JNIEnv* env; +// jobject array; +// jclass arrayClass; +// } StringEnumeratorContext; +// +// void WCDBRustStringEnumerator(StringEnumeratorContext* context, const char* string) +//{ +// JNIEnv* env = context->env; +// WCDBRustGetObjectMethodId(g_addMethod, context->arrayClass, "add", "(Ljava/lang/Object;)Z"); +// if (g_addMethod == NULL) { +// return; +// } +// WCDBRustCreateJavaString(string); +// (*env)->CallBooleanMethod(env, context->array, g_addMethod, jstring); +// } +// +// jobject WCDBRustDatabaseClassMethod(getPaths, jlong self) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBRustFindClass(g_arrayClass, "java/util/ArrayList", return NULL); +// WCDBRustGetObjectMethodId(g_arrayInit, g_arrayClass, "", "()V"); +// jobject arrayList = (*env)->NewObject(env, g_arrayClass, g_arrayInit); +// StringEnumeratorContext context; +// context.env = env; +// context.array = arrayList; +// context.arrayClass = g_arrayClass; +// WCDBDatabaseGetPaths(selfStruct, &context, (WCDBStringEnumerater) WCDBRustStringEnumerator); +// return arrayList; +// } + +void* WCDBRustDatabaseClassMethod(getHandle, void* self, bool writeHint) { + WCDBRustBridgeStruct(CPPDatabase, self); + return (void*)WCDBDatabaseGetHandle(selfStruct, writeHint).innerValue; +} + +bool WCDBRustDatabaseClassMethod(canOpen, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseCanOpen(selfStruct); +} + +bool WCDBRustDatabaseClassMethod(isOpened, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseIsOpened(selfStruct); +} + +bool WCDBRustDatabaseClassMethod(isBlockaded, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseIsBlockaded(selfStruct); +} + +// +// typedef void (*DatabaseCloseCallback)(); +// +// typedef struct CloseDatabaseContext { +// JNIEnv* env; +// jobject callback; +//} CloseDatabaseContext; +// +// void WCDBRustDatabaseCloseCallback(CloseDatabaseContext* context) +//{ +// JNIEnv* env = context->env; +// WCDBRustTryGetDatabaseMethodId( +// "onClose", "(" WCDBRustDatabaseSignature "$CloseCallBack;)V", return ); +// (*env)->CallStaticVoidMethod( +// env, WCDBRustGetDatabaseClass(), g_methodId, context->callback); +//} + +void WCDBRustDatabase_close(void* self, void* context, WCDBDatabaseCloseCallback callback) { + WCDBRustBridgeStruct(CPPDatabase, self); + WCDBDatabaseClose(selfStruct, context, callback); +} + +void WCDBRustDatabaseClassMethod(blockade, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + WCDBDatabaseBlockade(selfStruct); +} + +void WCDBRustDatabaseClassMethod(unblockade, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + WCDBDatabaseUnblockade(selfStruct); +} + +// +// void WCDBRustDatabaseClassMethod(purge, jlong self) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBDatabasePurge(selfStruct); +// } +// +void WCDBRustDatabaseClassMethod(configCipher, + void* self, + uint8_t* cipherKey, + size_t len, + int pageSize, + int cipherVersion) { + WCDBRustBridgeStruct(CPPDatabase, self); + WCDBDatabaseConfigCipher(selfStruct, cipherKey, len, pageSize, cipherVersion); +} + +typedef struct WCDBRustGlobalSetConfigContext { + RustSetConfigCallback rust_callback; +} WCDBRustGlobalSetConfigContext; + +bool WCDBRustDatabaseConfigInvocationCallback(WCDBRustGlobalSetConfigContext* invocation, + CPPHandle handle) { + bool ret = false; + if (invocation == NULL || invocation->rust_callback == NULL) { + return ret; + } + RustSetConfigCallback callback = (RustSetConfigCallback)invocation->rust_callback; + ret = callback((void*)handle.innerValue); + return ret; +} + +bool WCDBRustDatabaseConfigUnInvocationCallback(WCDBRustGlobalSetConfigContext* unInvocation, + CPPHandle handle) { + bool ret = false; + if (unInvocation == NULL || unInvocation->rust_callback == NULL) { + return ret; + } + RustSetConfigCallback callback = (RustSetConfigCallback)unInvocation->rust_callback; + if (callback == NULL) { + return ret; + } + ret = callback((void*)handle.innerValue); + return ret; +} + +void WCDBRustSetConfigDestructContext(void* context) { + if (context != NULL) { + free(context); + context = NULL; + } +} + +void WCDBRustDatabaseClassMethod(config, + void* self, + const char* name, + RustSetConfigCallback* invocation, + RustSetConfigCallback* unInvocation, + int priority) { + WCDBRustBridgeStruct(CPPDatabase, self); + + size_t size = sizeof(RustSetConfigCallback); + WCDBRustGlobalSetConfigContext* invocationContext = + (WCDBRustGlobalSetConfigContext*)WCDBRustCreateGlobalRef(size); + invocationContext->rust_callback = invocation; + + WCDBRustGlobalSetConfigContext* unInvocationContext = + (WCDBRustGlobalSetConfigContext*)WCDBRustCreateGlobalRef(size); + unInvocationContext->rust_callback = unInvocation; + + WCDBDatabaseConfig( + selfStruct, name, + (WCDBConfigCallback)(invocation != NULL ? WCDBRustDatabaseConfigInvocationCallback : NULL), + invocationContext, + (WCDBConfigCallback)(unInvocation != NULL ? WCDBRustDatabaseConfigUnInvocationCallback + : NULL), + unInvocationContext, priority, (WCDBContextDestructor)WCDBRustSetConfigDestructContext); +} + +// +// void WCDBRustDatabaseClassMethod(enableLiteMode, jlong self, jboolean enable) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBDatabaseEnableLiteMode(selfStruct, enable); +// } +// +// void WCDBRustDatabasePerformanceTrace(jobject tracer, +// long tag, +// const char* path, +// unsigned long long handleId, +// const char* sql, +// const CPPPerformanceInfo* info) +//{ +// WCDBRustTryGetEnvOr(return ); +// WCDBRustTryGetDatabaseMethodId("onTracePerformance", +// "(" WCDBRustDatabaseSignature "$PerformanceTracer;J" +// WCDBRustStringSignature "J" WCDBRustStringSignature "J[I)V", +// return ); +// WCDBRustCreateJavaString(path); +// WCDBRustCreateJavaString(sql); +// jint size = sizeof(CPPPerformanceInfo) / sizeof(int) - 2; +// jintArray infoValues = (*env)->NewIntArray(env, size); +// if (infoValues != NULL) { +// (*env)->SetIntArrayRegion(env, infoValues, 0, size, (jint*) info); +// } +// (*env)->CallStaticVoidMethod(env, +// WCDBRustGetDatabaseClass(), +// g_methodId, +// tracer, +// (jlong) tag, +// jpath, +// (jlong) handleId, +// jsql, +// (jlong) info->costInNanoseconds, +// infoValues); +// WCDBRustTryDetach; +// } +// +typedef struct WCDBRustGlobalTracePerformanceContext { + RustGlobalTracePerformanceCallback rust_callback; +} WCDBRustGlobalTracePerformanceContext; + +void WCDBRustGlobalDatabasePerformanceTrace(WCDBRustGlobalTracePerformanceContext* context, + long tag, + const char* path, + unsigned long long handleId, + const char* sql, + const CPPPerformanceInfo* info) { + if (context == NULL || context->rust_callback == NULL) { + return; + } + context->rust_callback(tag, path, handleId, sql, info); +} + +void WCDBRustDestructContext(void* context) { + if (context != NULL) { + free(context); + context = NULL; + } +} + +void WCDBRustDatabaseClassMethod(globalTracePerformance, + RustGlobalTracePerformanceCallback rust_callback) { + size_t size = sizeof(WCDBRustGlobalTracePerformanceContext); + WCDBRustGlobalTracePerformanceContext* context = + (WCDBRustGlobalTracePerformanceContext*)WCDBRustCreateGlobalRef(size); + context->rust_callback = rust_callback; + WCDBDatabaseGlobalTracePerformance( + (WCDBPerformanceTracer)WCDBRustGlobalDatabasePerformanceTrace, context, + (WCDBContextDestructor)WCDBRustDestructContext); +} + +typedef struct WCDBRustTracePerformanceContext { + RustTracePerformanceCallback rust_callback; + void* cb_ptr; +} WCDBRustTracePerformanceContext; + +void WCDBRustDatabasePerformanceTrace(WCDBRustTracePerformanceContext* context, + long tag, + const char* path, + unsigned long long handleId, + const char* sql, + const CPPPerformanceInfo* info) { + if (context == NULL || context->rust_callback == NULL) { + return; + } + context->rust_callback(context->cb_ptr, tag, path, handleId, sql, info); +} + +void WCDBRustDatabaseClassMethod(tracePerformance, + void* self, + RustTracePerformanceCallback rust_callback, + void* cb_ptr) { + size_t size = sizeof(WCDBRustTracePerformanceContext); + WCDBRustBridgeStruct(CPPDatabase, self); + WCDBRustTracePerformanceContext* context = + (WCDBRustTracePerformanceContext*)WCDBRustCreateGlobalRef(size); + context->rust_callback = rust_callback; + context->cb_ptr = cb_ptr; + WCDBDatabaseTracePerformance(selfStruct, + (WCDBPerformanceTracer)WCDBRustDatabasePerformanceTrace, context, + WCDBRustDestructContext); +} +// +// void WCDBRustDatabaseSQLTrace(jobject tracer, +// long tag, +// const char* path, +// unsigned long long handleId, +// const char* sql, +// const char* info) +//{ +// WCDBRustTryGetEnvOr(return ); +// WCDBRustTryGetDatabaseMethodId("onTraceSQL", +// "(" WCDBRustDatabaseSignature "$SQLTracer;J" +// WCDBRustStringSignature "J" WCDBRustStringSignature +// WCDBRustStringSignature ")V", return ); +// WCDBRustCreateJavaString(path); +// WCDBRustCreateJavaString(sql); +// WCDBRustCreateJavaString(info); +// (*env)->CallStaticVoidMethod( +// env, WCDBRustGetDatabaseClass(), g_methodId, tracer, (jlong) tag, jpath, (jlong) handleId, +// jsql, jinfo); WCDBRustTryDetach; +//} +// + +typedef struct WCDBRustGlobalTraceSQLContext { + RustGlobalTraceSQLCallback rust_callback; +} WCDBRustGlobalTraceSQLContext; + +void WCDBRustDatabaseGlobalSQLTrace(WCDBRustGlobalTraceSQLContext* context, + long tag, + const char* path, + unsigned long long handleId, + const char* sql, + const char* info) { + if (context == NULL || context->rust_callback == NULL) { + return; + } + context->rust_callback(tag, path, handleId, sql, info); +} + +void WCDBRustDatabaseClassMethod(globalTraceSQL, RustGlobalTraceSQLCallback rust_callback) { + size_t size = sizeof(WCDBRustGlobalTraceSQLContext); + WCDBRustGlobalTraceSQLContext* context = + (WCDBRustGlobalTraceSQLContext*)WCDBRustCreateGlobalRef(size); + context->rust_callback = rust_callback; + WCDBDatabaseGlobalTraceSQL((WCDBSQLTracer)WCDBRustDatabaseGlobalSQLTrace, context, + WCDBRustDestructContext); +} + +typedef struct WCDBRustTraceSQLContext { + RustTraceSQLCallback rust_callback; + void* cb_ptr; +} WCDBRustTraceSQLContext; + +void WCDBRustDatabaseSQLTrace(WCDBRustTraceSQLContext* context, + long tag, + const char* path, + unsigned long long handleId, + const char* sql, + const char* info) { + if (context == NULL || context->rust_callback == NULL) { + return; + } + context->rust_callback(context->cb_ptr, tag, path, handleId, sql, info); +} + +void WCDBRustDatabaseClassMethod(traceSQL, + void* self, + RustTraceSQLCallback rust_callback, + void* cb_ptr) { + WCDBRustBridgeStruct(CPPDatabase, self); + size_t size = sizeof(WCDBRustTraceSQLContext); + WCDBRustTraceSQLContext* context = (WCDBRustTraceSQLContext*)WCDBRustCreateGlobalRef(size); + context->rust_callback = rust_callback; + context->cb_ptr = cb_ptr; + WCDBDatabaseTraceSQL(selfStruct, (WCDBSQLTracer)WCDBRustDatabaseSQLTrace, context, + WCDBRustDestructContext); +} +// +// void WCDBRustDatabaseClassMethod(setFullSQLTraceEnable, jlong self, jboolean enable) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBDatabaseSetFullSQLTraceEnable(selfStruct, enable); +//} +// +// void WCDBRustDatabaseErrorTrace(jobject tracer, CPPError error) +//{ +// WCDBRustTryGetEnvOr(return ); +// WCDBRustTryGetDatabaseMethodId( +// "onTraceException", "(" WCDBRustDatabaseSignature "$ExceptionTracer;J)V", return ); +// (*env)->CallStaticVoidMethod( +// env, WCDBRustGetDatabaseClass(), g_methodId, tracer, (jlong) error.innerValue); +// WCDBRustTryDetach; +//} +// + +typedef struct WCDBRustGlobalTraceExceptionContext { + RustGlobalTraceTraceExceptionCallback rust_callback; +} WCDBRustGlobalTraceExceptionContext; + +void WCDBRustDatabaseErrorGlobalTrace(WCDBRustGlobalTraceExceptionContext* context, + CPPError error) { + if (context == NULL || context->rust_callback == NULL) { + return; + } + context->rust_callback(error.innerValue); +} + +void WCDBRustDatabaseClassMethod(globalTraceException, + RustGlobalTraceTraceExceptionCallback rust_callback) { + size_t size = sizeof(RustGlobalTraceTraceExceptionCallback); + WCDBRustGlobalTraceExceptionContext* context = + (WCDBRustGlobalTraceExceptionContext*)WCDBRustCreateGlobalRef(size); + context->rust_callback = rust_callback; + WCDBDatabaseGlobalTraceError((WCDBErrorTracer)WCDBRustDatabaseErrorGlobalTrace, context, + WCDBRustDestructContext); +} + +typedef struct WCDBRustTraceExceptionContext { + RustTraceTraceExceptionCallback rust_callback; + void* cb_ptr; +} WCDBRustTraceExceptionContext; + +void WCDBRustDatabaseErrorTrace(WCDBRustTraceExceptionContext* context, CPPError error) { + if (context == NULL || context->rust_callback == NULL) { + return; + } + context->rust_callback(context->cb_ptr, error.innerValue); +} + +void WCDBRustDatabaseClassMethod(traceException, + void* self, + RustTraceTraceExceptionCallback rust_callback, + void* cb_ptr) { + WCDBRustBridgeStruct(CPPDatabase, self); + size_t size = sizeof(RustGlobalTraceTraceExceptionCallback); + WCDBRustTraceExceptionContext* context = + (WCDBRustTraceExceptionContext*)WCDBRustCreateGlobalRef(size); + context->rust_callback = rust_callback; + context->cb_ptr = cb_ptr; + WCDBDatabaseTraceError(selfStruct, (WCDBErrorTracer)WCDBRustDatabaseErrorTrace, context, + WCDBRustDestructContext); +} +// +// void WCDBRustDatabaseOperationTrace(jobject tracer, CPPDatabase database, long operation, const +// void* info) +//{ +// WCDBRustTryGetEnvOr(return ); +// WCDBRustTryGetDatabaseMethodId( +// "onTraceOperation", "(" WCDBRustDatabaseSignature "$OperationTracer;JIJ)V", return ); +// (*env)->CallStaticVoidMethod(env, +// WCDBRustGetDatabaseClass(), +// g_methodId, +// tracer, +// (jlong) database.innerValue, +// (jint) operation, +// (jlong) info); +// WCDBRustTryDetach; +//} +// +// void WCDBRustDatabaseClassMethod(globalTraceOperation, jobject tracer) +//{ +// WCDBRustTryGetVM; +// WCDBRustCreateGlobalRef(tracer); +// WCDBDatabaseGlobalTraceOperation( +// (tracer != NULL ? (WCDBOperationTracer) WCDBRustDatabaseOperationTrace : NULL), +// tracer, +// WCDBRustDestructContext); +//} +// +// typedef struct JNIEnumerateInfoContext { +// JNIEnv* env; +// jobject object; +//} JNIEnumerateInfoContext; +// +// void WCDBRustDatabaseEnumerateInfoCallback(JNIEnumerateInfoContext* context, +// const char* key, +// CPPCommonValue value) +//{ +// JNIEnv* env = context->env; +// jlong intValue = 0; +// double doubleValue = 0; +// const char* stringValue = NULL; +// switch (value.type) { +// case WCDBBridgedType_Int: +// intValue = (jlong) value.intValue; +// break; +// case WCDBBridgedType_Double: +// doubleValue = value.doubleValue; +// break; +// case WCDBBridgedType_String: +// stringValue = (const char*) value.intValue; +// break; +// default: +// break; +// } +// WCDBRustTryGetDatabaseMethodId("onEnumerateInfo", +// "(Ljava/util/HashMap;" WCDBRustStringSignature +// "IJD" WCDBRustStringSignature ")V", +// return ); +// WCDBRustCreateJavaString(key); +// WCDBRustCreateJavaString(stringValue); +// (*env)->CallStaticVoidMethod(env, +// WCDBRustGetDatabaseClass(), +// g_methodId, +// context->object, +// jkey, +// (int) value.type, +// intValue, +// doubleValue, +// jstringValue); +//} +// +// void WCDBRustDatabaseClassMethod(enumerateInfo, jobject javaInfo, jlong cppInfo) +//{ +// JNIEnumerateInfoContext context; +// context.object = javaInfo; +// context.env = env; +// WCDBEnumerateStringViewMap((const void*) cppInfo, +// (void*) &context, +// (StringViewMapEnumerator) WCDBRustDatabaseEnumerateInfoCallback); +//} +// +// void WCDBRustDatabaseBusyTrace(jobject tracer, long tag, const char* path, jlong tid, const char* +// sql) +//{ +// WCDBRustTryGetEnvOr(return ); +// WCDBRustTryGetDatabaseMethodId("onBusyTrace", +// "(" WCDBRustDatabaseSignature "$BusyTracer;J" +// WCDBRustStringSignature "J" WCDBRustStringSignature ")V", return +// ); +// WCDBRustCreateJavaString(path); +// WCDBRustCreateJavaString(sql); +// (*env)->CallStaticVoidMethod( +// env, WCDBRustGetDatabaseClass(), g_methodId, tracer, (jlong) tag, jpath, (jlong) tid, jsql); +// WCDBRustTryDetach; +//} +// +// void WCDBRustDatabaseClassMethod(globalTraceDatabaseBusy, jobject tracer, jdouble timeOut) +//{ +// WCDBRustTryGetVM; +// WCDBRustCreateGlobalRef(tracer); +// WCDBCoreGlobalTraceBusy( +// (tracer != NULL ? (WCDBBusyTracer) WCDBRustDatabaseBusyTrace : NULL), timeOut, tracer, +// WCDBRustDestructContext); +//} + +bool WCDBRustDatabaseClassMethod(removeFiles, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseRemoveFile(selfStruct); +} + +// +// jboolean WCDBRustDatabaseClassMethod(moveFile, jlong self, jstring destination) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBRustGetString(destination); +// jboolean ret = WCDBDatabaseMoveFile(selfStruct, destinationString); +// WCDBRustReleaseString(destination); +// return ret; +//} +// +// jlong WCDBRustDatabaseClassMethod(getFileSize, jlong self) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// OptionalUInt64 size = WCDBDatabaseGetFileSize(selfStruct); +// return size.hasValue ? size.value : -1; +//} +// +void WCDBRustDatabaseClassMethod(addTokenizer, void* self, const char* tokenizer) { + WCDBRustBridgeStruct(CPPDatabase, self); + WCDBDatabaseAddTokenizer(selfStruct, tokenizer); +} +// +// void WCDBRustDatabaseClassMethod(addAuxiliaryFunction, jlong self, jstring auxiliaryFunction) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBRustGetString(auxiliaryFunction); +// WCDBDatabaseAddAuxiliaryFunction(selfStruct, auxiliaryFunctionString); +// WCDBRustReleaseString(auxiliaryFunction); +//} +// +// void WCDBRustDatabaseCorrupted(jobject notification, CPPDatabase database) +//{ +// WCDBRustTryGetEnvOr(return ); +// WCDBRustTryGetDatabaseMethodId( +// "onCorrupted", "(" WCDBRustDatabaseSignature "$CorruptionNotification;J)V", return ); +// (*env)->CallStaticVoidMethod( +// env, WCDBRustGetDatabaseClass(), g_methodId, notification, (jlong) database.innerValue); +// WCDBRustTryDetach; +//} + +void WCDBRustDatabaseCorrupted(void* notification, CPPDatabase database) { + RustGlobalCorruptionNotificationCallback func = + (RustGlobalCorruptionNotificationCallback)notification; + func((void*)database.innerValue); +} + +void WCDBRustDatabaseClassMethod(setNotificationWhenCorrupted, + void* self, + RustGlobalCorruptionNotificationCallback* notification) { + WCDBRustBridgeStruct(CPPDatabase, self); + WCDBDatabaseSetNotificationWhenCorrupted( + selfStruct, notification == NULL ? NULL : WCDBRustDatabaseCorrupted, notification, + (WCDBContextDestructor)WCDBRustDestructContext); +} + +bool WCDBRustDatabaseClassMethod(checkIfCorrupted, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseCheckIfCorrupted(selfStruct); +} + +bool WCDBRustDatabaseClassMethod(checkIfIsAlreadyCorrupted, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseCheckIsAlreadyCorrupted(selfStruct); +} + +void WCDBRustDatabaseClassMethod(enableAutoBackup, void* self, bool enable) { + WCDBRustBridgeStruct(CPPDatabase, self); + WCDBDatabaseEnableAutoBackup(selfStruct, enable); +} + +bool WCDBRustDatabaseClassMethod(backup, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseBackup(selfStruct); +} + +bool WCDBRustDatabaseTableShouldBeBackup(void* context, const char* table) { + if (context == NULL) { + return false; + } + RustTableShouldBeBackupCallback callback = (RustTableShouldBeBackupCallback)context; + return callback(table); +} + +void WCDBRustDatabaseClassMethod(filterBackup, + void* self, + RustTableShouldBeBackupCallback* tableShouldBeBackup) { + WCDBRustBridgeStruct(CPPDatabase, self); + WCDBDatabaseFilterBackup( + selfStruct, tableShouldBeBackup == NULL ? NULL : WCDBRustDatabaseTableShouldBeBackup, + tableShouldBeBackup, (WCDBContextDestructor)WCDBRustDestructContext); +} + +bool WCDBRustDatabaseClassMethod(deposit, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseDeposit(selfStruct); +} + +bool WCDBRustDatabaseClassMethod(removeDepositedFiles, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseRemoveDepositedFiles(selfStruct); +} + +bool WCDBRustDatabaseClassMethod(containDepositedFiles, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseContainDepositedFiles(selfStruct); +} + +typedef struct WCDBRustGlobalProgressMonitorContext { + RustProgressMonitorCallback rust_callback; +} WCDBRustGlobalProgressMonitorContext; + +bool WCDBRustDatabaseOnProgressUpdate(WCDBRustGlobalProgressMonitorContext* context, + double percentage, + double increment) { + if (context == NULL || context->rust_callback == NULL) { + return false; + } + return context->rust_callback(percentage, increment); +} + +double WCDBRustDatabaseClassMethod(retrieve, + void* self, + RustProgressMonitorCallback* onProgressUpdate) { + size_t size = sizeof(RustProgressMonitorCallback); + WCDBRustGlobalProgressMonitorContext* context = + (WCDBRustGlobalProgressMonitorContext*)WCDBRustCreateGlobalRef(size); + context->rust_callback = onProgressUpdate; + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseRetrieve( + selfStruct, + onProgressUpdate != NULL ? (WCDBProgressUpdate)WCDBRustDatabaseOnProgressUpdate : NULL, + context, (WCDBContextDestructor)WCDBRustDestructContext); +} + +bool WCDBRustDatabaseClassMethod(vacuum, + void* self, + RustProgressMonitorCallback* onProgressUpdate) { + WCDBRustBridgeStruct(CPPDatabase, self); + size_t size = sizeof(RustProgressMonitorCallback); + WCDBRustGlobalProgressMonitorContext* context = + (WCDBRustGlobalProgressMonitorContext*)WCDBRustCreateGlobalRef(size); + context->rust_callback = onProgressUpdate; + return WCDBDatabaseVacuum( + selfStruct, + onProgressUpdate != NULL ? (WCDBProgressUpdate)WCDBRustDatabaseOnProgressUpdate : NULL, + context, (WCDBContextDestructor)WCDBRustDestructContext); +} + +void WCDBRustDatabaseClassMethod(enableAutoVacuum, void* self, bool incremental) { + WCDBRustBridgeStruct(CPPDatabase, self); + WCDBDatabaseEnableAutoVacuum(selfStruct, incremental); +} + +bool WCDBRustDatabaseClassMethod(incrementalVacuum, void* self, int pageCount) { + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseIncrementalVacuum(selfStruct, pageCount); +} +// +// jboolean WCDBRustDatabaseClassMethod(passiveCheckpoint, jlong self) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// return WCDBDatabasePassiveCheckpoint(selfStruct); +//} +// +bool WCDBRustDatabaseClassMethod(truncateCheckpoint, void* self) { + WCDBRustBridgeStruct(CPPDatabase, self); + return WCDBDatabaseTruncateCheckpoint(selfStruct); +} + +void WCDBRustDatabaseClassMethod(setAutoCheckpointEnable, void* self, bool enable) { + WCDBRustBridgeStruct(CPPDatabase, self); + WCDBCoreSetAutoCheckpointEnable(selfStruct, (bool)enable); +} +// +// void WCDBRustDatabaseFilterMigrate(jobject filter, const char* table, void* info, +// WCDBMigrationInfoSetter setter) +//{ +// WCDBRustTryGetEnvOr(return ); +// WCDBRustTryGetDatabaseMethodId("filterMigrate", +// "(" WCDBRustDatabaseSignature +// "$MigrationFilter;JJ" WCDBRustStringSignature ")V", +// return ); +// WCDBRustCreateJavaString(table); +// (*env)->CallStaticVoidMethod( +// env, WCDBRustGetDatabaseClass(), g_methodId, filter, (jlong) setter, (jlong) info, jtable); +// WCDBRustTryDetach; +//} +// +// void WCDBRustDatabaseClassMethod( +// addMigrationSource, jlong self, jstring sourcePath, jbyteArray cipherKey, jobject filter) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBRustCreateGlobalRef(filter); +// WCDBRustGetString(sourcePath); +// WCDBRustGetByteArrayCritical(cipherKey); +// WCDBDatabaseAddMigration(selfStruct, +// sourcePathString, +// cipherKeyArray, +// cipherKeyLength, +// filter != NULL ? WCDBRustDatabaseFilterMigrate : NULL, +// filter, +// WCDBRustDestructContext); +// WCDBRustReleaseByteArrayCritical(cipherKey); +// WCDBRustReleaseString(sourcePath); +//} +// +// void WCDBRustDatabaseClassMethod(setMigrationInfo, jlong infoSetter, jlong info, jstring +// sourceTable, jlong filterCondition) +//{ +// WCDBRustGetStringCritical(sourceTable); +// WCDBRustBridgeStruct(CPPExpression, filterCondition); +// ((WCDBMigrationInfoSetter) infoSetter)((void*) info, sourceTableString, +// filterConditionStruct); WCDBRustReleaseStringCritical(sourceTable); +//} +// +// jboolean WCDBRustDatabaseClassMethod(stepMigration, jlong self) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// return WCDBDatabaseStepMigration(selfStruct); +//} +// +// void WCDBRustDatabaseClassMethod(enableAutoMigration, jlong self, jboolean flag) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBDatabaseEnableAutoMigration(selfStruct, flag); +//} +// +// void WCDBRustDatabaseOnTableMigrate(jobject notification, +// CPPDatabase database, +// const char* table, +// const char* sourceTable) +//{ +// WCDBRustTryGetEnvOr(return ); +// WCDBRustTryGetDatabaseMethodId( +// "onTableMigrated", +// "(" WCDBRustDatabaseSignature +// "$MigrationNotification;J" WCDBRustStringSignature WCDBRustStringSignature ")V", +// return ); +// WCDBRustCreateJavaString(table); +// WCDBRustCreateJavaString(sourceTable); +// (*env)->CallStaticVoidMethod( +// env, WCDBRustGetDatabaseClass(), g_methodId, notification, (jlong) database.innerValue, +// jtable, jsourceTable); WCDBRustTryDetach; +//} +// +// void WCDBRustDatabaseClassMethod(setNotificationWhenMigrated, jlong self, jobject onMigrated) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBRustTryGetVM; +// WCDBRustCreateGlobalRef(onMigrated); +// WCDBDatabaseSetNotificationWhenMigrated( +// selfStruct, onMigrated != NULL ? WCDBRustDatabaseOnTableMigrate : NULL, onMigrated, +// WCDBRustDestructContext); +//} +// +// jboolean WCDBRustDatabaseClassMethod(isMigrated, jlong self) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// return WCDBDatabaseIsMigrated(selfStruct); +//} +// +// typedef struct DataEnumeratorContext { +// JNIEnv* env; +// bool isString; +// jint totalCount; +// jint index; +// jobjectArray* objects; +// jobject preObject; +// jbyte* preContent; +//} DataEnumeratorContext; +// +// void WCDBRustTryReleaseLastElement(DataEnumeratorContext* context) +//{ +// if (context->preObject == NULL || context->preContent == NULL) { +// return; +// } +// JNIEnv* env = context->env; +// if (context->isString) { +// (*env)->ReleaseStringCritical( +// env, context->preObject, (const jchar*) context->preContent); +// WCDBClearAllPreAllocatedMemory(); +// } else { +// (*env)->ReleasePrimitiveArrayCritical( +// env, context->preObject, context->preContent, 0); +// } +// context->preContent = NULL; +// context->preObject = NULL; +//} +// +// CPPData WCDBRustDataEnumerator(DataEnumeratorContext* context) +//{ +// CPPData ret; +// if (context->index >= context->totalCount) { +// ret.buffer = NULL; +// ret.size = 0; +// return ret; +// } +// WCDBRustTryReleaseLastElement(context); +// JNIEnv* env = context->env; +// if (context->isString) { +// jstring string = (jstring) (*env)->GetObjectArrayElement( +// env, context->objects, context->index); +// WCDBRustGetStringCritical(string); +// ret.buffer = (unsigned char*) stringString; +// ret.size = stringString != NULL ? strlen(stringString) : 0; +// context->preObject = string; +// context->preContent = (jbyte*) string_utf16String; +// } else { +// jbyteArray array = (jbyteArray) (*env)->GetObjectArrayElement( +// env, context->objects, context->index); +// WCDBRustGetByteArrayCritical(array); +// ret.buffer = (unsigned char*) arrayArray; +// ret.size = arrayLength; +// context->preObject = array; +// context->preContent = (jbyte*) arrayArray; +// } +// context->index++; +// return ret; +//} +// +// jbyteArray +// WCDBRustDatabaseClassMethod(trainDictWithStrings, jobjectArray stringArray, jbyte dictId) +//{ +// DataEnumeratorContext context; +// context.env = env; +// context.isString = true; +// context.totalCount +// = stringArray != NULL ? (*env)->GetArrayLength(env, stringArray) : 0; +// context.objects = stringArray; +// context.preObject = NULL; +// context.preContent = NULL; +// context.index = 0; +// CPPData dict = WCDBDatabaseTrainDict( +// dictId, (WCDBDataEnumerator) WCDBRustDataEnumerator, &context); +// WCDBRustTryReleaseLastElement(&context); +// jbyteArray ret = NULL; +// if (dict.size > 0 && dict.buffer != NULL) { +// ret = (*env)->NewByteArray(env, dict.size); +// (*env)->SetByteArrayRegion(env, ret, 0, dict.size, (const jbyte*) dict.buffer); +// free(dict.buffer); +// } +// return ret; +//} +// +// jbyteArray WCDBRustDatabaseClassMethod(trainDictWithDatas, jobjectArray dataArray, jbyte dictId) +//{ +// DataEnumeratorContext context; +// context.env = env; +// context.isString = false; +// context.totalCount = dataArray != NULL ? (*env)->GetArrayLength(env, dataArray) : 0; +// context.objects = dataArray; +// context.preObject = NULL; +// context.preContent = NULL; +// context.index = 0; +// CPPData dict = WCDBDatabaseTrainDict( +// dictId, (WCDBDataEnumerator) WCDBRustDataEnumerator, &context); +// WCDBRustTryReleaseLastElement(&context); +// jbyteArray ret = NULL; +// if (dict.size > 0 && dict.buffer != NULL) { +// ret = (*env)->NewByteArray(env, dict.size); +// (*env)->SetByteArrayRegion(env, ret, 0, dict.size, (const jbyte*) dict.buffer); +// free(dict.buffer); +// } +// return ret; +//} +// +// jboolean WCDBRustDatabaseClassMethod(registerDict, jbyteArray dict, jbyte dictId) +//{ +// WCDBRustGetByteArray(dict); +// bool ret = WCDBDatabaseRegisterDict(dictArray, dictLength, dictId); +// WCDBRustReleaseByteArray(dict); +// return ret; +//} +// +// void WCDBRustDatabaseClassMethod(addZSTDNormalCompress, jlong info, jlong column) +//{ +// WCDBRustBridgeStruct(CPPColumn, column); +// WCDBDatabaseSetZSTDNormalCompress((void*) info, columnStruct); +//} +// +// void WCDBRustDatabaseClassMethod(addZSTDDictCompress, jlong info, jlong column, jbyte dictId) +//{ +// WCDBRustBridgeStruct(CPPColumn, column); +// WCDBDatabaseSetZSTDDictCompress((void*) info, columnStruct, dictId); +//} +// +// void WCDBRustDatabaseClassMethod(addZSTDMultiDictCompress, +// jlong info, +// jlong column, +// jlong matchColumn, +// jlongArray values, +// jbyteArray dictIds) +//{ +// WCDBRustGetLongArray(values); +// WCDBRustGetByteArray(dictIds); +// WCDBRustBridgeStruct(CPPColumn, column); +// WCDBRustBridgeStruct(CPPColumn, matchColumn); +// WCDBDatabaseSetZSTDMultiDictCompress( +// (void*) info, columnStruct, matchColumnStruct, (const long long*) valuesArray, dictIdsArray, +// dictIdsLength); WCDBRustReleaseLongArray(values); WCDBRustReleaseByteArray(dictIds); +//} +// +// void WCDBRustDatabaseClassMethod(enableReplaceCompression, jlong info) +//{ +// WCDBDatabaseEnableReplaceCompresssion((void*) info); +//} +// +// void WCDBRustDatabaseFilterCompress(jobject filter, const char* table, void* info) +//{ +// WCDBRustTryGetEnvOr(return ); +// WCDBRustTryGetDatabaseMethodId("filterCompress", +// "(" WCDBRustDatabaseSignature +// "$CompressionFilter;J" WCDBRustStringSignature ")V", +// return ); +// WCDBRustCreateJavaString(table); +// (*env)->CallStaticVoidMethod( +// env, WCDBRustGetDatabaseClass(), g_methodId, filter, (jlong) info, jtable); +// WCDBRustTryDetach; +//} +// +// void WCDBRustDatabaseClassMethod(setCompression, jlong self, jobject filter) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBRustCreateGlobalRef(filter); +// WCDBDatabaseSetCompression( +// selfStruct, filter != NULL ? WCDBRustDatabaseFilterCompress : NULL, filter, +// WCDBRustDestructContext); +//} +// +// void WCDBRustDatabaseClassMethod(disableCompressNewData, jlong self, jboolean disable) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBDatabaseDisableCompressNewData(selfStruct, disable); +//} +// +// jboolean WCDBRustDatabaseClassMethod(stepCompression, jlong self) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// return WCDBDatabaseStepCompression(selfStruct); +//} +// +// void WCDBRustDatabaseClassMethod(enableAutoCompression, jlong self, jboolean enable) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBDatabaseEnableAutoCompression(selfStruct, enable); +//} +// +// void WCDBRustDatabaseOnTableCompressed(jobject notification, CPPDatabase database, const char* +// table) +//{ +// WCDBRustTryGetEnvOr(return ); +// WCDBRustTryGetDatabaseMethodId("onTableCompressed", +// "(" WCDBRustDatabaseSignature +// "$CompressionNotification;J" WCDBRustStringSignature ")V", +// return ); +// WCDBRustCreateJavaString(table); +// (*env)->CallStaticVoidMethod( +// env, WCDBRustGetDatabaseClass(), g_methodId, notification, (jlong) database.innerValue, +// jtable); WCDBRustTryDetach; +//} +// +// void WCDBRustDatabaseClassMethod(setNotificationWhenCompressed, jlong self, jobject onCompressed) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBRustTryGetVM; +// WCDBRustCreateGlobalRef(onCompressed); +// WCDBDatabaseSetNotificationWhenCompressed( +// selfStruct, onCompressed != NULL ? WCDBRustDatabaseOnTableCompressed : NULL, onCompressed, +// WCDBRustDestructContext); +//} +// +// jboolean WCDBRustDatabaseClassMethod(isCompressed, jlong self) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// return WCDBDatabaseIsCompressed(selfStruct); +//} +// +// jdouble WCDBRustDatabaseClassMethod(rollbackCompression, jlong self, jobject onProgressUpdate) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// WCDBRustTryGetVM; +// WCDBRustCreateGlobalRef(onProgressUpdate); +// return WCDBDatabaseRollbackCompression( +// selfStruct, +// onProgressUpdate != NULL ? (WCDBProgressUpdate) WCDBRustDatabaseOnProgressUpdate : NULL, +// onProgressUpdate, +// WCDBRustDestructContext); +//} +// +// jint WCDBRustDatabaseClassMethod(getNumberOfAliveHandle, jlong self) +//{ +// WCDBRustBridgeStruct(CPPDatabase, self); +// return WCDBDatabaseGetAliveHandleCount(selfStruct); +//} diff --git a/src/rust/cpp/core/DatabaseRust.h b/src/rust/cpp/core/DatabaseRust.h new file mode 100644 index 000000000..c1210c904 --- /dev/null +++ b/src/rust/cpp/core/DatabaseRust.h @@ -0,0 +1,206 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "DatabaseBridge.h" +#include "WCDBRust.h" + +#include + +// #define WCDBRustDatabaseFuncName(funcName) WCDBRust(Database, funcName) +// #def ine WCDBRustDatabaseObjectMethod(funcName, ...) \ +// WCDBRustObjectMethod(Database, funcName, __VA_ARGS__) +// #def ine WCDBRustDatabaseObjectMethodWithNoArg(funcName) \ +// WCDBRustObjectMethodWithNoArg(Database, funcName) +// #def ine WCDBRustDatabaseClassMethodWithNoArg(funcName) \ +// WCDBRustClassMethodWithNoArg(Database, funcName) +#define WCDBRustDatabaseClassMethod(funcName, ...) \ + WCDBRustClassMethod(Database, funcName, __VA_ARGS__) + +// #define WCDBRustDatabaseSignature "Lcom/tencent/wcdb/core/Database" +// +void* WCDBRustDatabaseClassMethod(getError, void* self); + +void* WCDBRustDatabaseClassMethod(getTag, void* self); +void WCDBRustDatabaseClassMethod(setTag, void* self, long tag); +const char* WCDBRustDatabaseClassMethod(getPath, void* self); + +// jobject WCDBRustDatabaseClassMethod(getPaths, jlong self); +void* WCDBRustDatabaseClassMethod(getHandle, void* self, bool writeHint); + +bool WCDBRustDatabaseClassMethod(canOpen, void* self); + +bool WCDBRustDatabaseClassMethod(isOpened, void* self); +bool WCDBRustDatabaseClassMethod(isBlockaded, void* self); +void WCDBRustDatabaseClassMethod(close, + void* self, + void* context, + WCDBDatabaseCloseCallback callback); + +void WCDBRustDatabaseClassMethod(blockade, void* self); +void WCDBRustDatabaseClassMethod(unblockade, void* self); +// void WCDBRustDatabaseClassMethod(purge, jlong self); +// +void WCDBRustDatabaseClassMethod(configCipher, + void* self, + uint8_t* cipherKey, + size_t len, + int pageSize, + int cipherVersion); + +typedef bool (*RustSetConfigCallback)(void* cpp_handle); + +void WCDBRustDatabaseClassMethod(config, + void* self, + const char* name, + RustSetConfigCallback* invocation, + RustSetConfigCallback* unInvocation, + int priority); + +// void WCDBRustDatabaseClassMethod(enableLiteMode, jlong self, jboolean enable); +// +typedef void (*RustGlobalTracePerformanceCallback)(long, + const char*, + unsigned long long, + const char*, + const CPPPerformanceInfo*); + +void WCDBRustDatabaseClassMethod(globalTracePerformance, + RustGlobalTracePerformanceCallback rust_callback); + +typedef void (*RustTracePerformanceCallback)(void*, + long, + const char*, + unsigned long long, + const char*, + const CPPPerformanceInfo*); + +void WCDBRustDatabaseClassMethod(tracePerformance, + void* self, + RustTracePerformanceCallback rust_callback, + void* cb_ptr); + +typedef void ( + *RustGlobalTraceSQLCallback)(long, const char*, unsigned long long, const char*, const char*); + +void WCDBRustDatabaseClassMethod(globalTraceSQL, RustGlobalTraceSQLCallback rust_callback); + +typedef void ( + *RustTraceSQLCallback)(void*, long, const char*, unsigned long long, const char*, const char*); + +void WCDBRustDatabaseClassMethod(traceSQL, + void* self, + RustTraceSQLCallback rust_callback, + void* cb_ptr); +// void WCDBRustDatabaseClassMethod(setFullSQLTraceEnable, jlong self, jboolean enable); +// + +typedef void (*RustGlobalTraceTraceExceptionCallback)(void* exception); +void WCDBRustDatabaseClassMethod(globalTraceException, + RustGlobalTraceTraceExceptionCallback rust_callback); + +typedef void (*RustTraceTraceExceptionCallback)(void* cb_ptr, void* exception); +void WCDBRustDatabaseClassMethod(traceError, + void* self, + RustTraceTraceExceptionCallback rust_callback, + void* cb_ptr); + +// void WCDBRustDatabaseClassMethod(globalTraceOperation, jobject tracer); +// void WCDBRustDatabaseClassMethod(enumerateInfo, jobject javaInfo, jlong cppInfo); +// +// void WCDBRustDatabaseClassMethod(globalTraceDatabaseBusy, jobject tracer, jdouble timeOut); +// +bool WCDBRustDatabaseClassMethod(removeFiles, void* self); +// jboolean WCDBRustDatabaseClassMethod(moveFile, jlong self, jstring destination); +// +// jlong WCDBRustDatabaseClassMethod(getFileSize, jlong self); +// +void WCDBRustDatabaseClassMethod(addTokenizer, void* self, const char* tokenizer); +// void WCDBRustDatabaseClassMethod(addAuxiliaryFunction, jlong self, jstring auxiliaryFunction); + +typedef void (*RustGlobalCorruptionNotificationCallback)(void* self); +void WCDBRustDatabaseClassMethod(setNotificationWhenCorrupted, + void* self, + RustGlobalCorruptionNotificationCallback* notification); + +bool WCDBRustDatabaseClassMethod(checkIfCorrupted, void* self); +bool WCDBRustDatabaseClassMethod(checkIfIsAlreadyCorrupted, void* self); +void WCDBRustDatabaseClassMethod(enableAutoBackup, void* self, bool enable); +bool WCDBRustDatabaseClassMethod(backup, void* self); + +typedef bool (*RustTableShouldBeBackupCallback)(const char* table); +void WCDBRustDatabaseClassMethod(filterBackup, + void* self, + RustTableShouldBeBackupCallback* tableShouldBeBackup); + +bool WCDBRustDatabaseClassMethod(deposit, void* self); +bool WCDBRustDatabaseClassMethod(removeDepositedFiles, void* self); +bool WCDBRustDatabaseClassMethod(containDepositedFiles, void* self); +typedef bool (*RustProgressMonitorCallback)(double percentage, double increment); +double WCDBRustDatabaseClassMethod(retrieve, + void* self, + RustProgressMonitorCallback* onProgressUpdate); +bool WCDBRustDatabaseClassMethod(vacuum, void* self, RustProgressMonitorCallback* onProgressUpdate); +void WCDBRustDatabaseClassMethod(enableAutoVacuum, void* self, bool incremental); +bool WCDBRustDatabaseClassMethod(incrementalVacuum, void* self, int pageCount); +// +// jboolean WCDBRustDatabaseClassMethod(passiveCheckpoint, jlong self); +bool WCDBRustDatabaseClassMethod(truncateCheckpoint, void* self); +void WCDBRustDatabaseClassMethod(setAutoCheckpointEnable, void* self, bool enable); +// +// void WCDBRustDatabaseClassMethod(addMigrationSource, +// jlong self, +// jstring sourcePath, +// jbyteArray cipherKey, +// jobject filter); +// void WCDBRustDatabaseClassMethod(setMigrationInfo, +// jlong infoSetter, +// jlong info, +// jstring sourceTable, +// jlong filterCondition); +// jboolean WCDBRustDatabaseClassMethod(stepMigration, jlong self); +// void WCDBRustDatabaseClassMethod(enableAutoMigration, jlong self, jboolean flag); +// void WCDBRustDatabaseClassMethod(setNotificationWhenMigrated, jlong self, jobject onMigrated); +// jboolean WCDBRustDatabaseClassMethod(isMigrated, jlong self); +// +// jbyteArray +// WCDBRustDatabaseClassMethod(trainDictWithStrings, jobjectArray stringArray, jbyte dictId); +// jbyteArray +// WCDBRustDatabaseClassMethod(trainDictWithDatas, jobjectArray dataArray, jbyte dictId); +// jboolean WCDBRustDatabaseClassMethod(registerDict, jbyteArray dict, jbyte dictId); +// void WCDBRustDatabaseClassMethod(addZSTDNormalCompress, jlong info, jlong column); +// void WCDBRustDatabaseClassMethod(addZSTDDictCompress, jlong info, jlong column, jbyte dictId); +// void WCDBRustDatabaseClassMethod(addZSTDMultiDictCompress, +// jlong info, +// jlong column, +// jlong matchColumn, +// jlongArray values, +// jbyteArray dictIds); +// void WCDBRustDatabaseClassMethod(enableReplaceCompression, jlong info); +// void WCDBRustDatabaseClassMethod(setCompression, jlong self, jobject filter); +// void WCDBRustDatabaseClassMethod(disableCompressNewData, jlong self, jboolean disable); +// jboolean WCDBRustDatabaseClassMethod(stepCompression, jlong self); +// void WCDBRustDatabaseClassMethod(enableAutoCompression, jlong self, jboolean enable); +// void WCDBRustDatabaseClassMethod(setNotificationWhenCompressed, jlong self, jobject +// onCompressed); jboolean WCDBRustDatabaseClassMethod(isCompressed, jlong self); jdouble +// WCDBRustDatabaseClassMethod(rollbackCompression, jlong self, jobject onProgressUpdate); +// +// jint WCDBRustDatabaseClassMethod(getNumberOfAliveHandle, jlong self); diff --git a/src/rust/cpp/core/HandleRust.c b/src/rust/cpp/core/HandleRust.c new file mode 100644 index 000000000..b3869a5e4 --- /dev/null +++ b/src/rust/cpp/core/HandleRust.c @@ -0,0 +1,201 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "HandleRust.h" + +#include "HandleBridge.h" +#include "assert.h" + +void* WCDBRustHandleClassMethod(getError, void* self) { + WCDBRustBridgeStruct(CPPHandle, self); + return (void*)WCDBHandleGetError(selfStruct).innerValue; +} + +// jlong WCDBRustHandleClassMethod(getOrCreatePreparedStatement, void* self, jlong statement) +//{ +// WCDBRustBridgeStruct(CPPHandle, self); +// return (jlong) WCDBHandleGetOrCreatePreparedStatement(selfStruct, (CPPObject *) statement) +// .innerValue; +// } +// +// jlong WCDBRustHandleClassMethod(getOrCreatePreparedStatementWithSQL, void* self, jstring sql) +//{ +// WCDBRustBridgeStruct(CPPHandle, self); +// WCDBRustGetString(sql); +// jlong ret = (jlong) WCDBHandleGetOrCreatePreparedSQL(selfStruct, sqlString).innerValue; +// WCDBRustReleaseString(sql); +// return ret; +// } + +void* WCDBRustHandleClassMethod(getMainStatement, void* self) { + WCDBRustBridgeStruct(CPPHandle, self); + return (void*)WCDBHandleGetMainStatement(selfStruct).innerValue; +} + +// void WCDBRustHandleClassMethod(finalizeAllStatements, void* self) +//{ +// WCDBRustBridgeStruct(CPPHandle, self); +// WCDBHandleFinalizeStatements(selfStruct); +// } +// +bool WCDBRustHandleClassMethod(execute, void* self, void* statement) { + WCDBRustBridgeStruct(CPPHandle, self); + return WCDBHandleExecute(selfStruct, (CPPObject*)statement); +} + +bool WCDBRustHandleClassMethod(executeSQL, void* self, const char* sql) { + WCDBRustBridgeStruct(CPPHandle, self); + return WCDBHandleExecuteSQL(selfStruct, sql); +} + +int WCDBRustHandleClassMethod(tableExist, void* self, const char* table) { + WCDBRustBridgeStruct(CPPHandle, self); + OptionalBool ret = WCDBHandleExistTable(selfStruct, table); + return ret.hasValue ? (int)ret.value : 2; +} + +int WCDBRustHandleClassMethod(getChanges, void* self) { + WCDBRustBridgeStruct(CPPHandle, self); + return WCDBHandleGetChange(selfStruct); +} + +// jint WCDBRustHandleClassMethod(getTotalChanges, void* self) +//{ +// WCDBRustBridgeStruct(CPPHandle, self); +// return WCDBHandleGetTotalChange(selfStruct); +// } + +long long WCDBRustHandleClassMethod(getLastInsertRowid, void* self) { + WCDBRustBridgeStruct(CPPHandle, self); + return WCDBHandleGetLastInsertedRowID(selfStruct); +} + +// jboolean WCDBRustHandleClassMethod(isInTransaction, void* self) +//{ +// WCDBRustBridgeStruct(CPPHandle, self); +// return WCDBHandleIsInTransaction(selfStruct); +// } +// +// jboolean WCDBRustHandleClassMethod(beginTransaction, void* self) +//{ +// WCDBRustBridgeStruct(CPPHandle, self); +// return WCDBHandleBeginTransaction(selfStruct); +// } +// +// jboolean WCDBRustHandleClassMethod(commitTransaction, void* self) +//{ +// WCDBRustBridgeStruct(CPPHandle, self); +// return WCDBHandleCommitTransaction(selfStruct); +// } +// +// void WCDBRustHandleClassMethod(rollbackTransaction, void* self) +//{ +// WCDBRustBridgeStruct(CPPHandle, self); +// return WCDBHandleRollbackTransaction(selfStruct); +// } + +typedef struct TransactionContext { + RustTransactionCallback rust_callback; + void* closure_raw; + void* rust_handle_raw; +} TransactionContext; + +bool WCDBRustHandleTransactionCallBack(TransactionContext* context, CPPHandle handle) { + return context->rust_callback(context->closure_raw, context->rust_handle_raw); +} + +bool WCDBRustHandleObjectMethod(runTransaction, + void* self, + RustTransactionCallback rust_callback, + void* closure_raw, + void* rust_handle_raw) { + WCDBRustBridgeStruct(CPPHandle, self); + TransactionContext context; + context.rust_callback = rust_callback; + context.closure_raw = closure_raw; + context.rust_handle_raw = rust_handle_raw; + return WCDBHandleRunTransaction(selfStruct, &context, + (TransactionCallback)WCDBRustHandleTransactionCallBack); +} + +// bool WCDBRustHandlePausableTransactionCallBack(TransactionContext *context, +// CPPHandle handle, +// bool *stop, +// bool isNewTransaction) +//{ +// JNIEnv *env = context->env; +// +// static jmethodID g_methodId = NULL; +// if (g_methodId == NULL) { +// g_methodId = (*env)->GetMethodID( +// env, WCDBRustGetHandleClass(), "onPausableTransaction", +// "(JLcom/tencent/wcdb/core/PausableTransaction;Z)I"); if (g_methodId == NULL) { +// assert(0); +// return false; +// } +// } +// jint ret = (*env)->CallIntMethod( +// env, context->handle, g_methodId, (jlong) handle.innerValue, context->transaction, +// isNewTransaction); if ((*env)->ExceptionCheck(env)) { +// ret = 2; +// } +// if (ret == 2) { +// return false; +// } else { +// *stop = ret == 1; +// return true; +// } +// } +// +// jboolean WCDBRustHandleObjectMethod(runPausableTransaction, void* self, jobject transaction) +//{ +// WCDBRustBridgeStruct(CPPHandle, self); +// TransactionContext context; +// context.env = env; +// context.handle = obj; +// context.transaction = transaction; +// return WCDBHandleRunPausableTransaction( +// selfStruct, &context, (PausableTransaction) WCDBRustHandlePausableTransactionCallBack); +// } +// +// jlong WCDBRustHandleClassMethodWithNoArg(createCancellationSignal) +//{ +// jlong ret = (jlong) WCDBCancellationSignalCreate().innerValue; +// return ret; +// } +// +// void WCDBRustHandleClassMethod(cancelSignal, jlong signal) +//{ +// WCDBRustBridgeStruct(CPPCancellationSignal, signal); +// WCDBCancellationSignalCancel(signalStruct); +// } +// +// void WCDBRustHandleClassMethod(attachCancellationSignal, void* self, jlong signal) +//{ +// WCDBRustBridgeStruct(CPPHandle, self); +// WCDBRustBridgeStruct(CPPCancellationSignal, signal); +// WCDBHandleAttachCancellationSignal(selfStruct, signalStruct); +// } +// +// void WCDBRustHandleClassMethod(detachCancellationSignal, void* self) +//{ +// WCDBRustBridgeStruct(CPPHandle, self); +// WCDBHandleDettachCancellationSignal(selfStruct); +// } diff --git a/src/rust/cpp/core/HandleRust.h b/src/rust/cpp/core/HandleRust.h new file mode 100644 index 000000000..8775a8f44 --- /dev/null +++ b/src/rust/cpp/core/HandleRust.h @@ -0,0 +1,67 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustHandleFuncName(funcName) WCDBRust(Handle, funcName) +#define WCDBRustHandleObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(Handle, funcName, __VA_ARGS__) +#define WCDBRustHandleObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(Handle, funcName) +#define WCDBRustHandleClassMethodWithNoArg(funcName) WCDBRustClassMethodWithNoArg(Handle, funcName) +#define WCDBRustHandleClassMethod(funcName, ...) WCDBRustClassMethod(Handle, funcName, __VA_ARGS__) + +void* WCDBRustHandleClassMethod(getError, void* self); + +// jlong WCDBRustHandleClassMethod(getOrCreatePreparedStatement, void* self, jlong statement); +// jlong WCDBRustHandleClassMethod(getOrCreatePreparedStatementWithSQL, void* self, jstring sql); +void* WCDBRustHandleClassMethod(getMainStatement, void* self); +// void WCDBRustHandleClassMethod(finalizeAllStatements, void* self); +bool WCDBRustHandleClassMethod(execute, void* self, void* statement); + +bool WCDBRustHandleClassMethod(executeSQL, void* self, const char* sql); +int WCDBRustHandleClassMethod(tableExist, void* self, const char* table); + +int WCDBRustHandleClassMethod(getChanges, void* self); + +// jint WCDBRustHandleClassMethod(getTotalChanges, void* self); +long long WCDBRustHandleClassMethod(getLastInsertRowid, void* self); +// +// jboolean WCDBRustHandleClassMethod(isInTransaction, void* self); +// jboolean WCDBRustHandleClassMethod(beginTransaction, void* self); +// jboolean WCDBRustHandleClassMethod(commitTransaction, void* self); +// void WCDBRustHandleClassMethod(rollbackTransaction, void* self); + +typedef bool (*RustTransactionCallback)(void* closure_raw, void* rust_handle_raw); + +bool WCDBRustHandleObjectMethod(runTransaction, + void* self, + RustTransactionCallback rust_callback, + void* closure_raw, + void* rust_handle_raw); +// jboolean WCDBRustHandleObjectMethod(runPausableTransaction, void* self, jobject transaction); +// +// jlong WCDBRustHandleClassMethodWithNoArg(createCancellationSignal); +// void WCDBRustHandleClassMethod(cancelSignal, jlong signal); +// +// void WCDBRustHandleClassMethod(attachCancellationSignal, void* self, jlong signal); +// void WCDBRustHandleClassMethod(detachCancellationSignal, void* self); diff --git a/src/rust/cpp/core/HandleStatementRust.c b/src/rust/cpp/core/HandleStatementRust.c new file mode 100644 index 000000000..46fe18689 --- /dev/null +++ b/src/rust/cpp/core/HandleStatementRust.c @@ -0,0 +1,169 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "HandleStatementRust.h" + +#include "HandleStatementBridge.h" + +void* WCDBRustHandleStatementClassMethod(getError, void* self) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + return (void*)WCDBHandleStatementGetError(selfStruct).innerValue; +} + +bool WCDBRustHandleStatementClassMethod(prepare, void* self, void* statement) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + return WCDBHandleStatementPrepare(selfStruct, (CPPObject*)statement); +} + +bool WCDBRustHandleStatementClassMethod(prepareSQL, void* self, const char* sql) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + bool ret = WCDBHandleStatementPrepareSQL(selfStruct, sql); + return ret; +} + +// bool WCDBRustHandleStatementClassMethod(checkPrepared, void* self) +//{ +// WCDBRustBridgeStruct(CPPHandleStatement, self); +// return WCDBHandleStatementCheckPrepared(selfStruct); +// } + +bool WCDBRustHandleStatementClassMethod(step, void* self) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + return WCDBHandleStatementStep(selfStruct); +} + +void WCDBRustHandleStatementClassMethod(reset, void* self) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + WCDBHandleStatementReset(selfStruct); +} + +// void WCDBRustHandleStatementClassMethod(clearBindings, void* self) +//{ +// WCDBRustBridgeStruct(CPPHandleStatement, self); +// WCDBHandleStatementClearBindings(selfStruct); +// } + +void WCDBRustHandleStatementClassMethod(finalize, void* self) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + WCDBHandleStatementFinalize(selfStruct); +} + +bool WCDBRustHandleStatementClassMethod(isDone, void* self) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + return WCDBHandleStatementIsDone(selfStruct); +} + +void WCDBRustHandleStatementClassMethod(bindInteger, void* self, long long value, int index) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + WCDBHandleStatementBindInteger(selfStruct, index, value); +} + +void WCDBRustHandleStatementClassMethod(bindDouble, void* self, double value, int index) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + WCDBHandleStatementBindDouble(selfStruct, index, value); +} + +void WCDBRustHandleStatementClassMethod(bindText, void* self, const char* value, int index) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + WCDBHandleStatementBindText(selfStruct, index, value); +} + +void WCDBRustHandleStatementClassMethod(bindBLOB, + void* self, + const unsigned char* data, + size_t len, + int index) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + WCDBHandleStatementBindBlob(selfStruct, index, data, len); +} + +void WCDBRustHandleStatementClassMethod(bindNull, void* self, int index) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + WCDBHandleStatementBindNull(selfStruct, index); +} + +// jint WCDBRustHandleStatementClassMethod(bindParameterIndex, void* self, jstring parameterName) +//{ +// WCDBRustBridgeStruct(CPPHandleStatement, self); +// WCDBRustGetString(parameterName); +// jint index = WCDBHandleStatementBindParameterIndex(selfStruct, parameterNameString); +// WCDBRustReleaseString(parameterName); +// return index; +// } +// +int WCDBRustHandleStatementClassMethod(getColumnType, void* self, int index) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + return WCDBHandleStatementGetColumnType(selfStruct, index); +} + +long long WCDBRustHandleStatementClassMethod(getInteger, void* self, int index) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + return WCDBHandleStatementGetInteger(selfStruct, index); +} + +double WCDBRustHandleStatementClassMethod(getDouble, void* self, int index) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + return WCDBHandleStatementGetDouble(selfStruct, index); +} + +const char* WCDBRustHandleStatementClassMethod(getText, void* self, int index) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + return WCDBHandleStatementGetText(selfStruct, index); +} + +void WCDBRustHandleStatementClassMethod(getBLOB, + void* self, + int index, + const unsigned char** data, + signed long long* dataSize) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + signed long long length = WCDBHandleStatementGetColumnSize(selfStruct, index); + *data = WCDBHandleStatementGetBlob(selfStruct, index); + *dataSize = length; +} + +int WCDBRustHandleStatementClassMethod(getColumnCount, void* self) { + WCDBRustBridgeStruct(CPPHandleStatement, self); + return WCDBHandleStatementGetColumnCount(selfStruct); +} + +// jstring WCDBRustHandleStatementClassMethod(getColumnName, void* self, jint index) +//{ +// WCDBRustBridgeStruct(CPPHandleStatement, self); +// WCDBRustCreateJStringAndReturn(WCDBHandleStatementGetColumnName(selfStruct, index)); +//} +// +// jstring WCDBRustHandleStatementClassMethod(getOriginalColumnName, void* self, jint index) +//{ +// WCDBRustBridgeStruct(CPPHandleStatement, self); +// WCDBRustCreateJStringAndReturn(WCDBHandleStatementGetOriginalColumnName(selfStruct, index)); +//} +// +// jstring WCDBRustHandleStatementClassMethod(getColumnTableName, void* self, jint index) +//{ +// WCDBRustBridgeStruct(CPPHandleStatement, self); +// WCDBRustCreateJStringAndReturn(WCDBHandleStatementGetColumnTableName(selfStruct, index)); +//} +// +// bool WCDBRustHandleStatementClassMethod(isReadOnly, void* self) +//{ +// WCDBRustBridgeStruct(CPPHandleStatement, self); +// return WCDBHandleStatementIsReadOnly(selfStruct); +//} diff --git a/src/rust/cpp/core/HandleStatementRust.h b/src/rust/cpp/core/HandleStatementRust.h new file mode 100644 index 000000000..cf1e0b8eb --- /dev/null +++ b/src/rust/cpp/core/HandleStatementRust.h @@ -0,0 +1,78 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustHandleStatementFuncName(funcName) WCDBRust(HandleStatement, funcName) +#define WCDBRustHandleStatementObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(HandleStatement, funcName, __VA_ARGS__) +#define WCDBRustHandleStatementObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(HandleStatement, funcName) +#define WCDBRustHandleStatementClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(HandleStatement, funcName) +#define WCDBRustHandleStatementClassMethod(funcName, ...) \ + WCDBRustClassMethod(HandleStatement, funcName, __VA_ARGS__) + +void* WCDBRustHandleStatementClassMethod(getError, void* self); + +bool WCDBRustHandleStatementClassMethod(prepare, void* self, void* statement); +bool WCDBRustHandleStatementClassMethod(prepareSQL, void* self, const char* sql); +// bool WCDBRustHandleStatementClassMethod(checkPrepared, void* self); +bool WCDBRustHandleStatementClassMethod(step, void* self); + +void WCDBRustHandleStatementClassMethod(reset, void* self); + +// void WCDBRustHandleStatementClassMethod(clearBindings, void* self); +void WCDBRustHandleStatementClassMethod(finalize, void* self); + +bool WCDBRustHandleStatementClassMethod(isDone, void* self); + +void WCDBRustHandleStatementClassMethod(bindInteger, void* self, long long value, int index); + +void WCDBRustHandleStatementClassMethod(bindDouble, void* self, double value, int index); + +void WCDBRustHandleStatementClassMethod(bindText, void* self, const char* value, int index); + +void WCDBRustHandleStatementClassMethod(bindBLOB, + void* self, + const unsigned char* data, + size_t len, + int index); +void WCDBRustHandleStatementClassMethod(bindNull, void* self, int index); + +// jint WCDBRustHandleStatementClassMethod(bindParameterIndex, void* self, jstring parameterName); +int WCDBRustHandleStatementClassMethod(getColumnType, void* self, int index); +long long WCDBRustHandleStatementClassMethod(getInteger, void* self, int index); + +double WCDBRustHandleStatementClassMethod(getDouble, void* self, int index); + +const char* WCDBRustHandleStatementClassMethod(getText, void* self, int index); +void WCDBRustHandleStatementClassMethod(getBLOB, + void* self, + int index, + const unsigned char** data, + signed long long* dataSize); +int WCDBRustHandleStatementClassMethod(getColumnCount, void* self); +// jstring WCDBRustHandleStatementClassMethod(getColumnName, void* self, jint index); +// jstring WCDBRustHandleStatementClassMethod(getOriginalColumnName, void* self, jint index); +// jstring WCDBRustHandleStatementClassMethod(getColumnTableName, void* self, jint index); +// bool WCDBRustHandleStatementClassMethod(isReadOnly, void* self); diff --git a/src/rust/cpp/winq/WinqRust.c b/src/rust/cpp/winq/WinqRust.c new file mode 100644 index 000000000..79ea15fbb --- /dev/null +++ b/src/rust/cpp/winq/WinqRust.c @@ -0,0 +1,31 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WinqRust.h" + +#include "WinqBridge.h" + +const char* WCDBRustClassMethod(Winq, getDescription, void* statement) { + WCDBWinqGetDescription((CPPObject*)statement); +} + +bool WCDBRustClassMethod(Winq, isWriteStatement, void* statement) { + return WCDBStatementNeedToWrite((CPPObject*)statement); +} diff --git a/src/rust/cpp/winq/WinqRust.h b/src/rust/cpp/winq/WinqRust.h new file mode 100644 index 000000000..931d9a0e8 --- /dev/null +++ b/src/rust/cpp/winq/WinqRust.h @@ -0,0 +1,27 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +const char* WCDBRustClassMethod(Winq, getDescription, void* statement); + +bool WCDBRustClassMethod(Winq, isWriteStatement, void* statement); diff --git a/src/rust/cpp/winq/identifier/BindParameterRust.c b/src/rust/cpp/winq/identifier/BindParameterRust.c new file mode 100644 index 000000000..ad26ea79d --- /dev/null +++ b/src/rust/cpp/winq/identifier/BindParameterRust.c @@ -0,0 +1,56 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BindParameterRust.h" + +#include "BindParameterBridge.h" + +void* WCDBRustBindParameterClassMethod(createQuestionSignType, int num) { + return WCDBBindparameterCreateQuestionSignType(num).innerValue; +} + +void* WCDBRustBindParameterClassMethod(createAtSignType, const char* name) { + // WCDBRustGetStringCritical(name); + void* ret = WCDBBindparameterCreateAtSignType(name).innerValue; + // WCDBRustReleaseStringCritical(name); + return ret; +} + +void* WCDBRustBindParameterClassMethod(createColonSignType, const char* name) { + // WCDBRustGetStringCritical(name); + void* ret = WCDBBindparameterCreateColonSignType(name).innerValue; + // WCDBRustReleaseStringCritical(name); + return ret; +} + +void* WCDBRustBindParameterClassMethod(createDollarSignType, const char* name) { + // WCDBRustGetStringCritical(name); + void* ret = WCDBBindparameterCreateDollarSignType(name).innerValue; + // WCDBRustReleaseStringCritical(name); + return ret; +} + +void** WCDBRustBindParameterClassMethod(bindParameters, int size) { + void** ret = malloc(sizeof(void*) * size); + for (int i = 0; i < size; i++) { + ret[i] = WCDBBindparameterCreateQuestionSignType(i + 1).innerValue; + } + return ret; +} \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/BindParameterRust.h b/src/rust/cpp/winq/identifier/BindParameterRust.h new file mode 100644 index 000000000..11e89fafc --- /dev/null +++ b/src/rust/cpp/winq/identifier/BindParameterRust.h @@ -0,0 +1,34 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include "WCDBRust.h" + +#define WCDBRustBindParameterFuncName(funcName) WCDBRust(BindParameter, funcName) +#define WCDBRustBindParameterObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(BindParameter, funcName, __VA_ARGS__) +#define WCDBRustBindParameterClassMethod(funcName, ...) \ + WCDBRustClassMethod(BindParameter, funcName, __VA_ARGS__) + +void* WCDBRustBindParameterClassMethod(createQuestionSignType, int num); +void* WCDBRustBindParameterClassMethod(createAtSignType, const char* name); +void* WCDBRustBindParameterClassMethod(createColonSignType, const char* name); +void* WCDBRustBindParameterClassMethod(createDollarSignType, const char* name); +void** WCDBRustBindParameterClassMethod(bindParameters, int size); \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/ColumnConstraintRust.c b/src/rust/cpp/winq/identifier/ColumnConstraintRust.c new file mode 100644 index 000000000..491b4ef84 --- /dev/null +++ b/src/rust/cpp/winq/identifier/ColumnConstraintRust.c @@ -0,0 +1,92 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ColumnConstraintRust.h" + +#include "ColumnConstraintBridge.h" + +void* WCDBRustColumnConstraintClassMethod(create, const char* name) { + void* ret = (void*)WCDBColumnConstraintCreate(name).innerValue; + return ret; +} + +void WCDBRustColumnConstraintClassMethod(configPrimaryKey, void* constraint) { + WCDBRustBridgeStruct(CPPColumnConstraint, constraint); + WCDBColumnConstraintConfigPrimaryKey(constraintStruct); +} + +// void WCDBRustColumnConstraintClassMethod(configOrder, jlong constraint, jint order) +//{ +// WCDBRustBridgeStruct(CPPColumnConstraint, constraint); +// WCDBColumnConstraintConfigOrder(constraintStruct, order); +// } +// +void WCDBRustColumnConstraintClassMethod(configConflictAction, + void* constraint, + int conflictAction) { + WCDBRustBridgeStruct(CPPColumnConstraint, constraint); + WCDBColumnConstraintConfigCoflictAction(constraintStruct, conflictAction); +} + +void WCDBRustColumnConstraintClassMethod(configAutoIncrement, void* constraint) { + WCDBRustBridgeStruct(CPPColumnConstraint, constraint); + WCDBColumnConstraintConfigAutoIncrement(constraintStruct); +} + +void WCDBRustColumnConstraintClassMethod(configNotNull, void* constraint) { + WCDBRustBridgeStruct(CPPColumnConstraint, constraint); + WCDBColumnConstraintConfigNotNull(constraintStruct); +} + +void WCDBRustColumnConstraintClassMethod(configUnique, void* constraint) { + WCDBRustBridgeStruct(CPPColumnConstraint, constraint); + WCDBColumnConstraintConfigUnique(constraintStruct); +} +// +// void WCDBRustColumnConstraintClassMethod(configCheck, jlong constraint, jlong expression) +//{ +// WCDBRustBridgeStruct(CPPColumnConstraint, constraint); +// WCDBRustBridgeStruct(CPPExpression, expression); +// WCDBColumnConstraintConfigCheck(constraintStruct, expressionStruct); +//} +// +void WCDBRustColumnConstraintClassMethod(configDefaultValue, + void* constraint, + WCDBRustCommonValueParameter(value)) { + WCDBRustBridgeStruct(CPPColumnConstraint, constraint); + WCDBRustCreateCommonValueWithIsCritical(value, true); + WCDBColumnConstraintConfigDefaultValue2(constraintStruct, value_common); +} + +void WCDBRustColumnConstraintClassMethod(configCollation, void* constraint, const char* collation) { + WCDBRustBridgeStruct(CPPColumnConstraint, constraint); + WCDBColumnConstraintConfigCollation(constraintStruct, collation); +} + +void WCDBRustColumnConstraintClassMethod(configForeignKey, void* constraint, void* foreignKey) { + WCDBRustBridgeStruct(CPPColumnConstraint, constraint); + WCDBRustBridgeStruct(CPPForeignKey, foreignKey); + WCDBColumnConstraintConfigForeignKey(constraintStruct, foreignKeyStruct); +} + +void WCDBRustColumnConstraintClassMethod(configUnIndex, void* constraint) { + WCDBRustBridgeStruct(CPPColumnConstraint, constraint); + WCDBColumnConstraintConfigUnIndexed(constraintStruct); +} diff --git a/src/rust/cpp/winq/identifier/ColumnConstraintRust.h b/src/rust/cpp/winq/identifier/ColumnConstraintRust.h new file mode 100644 index 000000000..953fc1370 --- /dev/null +++ b/src/rust/cpp/winq/identifier/ColumnConstraintRust.h @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustColumnConstraintFuncName(funcName) WCDBRust(ColumnConstraint, funcName) +#define WCDBRustColumnConstraintObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(ColumnConstraint, funcName, __VA_ARGS__) +#define WCDBRustColumnConstraintClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(ColumnConstraint, funcName) +#define WCDBRustColumnConstraintClassMethod(funcName, ...) \ + WCDBRustClassMethod(ColumnConstraint, funcName, __VA_ARGS__) + +void* WCDBRustColumnConstraintClassMethod(create, const char* name); + +void WCDBRustColumnConstraintClassMethod(configPrimaryKey, void* constraint); + +// void WCDBRustColumnConstraintClassMethod(configOrder, jlong constraint, jint order); + +void WCDBRustColumnConstraintClassMethod(configConflictAction, + void* constraint, + int conflictAction); + +void WCDBRustColumnConstraintClassMethod(configAutoIncrement, void* constraint); + +void WCDBRustColumnConstraintClassMethod(configNotNull, void* constraint); + +void WCDBRustColumnConstraintClassMethod(configUnique, void* constraint); +// +// void WCDBRustColumnConstraintClassMethod(configCheck, jlong constraint, jlong expression); +// +void WCDBRustColumnConstraintClassMethod(configDefaultValue, + void* constraint, + WCDBRustCommonValueParameter(value)); + +void WCDBRustColumnConstraintClassMethod(configCollation, void* constraint, const char* collation); + +void WCDBRustColumnConstraintClassMethod(configForeignKey, void* constraint, void* foreignKey); + +void WCDBRustColumnConstraintClassMethod(configUnindexed, void* constraint); diff --git a/src/rust/cpp/winq/identifier/ColumnDefRust.c b/src/rust/cpp/winq/identifier/ColumnDefRust.c new file mode 100644 index 000000000..82af9da8f --- /dev/null +++ b/src/rust/cpp/winq/identifier/ColumnDefRust.c @@ -0,0 +1,42 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ColumnDefRust.h" + +#include "ColumnDefBridge.h" + +void* WCDBRustColumnDefClassMethod(create, + WCDBRustObjectOrStringParameter(column), + int columnType) { + WCDBRustCreateObjectOrStringCommonValue(column, true); + void* ret = 0; + if (columnType != 0) { + ret = (void*)WCDBColumnDefCreateWithType2(column_common, columnType).innerValue; + } else { + ret = (void*)WCDBColumnDefCreateWithoutType2(column_common).innerValue; + } + return ret; +} + +void WCDBRustColumnDefClassMethod(constraint, void* columnDef, void* constraint) { + WCDBRustBridgeStruct(CPPColumnDef, columnDef); + WCDBRustBridgeStruct(CPPColumnConstraint, constraint); + WCDBColumnDefConfigConstraint(columnDefStruct, constraintStruct); +} diff --git a/src/rust/cpp/winq/identifier/ColumnDefRust.h b/src/rust/cpp/winq/identifier/ColumnDefRust.h new file mode 100644 index 000000000..6b54eb5ff --- /dev/null +++ b/src/rust/cpp/winq/identifier/ColumnDefRust.h @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustColumnDefFuncName(funcName) WCDBRust(ColumnDef, funcName) +#define WCDBRustColumnDefObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(ColumnDef, funcName, __VA_ARGS__) +#define WCDBRustColumnDefClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(ColumnDef, funcName) +#define WCDBRustColumnDefClassMethod(funcName, ...) \ + WCDBRustClassMethod(ColumnDef, funcName, __VA_ARGS__) + +void* WCDBRustColumnDefClassMethod(create, WCDBRustObjectOrStringParameter(column), int columnType); + +void WCDBRustColumnDefClassMethod(constraint, void* columnDef, void* constraint); \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/ColumnRust.c b/src/rust/cpp/winq/identifier/ColumnRust.c new file mode 100644 index 000000000..c4a053dfd --- /dev/null +++ b/src/rust/cpp/winq/identifier/ColumnRust.c @@ -0,0 +1,57 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ColumnRust.h" + +#include "ColumnBridge.h" + +void* WCDBRustColumnClassMethodWithNoArg(createAll) { + return (void*)WCDBColumnCreateAll().innerValue; +} + +void* WCDBRustColumnClassMethodWithNoArg(createRowId) { + return (void*)WCDBColumnCreateRowId().innerValue; +} + +void* WCDBRustColumn_createWithName(const char* name, void* binding) { + return (void*)WCDBColumnCreateWithName2(name, (const void*)binding).innerValue; +} + +// jlong WCDBRustColumnClassMethod(copy, jlong column) +//{ +// WCDBRustBridgeStruct(CPPColumn, column); +// return (jlong) WCDBColumnCopy(columnStruct).innerValue; +// } + +void WCDBRustColumnClassMethod(inTable, void* column, const char* table) { + WCDBRustBridgeStruct(CPPColumn, column); + WCDBColumnInTable(columnStruct, table); +} + +void WCDBRustColumnClassMethod(ofSchema, void* column, WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPColumn, column); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBColumnOfSchema2(columnStruct, schema_common); +} + +void* WCDBRustColumnClassMethod(configAlias, void* column, const char* alias) { + WCDBRustBridgeStruct(CPPColumn, column); + return WCDBColumnConfigAlias(columnStruct, alias).innerValue; +} diff --git a/src/rust/cpp/winq/identifier/ColumnRust.h b/src/rust/cpp/winq/identifier/ColumnRust.h new file mode 100644 index 000000000..c13a1d24e --- /dev/null +++ b/src/rust/cpp/winq/identifier/ColumnRust.h @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustColumnFuncName(funcName) WCDBRust(Column, funcName) +#define WCDBRustColumnObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(Column, funcName, __VA_ARGS__) +#define WCDBRustColumnClassMethodWithNoArg(funcName) WCDBRustClassMethodWithNoArg(Column, funcName) +#define WCDBRustColumnClassMethod(funcName, ...) WCDBRustClassMethod(Column, funcName, __VA_ARGS__) + +void* WCDBRustColumnClassMethodWithNoArg(createAll); + +void* WCDBRustColumnClassMethodWithNoArg(createRowId); + +void* WCDBRustColumnClassMethod(createWithName, const char* name, void* binding); + +// jlong WCDBRustColumnClassMethod(copy, jlong column); + +void WCDBRustColumnClassMethod(inTable, void* column, const char* table); + +void WCDBRustColumnClassMethod(ofSchema, void* column, WCDBRustObjectOrStringParameter(schema)); + +void* WCDBRustColumnClassMethod(configAlias, void* column, const char* alias); diff --git a/src/rust/cpp/winq/identifier/CommonTableExpressionRust.c b/src/rust/cpp/winq/identifier/CommonTableExpressionRust.c new file mode 100644 index 000000000..4c0b8b9dd --- /dev/null +++ b/src/rust/cpp/winq/identifier/CommonTableExpressionRust.c @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CommonTableExpressionRust.h" + +#include "CommonTableExpressionBridge.h" + +void* WCDBRustCommonTableExpressionClassMethod(createWithTable, const char* tableName) { + return (void*)WCDBCommonTableExpressionCreate(tableName).innerValue; +} + +void WCDBRustCommonTableExpressionClassMethod(configColumn, void* self, void* column) { + WCDBRustBridgeStruct(CPPCommonTableExpression, self); + WCDBRustBridgeStruct(CPPColumn, column); + WCDBCommonTableExpressionAddColumn(selfStruct, columnStruct); +} + +void WCDBRustCommonTableExpressionClassMethod(configSelectStatement, void* self, void* select) { + WCDBRustBridgeStruct(CPPCommonTableExpression, self); + WCDBRustBridgeStruct(CPPStatementSelect, select); + WCDBCommonTableExpressionAsSelection(selfStruct, selectStruct); +} diff --git a/src/rust/cpp/winq/identifier/CommonTableExpressionRust.h b/src/rust/cpp/winq/identifier/CommonTableExpressionRust.h new file mode 100644 index 000000000..87d5f70fa --- /dev/null +++ b/src/rust/cpp/winq/identifier/CommonTableExpressionRust.h @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustCommonTableExpressionFuncName(funcName) WCDBRust(CommonTableExpression, funcName) +#define WCDBRustCommonTableExpressionObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(CommonTableExpression, funcName, __VA_ARGS__) +#define WCDBRustCommonTableExpressionClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(CommonTableExpression, funcName) +#define WCDBRustCommonTableExpressionClassMethod(funcName, ...) \ + WCDBRustClassMethod(CommonTableExpression, funcName, __VA_ARGS__) + +void* WCDBRustCommonTableExpressionClassMethod(createWithTable, const char* tableName); + +void WCDBRustCommonTableExpressionClassMethod(configColumn, void* self, void* column); + +void WCDBRustCommonTableExpressionClassMethod(configSelectStatement, void* self, void* select); diff --git a/src/rust/cpp/winq/identifier/ExpressionOperableRust.c b/src/rust/cpp/winq/identifier/ExpressionOperableRust.c new file mode 100644 index 000000000..aa2569c87 --- /dev/null +++ b/src/rust/cpp/winq/identifier/ExpressionOperableRust.c @@ -0,0 +1,131 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ExpressionOperableRust.h" + +#include "ExpressionOperatableBridge.h" + +#include + +void* WCDBRustExpressionOperableClassMethod(nullOperate, + int operandType, + void* operand, + bool isNot) { + CPPCommonValue operand_common; + operand_common.type = operandType; + operand_common.intValue = (long long)(uintptr_t)operand; + return (void*)WCDBExpressionNullOperate2(operand_common, isNot).innerValue; +} + +void* WCDBRustExpressionOperableClassMethod(binaryOperate, + int leftType, + void* left, + WCDBRustCommonValueParameter(right), + int operatorType, + bool isNot) { + CPPCommonValue left_common; + left_common.type = leftType; + left_common.intValue = (long long)left; + WCDBRustCreateCommonValue(right); + void* ret = (void*)WCDBExpressionBinaryOperate2(left_common, right_common, operatorType, isNot) + .innerValue; + return ret; +} + +void* WCDBRustExpressionOperableClassMethod(betweenOperate, + int operandType, + void* operand, + WCDBRustCommonValueParameter(left), + WCDBRustCommonValueParameter(right), + bool isNot) { + CPPCommonValue operand_common; + operand_common.type = operandType; + operand_common.intValue = (long long)operand; + WCDBRustCreateCommonValueWithIsCritical(left, false); + WCDBRustCreateCommonValueWithIsCritical(right, false); + void* ret = + (void*)WCDBExpressionBetweenOperate2(operand_common, left_common, right_common, isNot) + .innerValue; + // WCDBRustTryReleaseStringInCommonValue(left); + // WCDBRustTryReleaseStringInCommonValue(right); + return ret; +} + +void* WCDBRustExpressionOperableClassMethod(inOperate, + int operandType, + void* operand, + WCDBRustCommonArrayParameter(values), + bool isNot) { + CPPCommonValue operand_common; + operand_common.type = operandType; + operand_common.intValue = (long long)operand; + void* ret = 0; + WCDBRustCreateCommonArrayWithAction( + values, + ret = (void*)WCDBExpressionInOperate(operand_common, values_commonArray, isNot).innerValue); + return ret; +} + +void* WCDBRustExpressionOperableClassMethod(inTableOperate, + int operandType, + void* operand, + const char* table, + bool isNot) { + CPPCommonValue operand_common; + operand_common.type = operandType; + operand_common.intValue = (long long)(uintptr_t)operand; + void* ret = (void*)WCDBExpressionInTableOperate2(operand_common, table, isNot).innerValue; + return ret; +} + +// jlong WCDBRustExpressionOperableClassMethod( +// inFunctionOperate, jint operandType, jlong operand, jstring func, jboolean isNot) +//{ +// CPPCommonValue operand_common; +// operand_common.type = operandType; +// operand_common.intValue = operand; +// WCDBRustGetStringCritical(func); +// jlong ret = (jlong) WCDBExpressionInFunctionOperate2(operand_common, funcString, isNot) +// .innerValue; +// WCDBRustReleaseStringCritical(func); +// return ret; +//} +// +void* WCDBRustExpressionOperableClassMethod(inSelectionOperate, + int operandType, + void* operand, + void* select, + bool isNot) { + CPPCommonValue operand_common; + operand_common.type = operandType; + operand_common.intValue = (long long)operand; + WCDBRustBridgeStruct(CPPStatementSelect, select); + return WCDBExpressionInSelectionOperate2(operand_common, selectStruct, isNot).innerValue; +} + +void* WCDBRustExpressionOperableClassMethod(collateOperate, + int operandType, + void* operand, + const char* collation) { + CPPCommonValue operand_common; + operand_common.type = operandType; + operand_common.intValue = (long long)operand; + return (void*)WCDBExpressionCollateOperate2(operand_common, collation).innerValue; +} diff --git a/src/rust/cpp/winq/identifier/ExpressionOperableRust.h b/src/rust/cpp/winq/identifier/ExpressionOperableRust.h new file mode 100644 index 000000000..ac59d958c --- /dev/null +++ b/src/rust/cpp/winq/identifier/ExpressionOperableRust.h @@ -0,0 +1,76 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustExpressionOperableFuncName(funcName) WCDBRust(ExpressionOperable, funcName) +#define WCDBRustExpressionOperableObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(ExpressionOperable, funcName, __VA_ARGS__) +#define WCDBRustExpressionOperableClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(ExpressionOperable, funcName) +#define WCDBRustExpressionOperableClassMethod(funcName, ...) \ + WCDBRustClassMethod(ExpressionOperable, funcName, __VA_ARGS__) + +void* WCDBRustExpressionOperableClassMethod(nullOperate, + int operandType, + void* operand, + bool isNot); + +void* WCDBRustExpressionOperableClassMethod(binaryOperate, + int leftType, + void* left, + WCDBRustCommonValueParameter(right), + int operatorType, + bool isNot); + +void* WCDBRustExpressionOperableClassMethod(betweenOperate, + int operandType, + void* operand, + WCDBRustCommonValueParameter(left), + WCDBRustCommonValueParameter(right), + bool isNot); + +void* WCDBRustExpressionOperableClassMethod(inOperate, + int operandType, + void* operand, + WCDBRustCommonArrayParameter(values), + bool isNot); + +void* WCDBRustExpressionOperableClassMethod(inTableOperate, + int operandType, + void* operand, + const char* table, + bool isNot); +// +// jlong WCDBRustExpressionOperableClassMethod( +// inFunctionOperate, jint operandType, jlong operand, jstring func, jboolean isNot); +// +void* WCDBRustExpressionOperableClassMethod(inSelectionOperate, + int operandType, + void* operand, + void* select, + bool isNot); + +void* WCDBRustExpressionOperableClassMethod(collateOperate, + int operandType, + void* operand, + const char* collation); diff --git a/src/rust/cpp/winq/identifier/ExpressionRust.c b/src/rust/cpp/winq/identifier/ExpressionRust.c new file mode 100644 index 000000000..358e7e0aa --- /dev/null +++ b/src/rust/cpp/winq/identifier/ExpressionRust.c @@ -0,0 +1,180 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ExpressionRust.h" + +#include "ExpressionBridge.h" + +void* WCDBRustExpressionClassMethod(create, int type, void* object) { + CPPCommonValue commonValue; + commonValue.type = type; + commonValue.intValue = (long long)object; + return WCDBExpressionCreate(commonValue).innerValue; +} + +void* WCDBRustExpressionClassMethod(createWithFunction, const char* func) { + return WCDBExpressionCreateWithFunction(func).innerValue; +} + +// +// jlong WCDBRustExpressionClassMethod(createWithExistStatement, jlong select) +//{ +// WCDBRustBridgeStruct(CPPStatementSelect, select); +// return (jlong) WCDBExpressionCreateWithExistStatement(selectStruct).innerValue; +// } + +void* WCDBRustExpressionClassMethod(createWithExistStatement, void* select) { + WCDBRustBridgeStruct(CPPStatementSelect, select); + return WCDBExpressionCreateWithExistStatement(selectStruct).innerValue; +} + +void* WCDBRustExpressionClassMethod(createWithNotExistStatement, void* select) { + WCDBRustBridgeStruct(CPPStatementSelect, select); + return WCDBExpressionCreateWithNotExistStatement(selectStruct).innerValue; +} + +void WCDBRustExpressionClassMethod(setWithSchema, + void* expression, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBExpressionSetWithSchema2(expressionStruct, schema_common); + // WCDBRustTryReleaseStringInCommonValue(schema); +} + +void WCDBRustExpressionClassMethod(argument, + void* expression, + WCDBRustCommonValueParameter(argument)) { + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBRustCreateCommonValue(argument); + WCDBExpressionSetArgument(expressionStruct, argument_common); +} + +void WCDBRustExpressionClassMethod(invoke, void* expression) { + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBExpressionInvoke(expressionStruct); +} + +void WCDBRustExpressionClassMethod(invokeAll, void* expression) { + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBExpressionInvokeAll(expressionStruct); +} +void WCDBRustExpressionClassMethod(distinct, void* expression) { + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBExpressionDistinct(expressionStruct); +} + +void* WCDBRustExpressionClassMethod(cast, WCDBRustObjectOrStringParameter(expression)) { + WCDBRustCreateObjectOrStringCommonValue(expression, true); + void* ret = WCDBExpressionCast2(expression_common).innerValue; + // WCDBRustTryReleaseStringInCommonValue(expression); + return ret; +} + +void WCDBRustExpressionClassMethod(as, void* expression, int type) { + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBExpressionAs(expressionStruct, type); +} + +void* WCDBRustExpressionClassMethod(configAlias, void* expression, const char* alias) { + WCDBRustBridgeStruct(CPPExpression, expression); + void* ret = WCDBExpressionConfigAlias(expressionStruct, alias).innerValue; + return ret; +} + +void* WCDBRustExpressionClassMethod(caseWithExp, WCDBRustObjectOrStringParameter(expression)) { + if (expression_type == 0) { + return WCDBExpressionCase().innerValue; + } + WCDBRustCreateObjectOrStringCommonValue(expression, true); + void* ret = WCDBExpressionCaseWithExp2(expression_common).innerValue; + // WCDBRustTryReleaseStringInCommonValue(expression); + return ret; +} + +void* WCDBRustExpressionClassMethodWithNoArg(case_) { + return (void*)WCDBExpressionCase().innerValue; +} + +void WCDBRustExpressionClassMethod(setWithWhenExp, + void* expression, + WCDBRustCommonValueParameter(when)) { + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBRustCreateCommonValue(when); + WCDBExpressionSetWithWhenExp2(expressionStruct, when_common); + // WCDBRustTryReleaseStringInCommonValue(when); +} + +void WCDBRustExpressionClassMethod(setWithThenExp, + void* expression, + WCDBRustCommonValueParameter(then)) { + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBRustCreateCommonValue(then); + WCDBExpressionSetWithThenExp2(expressionStruct, then_common); + // WCDBRustTryReleaseStringInCommonValue(then); +} + +void WCDBRustExpressionClassMethod(setWithElseExp, + void* expression, + WCDBRustCommonValueParameter(else_)) { + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBRustCreateCommonValue(else_); + WCDBExpressionSetWithElseExp2(expressionStruct, else__common); + // WCDBRustTryReleaseStringInCommonValue(else_); +} + +void WCDBRustExpressionClassMethod(escapeWith, void* expression, const char* content) { + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBExpressionEscapeWith2(expressionStruct, content); +} + +// void WCDBRustExpressionClassMethod(escapeWith, jlong expression, jstring content) +//{ +// WCDBRustBridgeStruct(CPPExpression, expression); +// WCDBRustGetStringCritical(content); +// WCDBExpressionEscapeWith2(expressionStruct, contentString); +// WCDBRustReleaseStringCritical(content); +//} +// +void* WCDBRustExpressionClassMethod(createWithWindowFunction, const char* funcName) { + // WCDBRustGetStringCritical(funcName); + void* ret = (void*)WCDBExpressionCreateWithWindowFunction(funcName).innerValue; + // WCDBRustReleaseStringCritical(funcName); + return ret; +} + +void WCDBRustExpressionClassMethod(filter, void* expression, void* condition) { + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBRustBridgeStruct(CPPExpression, condition); + WCDBExpressionFilter(expressionStruct, conditionStruct); +} + +void WCDBRustExpressionClassMethod(overWindowDef, void* expression, void* def) { + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBRustBridgeStruct(CPPWindowDef, def); + WCDBExpressionOverWindowDef(expressionStruct, defStruct); +} + +void WCDBRustExpressionClassMethod(overWindow, void* expression, const char* windowString) { + WCDBRustBridgeStruct(CPPExpression, expression); + // WCDBRustGetStringCritical(window); + WCDBExpressionOverWindow(expressionStruct, windowString); + // WCDBRustReleaseStringCritical(window); +} diff --git a/src/rust/cpp/winq/identifier/ExpressionRust.h b/src/rust/cpp/winq/identifier/ExpressionRust.h new file mode 100644 index 000000000..760997893 --- /dev/null +++ b/src/rust/cpp/winq/identifier/ExpressionRust.h @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustExpressionFuncName(funcName) WCDBRust(Expression, funcName) +#define WCDBRustExpressionObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(Expression, funcName, __VA_ARGS__) +#define WCDBRustExpressionClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(Expression, funcName) +#define WCDBRustExpressionClassMethod(funcName, ...) \ + WCDBRustClassMethod(Expression, funcName, __VA_ARGS__) + +void* WCDBRustExpressionClassMethod(create, int type, void* object); + +void* WCDBRustExpressionClassMethod(createWithFunction, const char* func); +void* WCDBRustExpressionClassMethod(createWithExistStatement, void* select); +void* WCDBRustExpressionClassMethod(createWithNotExistStatement, void* select); + +void WCDBRustExpressionClassMethod(setWithSchema, + void* expression, + WCDBRustObjectOrStringParameter(schema)); +void WCDBRustExpressionClassMethod(argument, + void* expression, + WCDBRustCommonValueParameter(argument)); + +void WCDBRustExpressionClassMethod(invoke, void* expression); +void WCDBRustExpressionClassMethod(invokeAll, void* expression); + +void WCDBRustExpressionClassMethod(distinct, void* expression); + +void* WCDBRustExpressionClassMethod(cast, WCDBRustObjectOrStringParameter(expression)); +void WCDBRustExpressionClassMethod(as, void* expression, int type); + +void* WCDBRustExpressionClassMethod(configAlias, void* expression, const char* alias); + +void* WCDBRustExpressionClassMethod(caseWithExp, WCDBRustObjectOrStringParameter(expression)); +void WCDBRustExpressionClassMethod(setWithWhenExp, + void* expression, + WCDBRustCommonValueParameter(when)); +void WCDBRustExpressionClassMethod(setWithThenExp, + void* expression, + WCDBRustCommonValueParameter(then)); +void WCDBRustExpressionClassMethod(setWithElseExp, + void* expression, + WCDBRustCommonValueParameter(else_)); + +void WCDBRustExpressionClassMethod(escapeWith, void* expression, const char* content); + +void* WCDBRustExpressionClassMethod(createWithWindowFunction, const char* func); +void WCDBRustExpressionClassMethod(filter, void* expression, void* condition); +void WCDBRustExpressionClassMethod(overWindowDef, void* expression, void* def); +void WCDBRustExpressionClassMethod(overWindow, void* expression, const char* window); diff --git a/src/rust/cpp/winq/identifier/ForeignKeyRust.c b/src/rust/cpp/winq/identifier/ForeignKeyRust.c new file mode 100644 index 000000000..cb39d285a --- /dev/null +++ b/src/rust/cpp/winq/identifier/ForeignKeyRust.c @@ -0,0 +1,122 @@ +// Created by qiuwenchen on 2023/6/8. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ForeignKeyRust.h" + +#include "ForeignKeyBridge.h" + +void* WCDBRustForeignKeyClassMethodWithNoArg(createCppObject) { + return (void*)WCDBForeignKeyCreate().innerValue; +} + +void WCDBRustForeignKeyClassMethod(configReferencesTable, void* self, const char* table) { + // WCDBRustGetStringCritical(table); + WCDBRustBridgeStruct(CPPForeignKey, self); + WCDBForeignKeyConfigReferencesTable(selfStruct, table); + // WCDBRustReleaseStringCritical(table); +} + +void WCDBRustForeignKeyClassMethod(configColumns, + void* self, + WCDBRustObjectOrStringArrayParameter(column)) { + WCDBRustBridgeStruct(CPPForeignKey, self); + WCDBRustCreateObjectOrStringArrayCriticalWithAction( + column, WCDBForeignKeyAddColumn2(selfStruct, column_commonArray);) +} + +void WCDBRustForeignKeyClassMethod(configOnDeleteAction, void* self, int action) { + WCDBRustBridgeStruct(CPPForeignKey, self); + switch (action) { + case 0: + WCDBForeignKeyConfigOnDeleteSetNull(selfStruct); + break; + case 1: + WCDBForeignKeyConfigOnDeleteSetDefault(selfStruct); + break; + case 2: + WCDBForeignKeyConfigOnDeleteCascade(selfStruct); + break; + case 3: + WCDBForeignKeyConfigOnDeleteRestrict(selfStruct); + break; + case 4: + WCDBForeignKeyConfigOnDeleteNoAction(selfStruct); + break; + } +} + +void WCDBRustForeignKeyClassMethod(configOnUpdateAction, void* self, int action) { + WCDBRustBridgeStruct(CPPForeignKey, self); + switch (action) { + case 0: + WCDBForeignKeyConfigOnUpdateSetNull(selfStruct); + break; + case 1: + WCDBForeignKeyConfigOnUpdateSetDefault(selfStruct); + break; + case 2: + WCDBForeignKeyConfigOnUpdateCascade(selfStruct); + break; + case 3: + WCDBForeignKeyConfigOnUpdateRestrict(selfStruct); + break; + case 4: + WCDBForeignKeyConfigOnUpdateNoAction(selfStruct); + break; + } +} + +void WCDBRustForeignKeyClassMethod(configMatch, void* self, int match) { + WCDBRustBridgeStruct(CPPForeignKey, self); + WCDBForeignKeyConfigMatch(selfStruct, match); +} + +void WCDBRustForeignKeyClassMethod(configDeferrable, void* self, int type) { + WCDBRustBridgeStruct(CPPForeignKey, self); + switch (type) { + case 0: + WCDBForeignKeyConfigDeferrable(selfStruct); + break; + case 1: + WCDBForeignKeyConfigDeferrableInitiallyDeferred(selfStruct); + break; + case 2: + WCDBForeignKeyConfigDeferrableInitiallyImmediate(selfStruct); + break; + } +} + +void WCDBRustForeignKeyClassMethod(configNotDeferrable, void* self, int type) { + WCDBRustBridgeStruct(CPPForeignKey, self); + switch (type) { + case 0: + WCDBForeignKeyConfigNotDeferrable(selfStruct); + break; + case 1: + WCDBForeignKeyConfigNotDeferrableInitiallyDeferred(selfStruct); + break; + case 2: + WCDBForeignKeyConfigNotDeferrableInitiallyImmediate(selfStruct); + break; + } +} \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/ForeignKeyRust.h b/src/rust/cpp/winq/identifier/ForeignKeyRust.h new file mode 100644 index 000000000..338fdba3c --- /dev/null +++ b/src/rust/cpp/winq/identifier/ForeignKeyRust.h @@ -0,0 +1,50 @@ +// Created by qiuwenchen on 2023/6/8. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustForeignKeyFuncName(funcName) WCDBRust(ForeignKey, funcName) +#define WCDBRustForeignKeyObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(ForeignKey, funcName, __VA_ARGS__) +#define WCDBRustForeignKeyClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(ForeignKey, funcName) +#define WCDBRustForeignKeyClassMethod(funcName, ...) \ + WCDBRustClassMethod(ForeignKey, funcName, __VA_ARGS__) + +void* WCDBRustForeignKeyClassMethodWithNoArg(createCppObject); + +void WCDBRustForeignKeyClassMethod(configReferencesTable, void* self, const char* table); +void WCDBRustForeignKeyClassMethod(configColumns, + void* self, + WCDBRustObjectOrStringArrayParameter(column)); + +void WCDBRustForeignKeyClassMethod(configOnDeleteAction, void* self, int action); + +void WCDBRustForeignKeyClassMethod(configOnUpdateAction, void* self, int action); + +void WCDBRustForeignKeyClassMethod(configMatch, void* self, int match); + +void WCDBRustForeignKeyClassMethod(configDeferrable, void* self, int type); +void WCDBRustForeignKeyClassMethod(configNotDeferrable, void* self, int type); diff --git a/src/rust/cpp/winq/identifier/FrameSpecRust.c b/src/rust/cpp/winq/identifier/FrameSpecRust.c new file mode 100644 index 000000000..8fc87349c --- /dev/null +++ b/src/rust/cpp/winq/identifier/FrameSpecRust.c @@ -0,0 +1,107 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FrameSpecRust.h" + +#include "FrameSpecBridge.h" + +void* WCDBRustFrameSpecClassMethodWithNoArg(createCppObj) { + return (void*)WCDBFrameSpecCreate().innerValue; +} + +void WCDBRustFrameSpecClassMethod(configRange, void* self) { + WCDBRustBridgeStruct(CPPFrameSpec, self); + WCDBFrameSpecConfigRange(selfStruct); +} + +void WCDBRustFrameSpecClassMethod(configRows, void* self) { + WCDBRustBridgeStruct(CPPFrameSpec, self); + WCDBFrameSpecConfigRows(selfStruct); +} + +void WCDBRustFrameSpecClassMethod(configUnboundedPreceding, void* self) { + WCDBRustBridgeStruct(CPPFrameSpec, self); + WCDBFrameSpecConfigUnboundedPreceding(selfStruct); +} + +void WCDBRustFrameSpecClassMethod(configPreceding, + void* self, + WCDBRustObjectOrIntegerParameter(expression)) { + WCDBRustBridgeStruct(CPPFrameSpec, self); + WCDBRustCreateObjectOrIntegerCommonValue(expression); + WCDBFrameSpecConfigPreceding2(selfStruct, expression_common); +} + +void WCDBRustFrameSpecClassMethod(configCurrentRow, void* self) { + WCDBRustBridgeStruct(CPPFrameSpec, self); + WCDBFrameSpecConfigCurrentRow(selfStruct); +} + +void WCDBRustFrameSpecClassMethod(configBetweenUnboundedPreceding, void* self) { + WCDBRustBridgeStruct(CPPFrameSpec, self); + WCDBFrameSpecConfigBetweenUnboundedPreceding(selfStruct); +} + +void WCDBRustFrameSpecClassMethod(configBetweenPreceding, + void* self, + WCDBRustObjectOrIntegerParameter(expression)) { + WCDBRustBridgeStruct(CPPFrameSpec, self); + WCDBRustCreateObjectOrIntegerCommonValue(expression); + WCDBFrameSpecConfigBetweenPreceding2(selfStruct, expression_common); +} + +void WCDBRustFrameSpecClassMethod(configBetweenCurrentRow, void* self) { + WCDBRustBridgeStruct(CPPFrameSpec, self); + WCDBFrameSpecConfigBetweenCurrentRow(selfStruct); +} + +void WCDBRustFrameSpecClassMethod(configBetweenFollowing, + void* self, + WCDBRustObjectOrIntegerParameter(expression)) { + WCDBRustBridgeStruct(CPPFrameSpec, self); + WCDBRustCreateObjectOrIntegerCommonValue(expression); + WCDBFrameSpecConfigBetweenFollowing2(selfStruct, expression_common); +} + +void WCDBRustFrameSpecClassMethod(configAndPreceding, + void* self, + WCDBRustObjectOrIntegerParameter(expression)) { + WCDBRustBridgeStruct(CPPFrameSpec, self); + WCDBRustCreateObjectOrIntegerCommonValue(expression); + WCDBFrameSpecConfigAndPreceding2(selfStruct, expression_common); +} + +void WCDBRustFrameSpecClassMethod(configAndCurrentRow, void* self) { + WCDBRustBridgeStruct(CPPFrameSpec, self); + WCDBFrameSpecConfigAndCurrentRow(selfStruct); +} + +void WCDBRustFrameSpecClassMethod(configAndFollowing, + void* self, + WCDBRustObjectOrIntegerParameter(expression)) { + WCDBRustBridgeStruct(CPPFrameSpec, self); + WCDBRustCreateObjectOrIntegerCommonValue(expression); + WCDBFrameSpecConfigAndFollowing2(selfStruct, expression_common); +} + +void WCDBRustFrameSpecClassMethod(configAndUnboundedFollowing, void* self) { + WCDBRustBridgeStruct(CPPFrameSpec, self); + WCDBFrameSpecConfigAndUnboundedFollowing(selfStruct); +} diff --git a/src/rust/cpp/winq/identifier/FrameSpecRust.h b/src/rust/cpp/winq/identifier/FrameSpecRust.h new file mode 100644 index 000000000..fbd4ac583 --- /dev/null +++ b/src/rust/cpp/winq/identifier/FrameSpecRust.h @@ -0,0 +1,56 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustFrameSpecFuncName(funcName) WCDBRust(FrameSpec, funcName) +#define WCDBRustFrameSpecObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(FrameSpec, funcName, __VA_ARGS__) +#define WCDBRustFrameSpecClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(FrameSpec, funcName) +#define WCDBRustFrameSpecClassMethod(funcName, ...) \ + WCDBRustClassMethod(FrameSpec, funcName, __VA_ARGS__) + +void* WCDBRustFrameSpecClassMethodWithNoArg(createCppObj); +void WCDBRustFrameSpecClassMethod(configRange, void* self); +void WCDBRustFrameSpecClassMethod(configRows, void* self); +void WCDBRustFrameSpecClassMethod(configUnboundedPreceding, void* self); +void WCDBRustFrameSpecClassMethod(configPreceding, + void* self, + WCDBRustObjectOrIntegerParameter(expression)); +void WCDBRustFrameSpecClassMethod(configCurrentRow, void* self); +void WCDBRustFrameSpecClassMethod(configBetweenUnboundedPreceding, void* self); +void WCDBRustFrameSpecClassMethod(configBetweenPreceding, + void* self, + WCDBRustObjectOrIntegerParameter(expression)); +void WCDBRustFrameSpecClassMethod(configBetweenCurrentRow, void* self); +void WCDBRustFrameSpecClassMethod(configBetweenFollowing, + void* self, + WCDBRustObjectOrIntegerParameter(expression)); +void WCDBRustFrameSpecClassMethod(configAndPreceding, + void* self, + WCDBRustObjectOrIntegerParameter(expression)); +void WCDBRustFrameSpecClassMethod(configAndCurrentRow, void* self); +void WCDBRustFrameSpecClassMethod(configAndFollowing, + void* self, + WCDBRustObjectOrIntegerParameter(expression)); +void WCDBRustFrameSpecClassMethod(configAndUnboundedFollowing, void* self); diff --git a/src/rust/cpp/winq/identifier/IndexedColumnRust.c b/src/rust/cpp/winq/identifier/IndexedColumnRust.c new file mode 100644 index 000000000..6fbda21a4 --- /dev/null +++ b/src/rust/cpp/winq/identifier/IndexedColumnRust.c @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IndexedColumnRust.h" + +#include "IndexedColumnBridge.h" + +void* WCDBRustIndexedColumnClassMethod(create, WCDBRustObjectOrStringParameter(column)) { + WCDBRustCreateObjectOrStringCommonValue(column, true); + void* ret = (void*)WCDBIndexedColumnCreate(column_common).innerValue; + return ret; +} + +void WCDBRustIndexedColumnClassMethod(configCollation, void* indexedColumn, const char* collation) { + WCDBRustBridgeStruct(CPPIndexedColumn, indexedColumn); + WCDBIndexedColumnConfigCollation(indexedColumnStruct, collation); +} + +void WCDBRustIndexedColumnClassMethod(configOrder, void* indexedColumn, int order) { + WCDBRustBridgeStruct(CPPIndexedColumn, indexedColumn); + WCDBIndexedColumnConfigOrder(indexedColumnStruct, order); +} \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/IndexedColumnRust.h b/src/rust/cpp/winq/identifier/IndexedColumnRust.h new file mode 100644 index 000000000..039015b5c --- /dev/null +++ b/src/rust/cpp/winq/identifier/IndexedColumnRust.h @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBJNIIndexedColumnFuncName(funcName) WCDBRust(IndexedColumn, funcName) +#define WCDBRustIndexedColumnObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(IndexedColumn, funcName, __VA_ARGS__) +#define WCDBRustIndexedColumnClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(IndexedColumn, funcName) +#define WCDBRustIndexedColumnClassMethod(funcName, ...) \ + WCDBRustClassMethod(IndexedColumn, funcName, __VA_ARGS__) + +void* WCDBRustIndexedColumnClassMethod(create, WCDBRustObjectOrStringParameter(column)); + +void WCDBRustIndexedColumnClassMethod(configCollation, void* indexedColumn, const char* collation); + +void WCDBRustIndexedColumnClassMethod(configOrder, void* indexedColumn, int order); diff --git a/src/rust/cpp/winq/identifier/JoinRust.c b/src/rust/cpp/winq/identifier/JoinRust.c new file mode 100644 index 000000000..d33db7ce0 --- /dev/null +++ b/src/rust/cpp/winq/identifier/JoinRust.c @@ -0,0 +1,127 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JoinRust.h" + +#include "JoinBridge.h" + +void* WCDBRustJoinClassMethod(createCppObj, WCDBRustObjectOrStringParameter(query)) { + WCDBRustCreateObjectOrStringCommonValue(query, true); + void* ret = (void*)WCDBJoinCreateWithTableOrSubquery2(query_common).innerValue; + return ret; +} + +void WCDBRustJoinClassMethod(configWith, void* join, WCDBRustObjectOrStringParameter(query)) { + WCDBRustBridgeStruct(CPPJoin, join); + WCDBRustCreateObjectOrStringCommonValue(query, true); + WCDBJoinWith2(joinStruct, query_common); +} + +void WCDBRustJoinClassMethod(configWithJoin, void* join, WCDBRustObjectOrStringParameter(query)) { + WCDBRustBridgeStruct(CPPJoin, join); + WCDBRustCreateObjectOrStringCommonValue(query, true); + WCDBJoinWithJoin2(joinStruct, query_common); +} + +void WCDBRustJoinClassMethod(configWithLeftOuterJoin, + void* join, + WCDBRustObjectOrStringParameter(query)) { + WCDBRustBridgeStruct(CPPJoin, join); + WCDBRustCreateObjectOrStringCommonValue(query, true); + WCDBJoinWithLeftOuterJoin2(joinStruct, query_common); +} + +void WCDBRustJoinClassMethod(configWithLeftJoin, + void* join, + WCDBRustObjectOrStringParameter(query)) { + WCDBRustBridgeStruct(CPPJoin, join); + WCDBRustCreateObjectOrStringCommonValue(query, true); + WCDBJoinWithLeftJoin2(joinStruct, query_common); +} + +void WCDBRustJoinClassMethod(configWithInnerJoin, + void* join, + WCDBRustObjectOrStringParameter(query)) { + WCDBRustBridgeStruct(CPPJoin, join); + WCDBRustCreateObjectOrStringCommonValue(query, true); + WCDBJoinWithInnerJoin2(joinStruct, query_common); +} + +void WCDBRustJoinClassMethod(configWithCrossJoin, + void* join, + WCDBRustObjectOrStringParameter(query)) { + WCDBRustBridgeStruct(CPPJoin, join); + WCDBRustCreateObjectOrStringCommonValue(query, true); + WCDBJoinWithCrossJoin2(joinStruct, query_common); +} + +void WCDBRustJoinClassMethod(configWithNaturalJoin, + void* join, + WCDBRustObjectOrStringParameter(query)) { + WCDBRustBridgeStruct(CPPJoin, join); + WCDBRustCreateObjectOrStringCommonValue(query, true); + WCDBJoinWithNaturalJoin2(joinStruct, query_common); +} + +void WCDBRustJoinClassMethod(configWithNaturalLeftOuterJoin, + void* join, + WCDBRustObjectOrStringParameter(query)) { + WCDBRustBridgeStruct(CPPJoin, join); + WCDBRustCreateObjectOrStringCommonValue(query, true); + WCDBJoinWithNaturalLeftOuterJoin2(joinStruct, query_common); +} + +void WCDBRustJoinClassMethod(configWithNaturalLeftJoin, + void* join, + WCDBRustObjectOrStringParameter(query)) { + WCDBRustBridgeStruct(CPPJoin, join); + WCDBRustCreateObjectOrStringCommonValue(query, true); + WCDBJoinWithNaturalLeftJoin2(joinStruct, query_common); +} + +void WCDBRustJoinClassMethod(configWithNaturalInnerJoin, + void* join, + WCDBRustObjectOrStringParameter(query)) { + WCDBRustBridgeStruct(CPPJoin, join); + WCDBRustCreateObjectOrStringCommonValue(query, true); + WCDBJoinWithNaturalInnerJoin2(joinStruct, query_common); +} + +void WCDBRustJoinClassMethod(configWithNaturalCrossJoin, + void* join, + WCDBRustObjectOrStringParameter(query)) { + WCDBRustBridgeStruct(CPPJoin, join); + WCDBRustCreateObjectOrStringCommonValue(query, true); + WCDBJoinWithNaturalCrossJoin2(joinStruct, query_common); +} + +void WCDBRustJoinClassMethod(configOn, void* join, void* expression) { + WCDBRustBridgeStruct(CPPJoin, join); + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBJoinConfigOn(joinStruct, expressionStruct); +} + +void WCDBRustJoinClassMethod(configUsingColumn, + void* join, + WCDBRustObjectOrStringArrayParameter(columns)) { + WCDBRustBridgeStruct(CPPJoin, join); + WCDBRustCreateObjectOrStringArrayCriticalWithAction( + columns, WCDBJoinConfigUsingColumn2(joinStruct, columns_commonArray)); +} \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/JoinRust.h b/src/rust/cpp/winq/identifier/JoinRust.h new file mode 100644 index 000000000..b0e69be9e --- /dev/null +++ b/src/rust/cpp/winq/identifier/JoinRust.h @@ -0,0 +1,63 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustJoinFuncName(funcName) WCDBRust(Join, funcName) +#define WCDBRustJoinObjectMethod(funcName, ...) WCDBRustObjectMethod(Join, funcName, __VA_ARGS__) +#define WCDBRustJoinClassMethodWithNoArg(funcName) WCDBRustClassMethodWithNoArg(Join, funcName) +#define WCDBRustJoinClassMethod(funcName, ...) WCDBRustClassMethod(Join, funcName, __VA_ARGS__) + +void* WCDBRustJoinClassMethod(createCppObj, WCDBRustObjectOrStringParameter(query)); +void WCDBRustJoinClassMethod(configWith, void* join, WCDBRustObjectOrStringParameter(query)); +void WCDBRustJoinClassMethod(configWithJoin, void* join, WCDBRustObjectOrStringParameter(query)); +void WCDBRustJoinClassMethod(configWithLeftOuterJoin, + void* join, + WCDBRustObjectOrStringParameter(query)); +void WCDBRustJoinClassMethod(configWithLeftJoin, + void* join, + WCDBRustObjectOrStringParameter(query)); +void WCDBRustJoinClassMethod(configWithInnerJoin, + void* join, + WCDBRustObjectOrStringParameter(query)); +void WCDBRustJoinClassMethod(configWithCrossJoin, + void* join, + WCDBRustObjectOrStringParameter(query)); +void WCDBRustJoinClassMethod(configWithNaturalJoin, + void* join, + WCDBRustObjectOrStringParameter(query)); +void WCDBRustJoinClassMethod(configWithNaturalLeftOuterJoin, + void* join, + WCDBRustObjectOrStringParameter(query)); +void WCDBRustJoinClassMethod(configWithNaturalLeftJoin, + void* join, + WCDBRustObjectOrStringParameter(query)); +void WCDBRustJoinClassMethod(configWithNaturalInnerJoin, + void* join, + WCDBRustObjectOrStringParameter(query)); +void WCDBRustJoinClassMethod(configWithNaturalCrossJoin, + void* join, + WCDBRustObjectOrStringParameter(query)); +void WCDBRustJoinClassMethod(configOn, void* join, void* expression); +void WCDBRustJoinClassMethod(configUsingColumn, + void* join, + WCDBRustObjectOrStringArrayParameter(columns)); \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/LiteralValueRust.c b/src/rust/cpp/winq/identifier/LiteralValueRust.c new file mode 100644 index 000000000..657b09477 --- /dev/null +++ b/src/rust/cpp/winq/identifier/LiteralValueRust.c @@ -0,0 +1,61 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "LiteralValueRust.h" + +#include "LiteralValueBridge.h" + +void* WCDBRustLiteralValueClassMethod(create, WCDBRustCommonValueParameter(value)) { + WCDBRustCreateCommonValue(value) return (void*)WCDBLiteralValueCreate(value_common).innerValue; +} + +long long WCDBRustLiteralValueClassMethod(createWithInt64, long long value) { + return (long long)WCDBLiteralValueCreateWithInt64(value).innerValue; +} + +// jlong WCDBRustLiteralValueClassMethod(createWithBool, jboolean value) +//{ +// return (jlong) WCDBLiteralValueCreateWithBool(value).innerValue; +// } +// +// jlong WCDBRustLiteralValueClassMethod(createWithDouble, jdouble value) +//{ +// return (jlong) WCDBLiteralValueCreateWithDouble(value).innerValue; +// } +// +// jlong WCDBRustLiteralValueClassMethod(createWithString, jstring value) +//{ +// WCDBRustGetStringCritical(value); +// jlong result = (jlong) WCDBLiteralValueCreateWithString(valueString).innerValue; +// WCDBRustReleaseStringCritical(value); +// return result; +// } + +long long WCDBRustLiteralValueClassMethodWithNoArg(createWithCurrentTime) { + return (long long)WCDBLiteralValueCreateWithCurrentTime().innerValue; +} + +long long WCDBRustLiteralValueClassMethodWithNoArg(createWithCurrentDate) { + return (long long)WCDBLiteralValueCreateWithCurrentDate().innerValue; +} + +long long WCDBRustLiteralValueClassMethodWithNoArg(createWithCurrentTimestamp) { + return (long long)WCDBLiteralValueCreateWithCurrentTimestamp().innerValue; +} diff --git a/src/rust/cpp/winq/identifier/LiteralValueRust.h b/src/rust/cpp/winq/identifier/LiteralValueRust.h new file mode 100644 index 000000000..64021251f --- /dev/null +++ b/src/rust/cpp/winq/identifier/LiteralValueRust.h @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustLiteralValueFuncName(funcName) WCDBRust(LiteralValue, funcName) +#define WCDBRustLiteralValueObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(LiteralValue, funcName, __VA_ARGS__) +#define WCDBRustLiteralValueClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(LiteralValue, funcName) +#define WCDBRustLiteralValueClassMethod(funcName, ...) \ + WCDBRustClassMethod(LiteralValue, funcName, __VA_ARGS__) + +void* WCDBRustLiteralValueClassMethod(create, WCDBRustCommonValueParameter(value)); + +long long WCDBRustLiteralValueClassMethodWithNoArg(createWithCurrentTime); + +long long WCDBRustLiteralValueClassMethodWithNoArg(createWithCurrentDate); + +long long WCDBRustLiteralValueClassMethodWithNoArg(createWithCurrentTimestamp); diff --git a/src/rust/cpp/winq/identifier/OrderingTermRust.c b/src/rust/cpp/winq/identifier/OrderingTermRust.c new file mode 100644 index 000000000..38fd29934 --- /dev/null +++ b/src/rust/cpp/winq/identifier/OrderingTermRust.c @@ -0,0 +1,51 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "OrderingTermRust.h" + +#include "OrderingTermBridge.h" + +void* WCDBRustOrderingTermClassMethod(create, int type, void* expression) { + CPPCommonValue common_expression; + common_expression.type = type; + common_expression.intValue = (long long)expression; + return (void*)WCDBOrderingTermCreate2(common_expression).innerValue; +} + +// jlong WCDBRustOrderingTermClassMethod(create, jint type, jlong expression) +//{ +// CPPCommonValue common_expression; +// common_expression.type = type; +// common_expression.intValue = expression; +// return (jlong) WCDBOrderingTermCreate2(common_expression).innerValue; +// } +// +// void WCDBRustOrderingTermClassMethod(configCollation, jlong object, jstring collation) +//{ +// WCDBRustBridgeStruct(CPPOrderingTerm, object); +// WCDBRustGetStringCritical(collation); +// WCDBOrderingTermConfigCollation(objectStruct, collationString); +// WCDBRustReleaseStringCritical(collation); +// } + +void WCDBRustOrderingTermClassMethod(configOrder, void* object, int order) { + WCDBRustBridgeStruct(CPPOrderingTerm, object); + WCDBOrderingTermConfigOrder(objectStruct, order); +} \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/OrderingTermRust.h b/src/rust/cpp/winq/identifier/OrderingTermRust.h new file mode 100644 index 000000000..23d80d849 --- /dev/null +++ b/src/rust/cpp/winq/identifier/OrderingTermRust.h @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include "WCDBRust.h" + +#define WCDBRustOrderingTermFuncName(funcName) WCDBRust(OrderingTerm, funcName) +#define WCDBRustOrderingTermObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(OrderingTerm, funcName, __VA_ARGS__) +#define WCDBRustOrderingTermClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(OrderingTerm, funcName) +#define WCDBRustOrderingTermClassMethod(funcName, ...) \ + WCDBRustClassMethod(OrderingTerm, funcName, __VA_ARGS__) + +void* WCDBRustOrderingTermClassMethod(create, int type, void* expression); +// +// void WCDBRustOrderingTermClassMethod(configCollation, jlong object, jstring collation); + +void WCDBRustOrderingTermClassMethod(configOrder, void* object, int order); diff --git a/src/rust/cpp/winq/identifier/PragmaRust.c b/src/rust/cpp/winq/identifier/PragmaRust.c new file mode 100644 index 000000000..fb0a5bce7 --- /dev/null +++ b/src/rust/cpp/winq/identifier/PragmaRust.c @@ -0,0 +1,30 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PragmaRust.h" + +#include "PragmaBridge.h" + +void* WCDBRustPragmaClassMethod(create, const char* name) { + // WCDBRustGetStringCritical(name); + void* ret = (void*)WCDBPragmaCreateWithName(name).innerValue; + // WCDBRustReleaseStringCritical(name); + return ret; +} diff --git a/src/rust/cpp/winq/identifier/PragmaRust.h b/src/rust/cpp/winq/identifier/PragmaRust.h new file mode 100644 index 000000000..e3fc71119 --- /dev/null +++ b/src/rust/cpp/winq/identifier/PragmaRust.h @@ -0,0 +1,31 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustPragmaFuncName(funcName) WCDBRust(Pragma, funcName) +#define WCDBRustPragmaObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(Pragma, funcName, __VA_ARGS__) +#define WCDBRustPragmaClassMethodWithNoArg(funcName) WCDBRustClassMethodWithNoArg(Pragma, funcName) +#define WCDBRustPragmaClassMethod(funcName, ...) WCDBRustClassMethod(Pragma, funcName, __VA_ARGS__) + +void* WCDBRustPragmaClassMethod(create, const char* name); diff --git a/src/rust/cpp/winq/identifier/QualifiedTableRust.c b/src/rust/cpp/winq/identifier/QualifiedTableRust.c new file mode 100644 index 000000000..b0fc44f21 --- /dev/null +++ b/src/rust/cpp/winq/identifier/QualifiedTableRust.c @@ -0,0 +1,51 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QualifiedTableRust.h" + +#include "QualifiedTableBridge.h" + +void* WCDBRustQualifiedTableClassMethod(create, const char* tableName) { + void* ret = (void*)WCDBQualifiedTableCreateWithTable(tableName).innerValue; + return ret; +} + +void WCDBRustQualifiedTableClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPQualifiedTable, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBQualifiedTableConfigSchema2(selfStruct, schema_common); +} + +void WCDBRustQualifiedTableClassMethod(configAlias, void* self, const char* aliasString) { + WCDBRustBridgeStruct(CPPQualifiedTable, self); + WCDBQualifiedTableConfigAliasName(selfStruct, aliasString); +} + +void WCDBRustQualifiedTableClassMethod(configIndex, void* self, const char* indexString) { + WCDBRustBridgeStruct(CPPQualifiedTable, self); + WCDBQualifiedTableConfigIndexName(selfStruct, indexString); +} + +void WCDBRustQualifiedTableClassMethod(configNotIndexed, void* self) { + WCDBRustBridgeStruct(CPPQualifiedTable, self); + WCDBQualifiedTableConfigNoIndexed(selfStruct); +} \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/QualifiedTableRust.h b/src/rust/cpp/winq/identifier/QualifiedTableRust.h new file mode 100644 index 000000000..c2f3fa414 --- /dev/null +++ b/src/rust/cpp/winq/identifier/QualifiedTableRust.h @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustQualifiedTableFuncName(funcName) WCDBRust(QualifiedTable, funcName) +#define WCDBRustQualifiedTableObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(QualifiedTable, funcName, __VA_ARGS__) +#define WCDBRustQualifiedTableClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(QualifiedTable, funcName) +#define WCDBRustQualifiedTableClassMethod(funcName, ...) \ + WCDBRustClassMethod(QualifiedTable, funcName, __VA_ARGS__) + +void* WCDBRustQualifiedTableClassMethod(create, const char* tableName); +void WCDBRustQualifiedTableClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); +void WCDBRustQualifiedTableClassMethod(configAlias, void* self, const char* alias); +void WCDBRustQualifiedTableClassMethod(configIndex, void* self, const char* index); +void WCDBRustQualifiedTableClassMethod(configNotIndexed, void* self); diff --git a/src/rust/cpp/winq/identifier/ResultColumnRust.c b/src/rust/cpp/winq/identifier/ResultColumnRust.c new file mode 100644 index 000000000..74f3068a0 --- /dev/null +++ b/src/rust/cpp/winq/identifier/ResultColumnRust.c @@ -0,0 +1,34 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ResultColumnRust.h" + +#include "ResultColumnBridge.h" + +void* WCDBRustResultColumnClassMethod(create, WCDBRustObjectOrStringParameter(column)) { + WCDBRustCreateObjectOrStringCommonValue(column, true); + void* ret = (void*)WCDBResultColumnCreate(column_common).innerValue; + return ret; +} + +void WCDBRustResultColumnClassMethod(configAlias, void* object, const char* alias) { + WCDBRustBridgeStruct(CPPResultColumn, object); + WCDBResultColumnConfigAlias(objectStruct, alias); +} diff --git a/src/rust/cpp/winq/identifier/ResultColumnRust.h b/src/rust/cpp/winq/identifier/ResultColumnRust.h new file mode 100644 index 000000000..58bf302c2 --- /dev/null +++ b/src/rust/cpp/winq/identifier/ResultColumnRust.h @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustResultColumnFuncName(funcName) WCDBRust(ResultColumn, funcName) +#define WCDBRustResultColumnObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(ResultColumn, funcName, __VA_ARGS__) +#define WCDBRustResultColumnClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(ResultColumn, funcName) +#define WCDBRustResultColumnClassMethod(funcName, ...) \ + WCDBRustClassMethod(ResultColumn, funcName, __VA_ARGS__) + +void* WCDBRustResultColumnClassMethod(create, WCDBRustObjectOrStringParameter(column)); + +void WCDBRustResultColumnClassMethod(configAlias, void* object, const char* alias); \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/SchemaRust.c b/src/rust/cpp/winq/identifier/SchemaRust.c new file mode 100644 index 000000000..b5cb1d739 --- /dev/null +++ b/src/rust/cpp/winq/identifier/SchemaRust.c @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SchemaRust.h" + +#include "SchemaBridge.h" + +void* WCDBRustSchemaClassMethod(createWithName, const char* nameString) { + void* ret = WCDBSchemaCreateWithName(nameString).innerValue; + return ret; +} + +void* WCDBRustSchemaClassMethodWithNoArg(main) { + return WCDBSchemaMain().innerValue; +} + +void* WCDBRustSchemaClassMethodWithNoArg(temp) { + return WCDBSchemaTemp().innerValue; +} \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/SchemaRust.h b/src/rust/cpp/winq/identifier/SchemaRust.h new file mode 100644 index 000000000..a61d8062b --- /dev/null +++ b/src/rust/cpp/winq/identifier/SchemaRust.h @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustSchemaFuncName(funcName) WCDBRust(Schema, funcName) +#define WCDBRustSchemaObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(Schema, funcName, __VA_ARGS__) +#define WCDBRustSchemaClassMethodWithNoArg(funcName) WCDBRustClassMethodWithNoArg(Schema, funcName) +#define WCDBRustSchemaClassMethod(funcName, ...) WCDBRustClassMethod(Schema, funcName, __VA_ARGS__) + +void* WCDBRustSchemaClassMethod(createWithName, const char* name); + +void* WCDBRustSchemaClassMethodWithNoArg(main); + +void* WCDBRustSchemaClassMethodWithNoArg(temp); \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/TableConstraintRust.c b/src/rust/cpp/winq/identifier/TableConstraintRust.c new file mode 100644 index 000000000..cf03b9ef6 --- /dev/null +++ b/src/rust/cpp/winq/identifier/TableConstraintRust.c @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TableConstraintRust.h" + +#include "TableConstraintBridge.h" + +void* WCDBRustTableConstraintClassMethod(create, const char* name) { + void* ret = (void*)WCDBTableConstraintCreate(name).innerValue; + return ret; +} + +void WCDBRustTableConstraintClassMethod(configPrimaryKey, void* constraint) { + WCDBRustBridgeStruct(CPPTableConstraint, constraint); + WCDBTableConstraintConfigPrimaryKey(constraintStruct); +} + +void WCDBRustTableConstraintClassMethod(configUnique, void* constraint) { + WCDBRustBridgeStruct(CPPTableConstraint, constraint); + WCDBTableConstraintConfigUnique(constraintStruct); +} + +void WCDBRustTableConstraintClassMethod(configIndexedColumn, + void* constraint, + WCDBRustObjectOrStringArrayParameter(indexedColumns)) { + WCDBRustBridgeStruct(CPPTableConstraint, constraint); + WCDBRustCreateObjectOrStringArrayCriticalWithAction( + indexedColumns, + WCDBTableConstraintConfigIndexedColumn2(constraintStruct, indexedColumns_commonArray)); +} + +// void WCDBRustTableConstraintClassMethod(configConfliction, jlong constraint, int conflictAction) +//{ +// WCDBRustBridgeStruct(CPPTableConstraint, constraint); +// WCDBTableConstraintConfigConfliction(constraintStruct, conflictAction); +// } +// +// void WCDBRustTableConstraintClassMethod(configCheckCondition, jlong constraint, jlong expression) +//{ +// WCDBRustBridgeStruct(CPPTableConstraint, constraint); +// WCDBRustBridgeStruct(CPPExpression, expression); +// WCDBTableConstraintConfigCheckCondition(constraintStruct, expressionStruct); +// } +// +// void WCDBRustTableConstraintClassMethod(configForeignKey, +// jlong constraint, +// WCDBRustObjectOrStringArrayParameter(columns), +// jlong foreignKey) +//{ +// WCDBRustBridgeStruct(CPPTableConstraint, constraint); +// WCDBRustBridgeStruct(CPPForeignKey, foreignKey); +// WCDBRustCreateObjectOrStringArrayCriticalWithAction( +// columns, WCDBTableConstraintConfigForeignKey2(constraintStruct, columns_commonArray, +// foreignKeyStruct)); +// } diff --git a/src/rust/cpp/winq/identifier/TableConstraintRust.h b/src/rust/cpp/winq/identifier/TableConstraintRust.h new file mode 100644 index 000000000..8afd31a1b --- /dev/null +++ b/src/rust/cpp/winq/identifier/TableConstraintRust.h @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustTableConstraintFuncName(funcName) WCDBRust(TableConstraint, funcName) +#define WCDBRustTableConstraintObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(TableConstraint, funcName, __VA_ARGS__) +#define WCDBRustTableConstraintClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(TableConstraint, funcName) +#define WCDBRustTableConstraintClassMethod(funcName, ...) \ + WCDBRustClassMethod(TableConstraint, funcName, __VA_ARGS__) + +void* WCDBRustTableConstraintClassMethod(create, const char* name); +void WCDBRustTableConstraintClassMethod(configPrimaryKey, void* constraint); +void WCDBRustTableConstraintClassMethod(configUnique, void* constraint); +void WCDBRustTableConstraintClassMethod(configIndexedColumn, + void* constraint, + WCDBRustObjectOrStringArrayParameter(indexedColumns)); +// void WCDBRustTableConstraintClassMethod(configConfliction, jlong constraint, int conflictAction); +// void WCDBRustTableConstraintClassMethod(configCheckCondition, jlong constraint, jlong +// expression); void WCDBRustTableConstraintClassMethod(configForeignKey, +// jlong constraint, +// WCDBRustObjectOrStringArrayParameter(columns), +// jlong foreignKey); diff --git a/src/rust/cpp/winq/identifier/UpsertRust.c b/src/rust/cpp/winq/identifier/UpsertRust.c new file mode 100644 index 000000000..d383ffaf9 --- /dev/null +++ b/src/rust/cpp/winq/identifier/UpsertRust.c @@ -0,0 +1,69 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "UpsertRust.h" + +#include "UpsertBridge.h" + +#include + +void* WCDBRustUpsertClassMethodWithNoArg(createCppObj) { + return (void*)WCDBUpsertCreate().innerValue; +} + +void WCDBRustUpsertClassMethod(configIndexedColumn, + void* upsert, + WCDBRustObjectOrStringArrayParameter(columns)) { + WCDBRustBridgeStruct(CPPUpsert, upsert); + WCDBRustCreateObjectOrStringArrayCriticalWithAction( + columns, WCDBUpsertConfigIndexdColumn2(upsertStruct, columns_commonArray)); +} + +void WCDBRustUpsertClassMethod(configWhere, void* upsert, void* expression) { + WCDBRustBridgeStruct(CPPUpsert, upsert); + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBUpsertConfigWhere(upsertStruct, expressionStruct); +} + +void WCDBRustUpsertClassMethod(configDoNothing, void* upsert) { + WCDBRustBridgeStruct(CPPUpsert, upsert); + WCDBUpsertConfigDoNothing(upsertStruct); +} + +void WCDBRustUpsertClassMethod(configDoUpdate, void* upsert) { + WCDBRustBridgeStruct(CPPUpsert, upsert); + WCDBUpsertConfigDoUpdate(upsertStruct); +} + +void WCDBRustUpsertClassMethod(configSetColumns, + void* upsert, + WCDBRustObjectOrStringArrayParameter(columns)) { + WCDBRustBridgeStruct(CPPUpsert, upsert); + WCDBRustCreateObjectOrStringArrayCriticalWithAction( + columns, WCDBUpsertConfigSetColumns2(upsertStruct, columns_commonArray)); +} + +void WCDBRustUpsertClassMethod(configToValue, + void* upsert, + WCDBRustCommonValueParameter(expression)) { + WCDBRustBridgeStruct(CPPUpsert, upsert); + WCDBRustCreateCommonValueWithIsCritical(expression, true); + WCDBUpsertConfigToValue2(upsertStruct, expression_common); +} \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/UpsertRust.h b/src/rust/cpp/winq/identifier/UpsertRust.h new file mode 100644 index 000000000..0e08fa34f --- /dev/null +++ b/src/rust/cpp/winq/identifier/UpsertRust.h @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustUpsertFuncName(funcName) WCDBRust(Upsert, funcName) +#define WCDBRustUpsertObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(Upsert, funcName, __VA_ARGS__) +#define WCDBRustUpsertClassMethodWithNoArg(funcName) WCDBRustClassMethodWithNoArg(Upsert, funcName) +#define WCDBRustUpsertClassMethod(funcName, ...) WCDBRustClassMethod(Upsert, funcName, __VA_ARGS__) + +void* WCDBRustUpsertClassMethodWithNoArg(createCppObj); +void WCDBRustUpsertClassMethod(configIndexedColumn, + void* upsert, + WCDBRustObjectOrStringArrayParameter(columns)); +void WCDBRustUpsertClassMethod(configWhere, void* upsert, void* expression); +void WCDBRustUpsertClassMethod(configDoNothing, void* upsert); +void WCDBRustUpsertClassMethod(configDoUpdate, void* upsert); +void WCDBRustUpsertClassMethod(configSetColumns, + void* upsert, + WCDBRustObjectOrStringArrayParameter(columns)); +void WCDBRustUpsertClassMethod(configToValue, + void* upsert, + WCDBRustCommonValueParameter(expression)); diff --git a/src/rust/cpp/winq/identifier/WindowDefRust.c b/src/rust/cpp/winq/identifier/WindowDefRust.c new file mode 100644 index 000000000..d64a889e3 --- /dev/null +++ b/src/rust/cpp/winq/identifier/WindowDefRust.c @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WindowDefRust.h" + +#include "WindowDefBridge.h" + +void* WCDBRustWindowDefClassMethodWithNoArg(createCppObj) { + return (void*)WCDBWindowDefCreate().innerValue; +} + +void WCDBRustWindowDefClassMethod(configPartitions, + void* self, + WCDBRustMultiTypeArrayParameter(partitions)) { + WCDBRustBridgeStruct(CPPWindowDef, self); + WCDBRustCreateMultiTypeArray(partitions); + WCDBWindowDefConfigPartition2(selfStruct, partitionsArray); + // WCDBRustReleaseMultiTypeArray(partitions); +} + +void WCDBRustWindowDefClassMethod(configOrders, void* self, long* ordersArray, int ordersLength) { + WCDBRustBridgeStruct(CPPWindowDef, self); + // WCDBRustGetCppPointerArrayCritical(orders); + WCDBWindowDefConfigOrder(selfStruct, (const CPPOrderingTerm*)ordersArray, ordersLength); + // WCDBRustReleaseCppPointerArrayCritical(orders); +} + +void WCDBRustWindowDefClassMethod(configFrameSpec, void* self, void* frameSpec) { + WCDBRustBridgeStruct(CPPWindowDef, self); + WCDBRustBridgeStruct(CPPFrameSpec, frameSpec); + WCDBWindowDefConfigFrameSpec(selfStruct, frameSpecStruct); +} \ No newline at end of file diff --git a/src/rust/cpp/winq/identifier/WindowDefRust.h b/src/rust/cpp/winq/identifier/WindowDefRust.h new file mode 100644 index 000000000..62c4495e6 --- /dev/null +++ b/src/rust/cpp/winq/identifier/WindowDefRust.h @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include "WCDBRust.h" + +#define WCDBRustWindowDefFuncName(funcName) WCDBRust(WindowDef, funcName) +#define WCDBRustWindowDefObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(WindowDef, funcName, __VA_ARGS__) +#define WCDBRustWindowDefClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(WindowDef, funcName) +#define WCDBRustWindowDefClassMethod(funcName, ...) \ + WCDBRustClassMethod(WindowDef, funcName, __VA_ARGS__) + +void* WCDBRustWindowDefClassMethodWithNoArg(createCppObj); +void WCDBRustWindowDefClassMethod(configPartitions, + void* self, + WCDBRustMultiTypeArrayParameter(partitions)); +void WCDBRustWindowDefClassMethod(configOrders, void* self, long* ordersArray, int ordersLength); +void WCDBRustWindowDefClassMethod(configFrameSpec, void* self, void* frameSpec); diff --git a/src/rust/cpp/winq/statement/StatementAlterTableRust.c b/src/rust/cpp/winq/statement/StatementAlterTableRust.c new file mode 100644 index 000000000..c6016a55d --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementAlterTableRust.c @@ -0,0 +1,67 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementAlterTableRust.h" + +#include "StatementAlterTableBridge.h" + +void* WCDBRustStatementAlterTableClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementAlterTableCreate().innerValue; +} + +void WCDBRustStatementAlterTableClassMethod(configTable, void* self, const char* table) { + WCDBRustBridgeStruct(CPPStatementAlterTable, self); + WCDBStatementAlterTableConfigTable(selfStruct, table); +} + +void WCDBRustStatementAlterTableClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementAlterTable, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementAlterTableConfigSchema2(selfStruct, schema_common); +} + +void WCDBRustStatementAlterTableClassMethod(configRenameToTable, void* self, const char* table) { + WCDBRustBridgeStruct(CPPStatementAlterTable, self); + WCDBStatementAlterTableConfigRenameToTable(selfStruct, table); +} + +void WCDBRustStatementAlterTableClassMethod(configRenameColumn, + void* self, + WCDBRustObjectOrStringParameter(column)) { + WCDBRustBridgeStruct(CPPStatementAlterTable, self); + WCDBRustCreateObjectOrStringCommonValue(column, true); + WCDBStatementAlterTableConfigRenameColumn2(selfStruct, column_common); +} + +void WCDBRustStatementAlterTableClassMethod(configRenameToColumn, + void* self, + WCDBRustObjectOrStringParameter(column)) { + WCDBRustBridgeStruct(CPPStatementAlterTable, self); + WCDBRustCreateObjectOrStringCommonValue(column, true); + WCDBStatementAlterTableConfigRenameToColumn2(selfStruct, column_common); +} + +void WCDBRustStatementAlterTableClassMethod(configAddColumn, void* self, void* columnDef) { + WCDBRustBridgeStruct(CPPStatementAlterTable, self); + WCDBRustBridgeStruct(CPPColumnDef, columnDef); + WCDBStatementAlterTableConfigAddColumn(selfStruct, columnDefStruct); +} diff --git a/src/rust/cpp/winq/statement/StatementAlterTableRust.h b/src/rust/cpp/winq/statement/StatementAlterTableRust.h new file mode 100644 index 000000000..c3a1c32a1 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementAlterTableRust.h @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WCDBRust.h" + +#pragma once +#define WCDBRustStatementAlterTableFuncName(funcName) WCDBRust(StatementAlterTable, funcName) +#define WCDBRustStatementAlterTableObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementAlterTable, funcName, __VA_ARGS__) +#define WCDBRustStatementAlterTableClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementAlterTable, funcName) +#define WCDBRustStatementAlterTableClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementAlterTable, funcName, __VA_ARGS__) + +void* WCDBRustStatementAlterTableClassMethodWithNoArg(createCppObj); +void WCDBRustStatementAlterTableClassMethod(configTable, void* self, const char* table); +void WCDBRustStatementAlterTableClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); +void WCDBRustStatementAlterTableClassMethod(configRenameToTable, void* self, const char* table); +void WCDBRustStatementAlterTableClassMethod(configRenameColumn, + void* self, + WCDBRustObjectOrStringParameter(column)); +void WCDBRustStatementAlterTableClassMethod(configRenameToColumn, + void* self, + WCDBRustObjectOrStringParameter(column)); +void WCDBRustStatementAlterTableClassMethod(configAddColumn, void* self, void* columnDef); diff --git a/src/rust/cpp/winq/statement/StatementAnalyzeRust.c b/src/rust/cpp/winq/statement/StatementAnalyzeRust.c new file mode 100644 index 000000000..3453e1841 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementAnalyzeRust.c @@ -0,0 +1,58 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementAnalyzeRust.h" + +#include "StatementAnalyzeBridge.h" + +void* WCDBRustStatementAnalyzeClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementAnalyzeCreate().innerValue; +} + +void WCDBRustStatementAnalyzeClassMethod(toAnalyze, void* analyze) { + WCDBRustBridgeStruct(CPPStatementAnalyze, analyze); + WCDBStatementAnalyzeToAnalyze(analyzeStruct); +} + +void WCDBRustStatementAnalyzeClassMethod(configSchema, + void* analyze, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementAnalyze, analyze); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementAnalyzeConfigSchema2(analyzeStruct, schema_common); + // WCDBRustTryReleaseStringInCommonValue(schema); +} + +void WCDBRustStatementAnalyzeClassMethod(configTable, void* analyze, const char* table) { + WCDBRustBridgeStruct(CPPStatementAnalyze, analyze); + // WCDBRustGetStringCritical(table); + WCDBStatementAnalyzeConfigTable(analyzeStruct, table); + // WCDBRustReleaseStringCritical(table); +} + +void WCDBRustStatementAnalyzeClassMethod(configIndex, void* analyze, const char* index) { + WCDBRustBridgeStruct(CPPStatementAnalyze, analyze); + // WCDBRustGetStringCritical(index); + WCDBStatementAnalyzeConfigIndex(analyzeStruct, index); + // WCDBRustReleaseStringCritical(index); +} diff --git a/src/rust/cpp/winq/statement/StatementAnalyzeRust.h b/src/rust/cpp/winq/statement/StatementAnalyzeRust.h new file mode 100644 index 000000000..948731156 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementAnalyzeRust.h @@ -0,0 +1,41 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WCDBRust.h" + +#pragma once +#define WCDBRustStatementAnalyzeFuncName(funcName) WCDBRust(StatementAnalyze, funcName) +#define WCDBRustStatementAnalyzeObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementAnalyze, funcName, __VA_ARGS__) +#define WCDBRustStatementAnalyzeClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementAnalyze, funcName) +#define WCDBRustStatementAnalyzeClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementAnalyze, funcName, __VA_ARGS__) + +void* WCDBRustStatementAnalyzeClassMethodWithNoArg(createCppObj); +void WCDBRustStatementAnalyzeClassMethod(toAnalyze, void* analyze); +void WCDBRustStatementAnalyzeClassMethod(configSchema, + void* analyze, + WCDBRustObjectOrStringParameter(schema)); +void WCDBRustStatementAnalyzeClassMethod(configTable, void* analyze, const char* table); +void WCDBRustStatementAnalyzeClassMethod(configIndex, void* analyze, const char* index); diff --git a/src/rust/cpp/winq/statement/StatementAttachRust.c b/src/rust/cpp/winq/statement/StatementAttachRust.c new file mode 100644 index 000000000..83c0ca09a --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementAttachRust.c @@ -0,0 +1,57 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementAttachRust.h" + +#include "StatementAttachBridge.h" + +void* WCDBRustStatementAttachClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementAttachCreate().innerValue; +} + +void WCDBRustStatementAttachClassMethod(configPath, + void* self, + WCDBRustObjectOrStringParameter(path)) { + WCDBRustBridgeStruct(CPPStatementAttach, self); + WCDBRustCreateObjectOrStringCommonValue(path, true); + WCDBStatementAttachConfigPath2(selfStruct, path_common); + // WCDBRustTryReleaseStringInCommonValue(path); +} + +void WCDBRustStatementAttachClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementAttach, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementAttachConfigSchema2(selfStruct, schema_common); + // WCDBRustTryReleaseStringInCommonValue(schema); +} + +void WCDBRustStatementAttachClassMethod(configKey, + void* self, + WCDBRustObjectOrStringParameter(key)) { + WCDBRustBridgeStruct(CPPStatementAttach, self); + WCDBRustCreateObjectOrStringCommonValue(key, true); + WCDBStatementAttachConfigKey2(selfStruct, key_common); + // WCDBRustTryReleaseStringInCommonValue(key); +} diff --git a/src/rust/cpp/winq/statement/StatementAttachRust.h b/src/rust/cpp/winq/statement/StatementAttachRust.h new file mode 100644 index 000000000..d7694da0d --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementAttachRust.h @@ -0,0 +1,44 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WCDBRust.h" + +#pragma once +#define WCDBRustStatementAttachFuncName(funcName) WCDBRust(StatementAttach, funcName) +#define WCDBRustStatementAttachObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementAttach, funcName, __VA_ARGS__) +#define WCDBRustStatementAttachClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementAttach, funcName) +#define WCDBRustStatementAttachClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementAttach, funcName, __VA_ARGS__) + +void* WCDBRustStatementAttachClassMethodWithNoArg(createCppObj); +void WCDBRustStatementAttachClassMethod(configPath, + void* self, + WCDBRustObjectOrStringParameter(path)); +void WCDBRustStatementAttachClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); +void WCDBRustStatementAttachClassMethod(configKey, + void* self, + WCDBRustObjectOrStringParameter(key)); \ No newline at end of file diff --git a/src/rust/cpp/winq/statement/StatementBeginRust.c b/src/rust/cpp/winq/statement/StatementBeginRust.c new file mode 100644 index 000000000..8acdfbf88 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementBeginRust.c @@ -0,0 +1,32 @@ +// Created by chenqiuwen on 2023/4/9. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementBeginRust.h" + +#include "StatementBeginBridge.h" + +void* WCDBRustStatementBeginClassMethod(create, int type) { + CPPStatementBegin begin = WCDBStatementBeginCreate(); + WCDBStatementBeginConfigType(begin, type); + return (void*)begin.innerValue; +} diff --git a/src/rust/cpp/winq/statement/StatementBeginRust.h b/src/rust/cpp/winq/statement/StatementBeginRust.h new file mode 100644 index 000000000..7cd0351c9 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementBeginRust.h @@ -0,0 +1,35 @@ +// Created by chenqiuwen on 2023/4/9. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include "WCDBRust.h" + +#define WCDBRustStatementBeginFuncName(funcName) WCDBRust(StatementBegin, funcName) +#define WCDBRustStatementBeginObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementBegin, funcName, __VA_ARGS__) +#define WCDBRustStatementBeginClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementBegin, funcName) +#define WCDBRustStatementBeginClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementBegin, funcName, __VA_ARGS__) + +void* WCDBRustStatementBeginClassMethod(create, int type); diff --git a/src/rust/cpp/winq/statement/StatementCommitRust.c b/src/rust/cpp/winq/statement/StatementCommitRust.c new file mode 100644 index 000000000..e12d78d90 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementCommitRust.c @@ -0,0 +1,30 @@ +// Created by chenqiuwen on 2023/4/9. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementCommitRust.h" + +#include "StatementCommitBridge.h" + +void* WCDBRustStatementCommitClassMethodWithNoArg(create) { + return (void*)WCDBStatementCommitCreate().innerValue; +} diff --git a/src/rust/cpp/winq/statement/StatementCommitRust.h b/src/rust/cpp/winq/statement/StatementCommitRust.h new file mode 100644 index 000000000..2d662eeb8 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementCommitRust.h @@ -0,0 +1,36 @@ +// Created by chenqiuwen on 2023/4/9. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementCommitFuncName(funcName) WCDBRust(StatementCommit, funcName) +#define WCDBRustStatementCommitObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementCommit, funcName, __VA_ARGS__) +#define WCDBRustStatementCommitObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementCommit, funcName) +#define WCDBRustStatementCommitClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementCommit, funcName) + +void* WCDBRustStatementCommitClassMethodWithNoArg(create); diff --git a/src/rust/cpp/winq/statement/StatementCreateTableRust.c b/src/rust/cpp/winq/statement/StatementCreateTableRust.c new file mode 100644 index 000000000..170c57784 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementCreateTableRust.c @@ -0,0 +1,95 @@ +// Created by qiuwenchen on 2023/4/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementCreateTableRust.h" + +#include "StatementCreateTableBridge.h" + +void* WCDBRustStatementCreateTableClassMethodWithNoArg(create) { + return (void*)WCDBStatementCreateTableCreate().innerValue; +} + +void WCDBRustStatementCreateTableClassMethod(configTableName, void* self, const char* tableName) { + WCDBRustBridgeStruct(CPPStatementCreateTable, self); + WCDBStatementCreateTableConfigTable(selfStruct, tableName); +} +// +// void WCDBRustStatementCreateTableClassMethod(configSchema, +// jlong self, +// WCDBRustObjectOrStringParameter(schema)) +//{ +// WCDBRustBridgeStruct(CPPStatementCreateTable, self); +// WCDBRustCreateObjectOrStringCommonValue(schema, true); +// WCDBStatementCreateTableConfigSchema2(selfStruct, schema_common); +// WCDBRustTryReleaseStringInCommonValue(schema); +//} +// +// void WCDBRustStatementCreateTableClassMethod(configTemp, jlong self) +//{ +// WCDBRustBridgeStruct(CPPStatementCreateTable, self); +// WCDBStatementCreateTableConfigTemp(selfStruct); +//} +// +// void WCDBRustStatementCreateTableClassMethod(configIfNotExist, jlong self) +//{ +// WCDBRustBridgeStruct(CPPStatementCreateTable, self); +// WCDBStatementCreateTableConfigIfNotExist(selfStruct); +//} +// +// void WCDBRustStatementCreateTableClassMethod(configAs, jlong self, jlong select) +//{ +// WCDBRustBridgeStruct(CPPStatementCreateTable, self); +// WCDBRustBridgeStruct(CPPStatementSelect, select); +// WCDBStatementCreateTableConfigAs(selfStruct, selectStruct); +//} +// +// void WCDBRustStatementCreateTableClassMethod(configColumn, jlong self, jlong column) +//{ +// WCDBRustBridgeStruct(CPPStatementCreateTable, self); +// WCDBRustBridgeStruct(CPPColumnDef, column); +// WCDBStatementCreateTableConfigColumn(selfStruct, columnStruct); +//} +// +void WCDBRustStatementCreateTableClassMethod(configColumns, + void* self, + const void** columns, + int len) { + WCDBRustBridgeStruct(CPPStatementCreateTable, self); + WCDBStatementCreateTableConfigColumns(selfStruct, (const CPPColumnDef*)columns, len); +} +// +// void WCDBRustStatementCreateTableClassMethod(configConstraints, jlong self, jlongArray +// constraints) +//{ +// WCDBRustBridgeStruct(CPPStatementCreateTable, self); +// WCDBRustGetCppPointerArrayCritical(constraints); +// WCDBStatementCreateTableConfigTableConstraints( +// selfStruct, (const CPPTableConstraint*) constraintsArray, constraintsLength); +// WCDBRustReleaseCppPointerArrayCritical(constraints) +//} +// +// void WCDBRustStatementCreateTableClassMethod(configWithoutRowid, jlong self) +//{ +// WCDBRustBridgeStruct(CPPStatementCreateTable, self); +// WCDBStatementCreateTableConfigWithoutRowId(selfStruct); +//} diff --git a/src/rust/cpp/winq/statement/StatementCreateTableRust.h b/src/rust/cpp/winq/statement/StatementCreateTableRust.h new file mode 100644 index 000000000..6eec20626 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementCreateTableRust.h @@ -0,0 +1,52 @@ +// Created by qiuwenchen on 2023/4/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementCreateTableFuncName(funcName) WCDB(StatementCreateTable, funcName) +#define WCDBRustStatementCreateTableObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementCreateTable, funcName, __VA_ARGS__) +#define WCDBRustStatementCreateTableObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementCreateTable, funcName) +#define WCDBRustStatementCreateTableClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementCreateTable, funcName) +#define WCDBRustStatementCreateTableClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementCreateTable, funcName, __VA_ARGS__) + +void* WCDBRustStatementCreateTableClassMethodWithNoArg(create); +void WCDBRustStatementCreateTableClassMethod(configTableName, void* self, const char* tableName); +// void WCDBRustStatementCreateTableClassMethod(configSchema, +// jlong self, +// WCDBRustObjectOrStringParameter(schema)); +// void WCDBRustStatementCreateTableClassMethod(configTemp, jlong self); +// void WCDBRustStatementCreateTableClassMethod(configIfNotExist, jlong self); +// void WCDBRustStatementCreateTableClassMethod(configAs, jlong self, jlong select); +// void WCDBRustStatementCreateTableClassMethod(configColumn, jlong self, jlong column); +void WCDBRustStatementCreateTableClassMethod(configColumns, + void* self, + const void** columns, + int len); +// void WCDBRustStatementCreateTableClassMethod(configConstraints, jlong self, jlongArray +// constraints); void WCDBRustStatementCreateTableClassMethod(configWithoutRowid, jlong self); \ No newline at end of file diff --git a/src/rust/cpp/winq/statement/StatementCreateTriggerRust.c b/src/rust/cpp/winq/statement/StatementCreateTriggerRust.c new file mode 100644 index 000000000..ad278add1 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementCreateTriggerRust.c @@ -0,0 +1,136 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementCreateTriggerRust.h" + +#include "StatementCreateTriggerBridge.h" + +void* WCDBRustStatementCreateTriggerClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementCreateTriggerCreate().innerValue; +} + +void WCDBRustStatementCreateTriggerClassMethod(configTrigger, void* self, const char* name) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + // WCDBRustGetStringCritical(name); + WCDBStatementCreateTriggerConfigTrigger(selfStruct, name); + // WCDBRustReleaseStringCritical(name); +} + +void WCDBRustStatementCreateTriggerClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementCreateTriggerConfigSchema2(selfStruct, schema_common); + // WCDBRustTryReleaseStringInCommonValue(schema); +} + +void WCDBRustStatementCreateTriggerClassMethod(configTemp, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBStatementCreateTriggerConfigTemp(selfStruct); +} + +void WCDBRustStatementCreateTriggerClassMethod(configIfNotExist, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBStatementCreateTriggerConfigIfNotExist(selfStruct); +} + +void WCDBRustStatementCreateTriggerClassMethod(configBefore, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBStatementCreateTriggerConfigBefore(selfStruct); +} + +void WCDBRustStatementCreateTriggerClassMethod(configAfter, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBStatementCreateTriggerConfigAfter(selfStruct); +} + +void WCDBRustStatementCreateTriggerClassMethod(configInsteadOf, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBStatementCreateTriggerConfigInsteadOf(selfStruct); +} + +void WCDBRustStatementCreateTriggerClassMethod(configDelete, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBStatementCreateTriggerConfigDelete(selfStruct); +} + +void WCDBRustStatementCreateTriggerClassMethod(configInsert, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBStatementCreateTriggerConfigInsert(selfStruct); +} + +void WCDBRustStatementCreateTriggerClassMethod(configUpdate, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBStatementCreateTriggerConfigUpdate(selfStruct); +} + +void WCDBRustStatementCreateTriggerClassMethod(configColumns, + void* self, + WCDBRustObjectOrStringArrayParameter(columns)) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBRustCreateObjectOrStringArrayCriticalWithAction( + columns, WCDBStatementCreateTriggerConfigColumns2(selfStruct, columns_commonArray)); +} + +void WCDBRustStatementCreateTriggerClassMethod(configTable, void* self, const char* table) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + // WCDBRustGetStringCritical(table); + WCDBStatementCreateTriggerConfigTable(selfStruct, table); + // WCDBRustReleaseStringCritical(table); +} + +void WCDBRustStatementCreateTriggerClassMethod(configForEachRow, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBStatementCreateTriggerConfigForEachRow(selfStruct); +} + +void WCDBRustStatementCreateTriggerClassMethod(configWhen, void* self, void* expression) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBRustBridgeStruct(CPPExpression, expression); + WCDBStatementCreateTriggerConfigWhen(selfStruct, expressionStruct); +} + +void WCDBRustStatementCreateTriggerClassMethod(executeInsert, void* self, void* insert) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBRustBridgeStruct(CPPStatementInsert, insert); + WCDBStatementCreateTriggerExecuteInsert(selfStruct, insertStruct); +} + +void WCDBRustStatementCreateTriggerClassMethod(executeUpdate, void* self, void* update) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBRustBridgeStruct(CPPStatementUpdate, update); + WCDBStatementCreateTriggerExecuteUpdate(selfStruct, updateStruct); +} + +void WCDBRustStatementCreateTriggerClassMethod(executeDelete, void* self, void* delete_) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBRustBridgeStruct(CPPStatementDelete, delete_); + WCDBStatementCreateTriggerExecuteDelete(selfStruct, delete_Struct); +} + +void WCDBRustStatementCreateTriggerClassMethod(executeSelect, void* self, void* select) { + WCDBRustBridgeStruct(CPPStatementCreateTrigger, self); + WCDBRustBridgeStruct(CPPStatementSelect, select); + WCDBStatementCreateTriggerExecuteSelect(selfStruct, selectStruct); +} diff --git a/src/rust/cpp/winq/statement/StatementCreateTriggerRust.h b/src/rust/cpp/winq/statement/StatementCreateTriggerRust.h new file mode 100644 index 000000000..f3e7bd791 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementCreateTriggerRust.h @@ -0,0 +1,65 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementCreateTriggerFuncName(funcName) WCDBRust(StatementCreateTrigger, funcName) +#define WCDBRustStatementCreateTriggerObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementCreateTrigger, funcName, __VA_ARGS__) +#define WCDBRustStatementCreateTriggerObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementCreateTrigger, funcName) +#define WCDBRustStatementCreateTriggerClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementCreateTrigger, funcName) +#define WCDBRustStatementCreateTriggerClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementCreateTrigger, funcName, __VA_ARGS__) + +void* WCDBRustStatementCreateTriggerClassMethodWithNoArg(createCppObj); + +void WCDBRustStatementCreateTriggerClassMethod(configTrigger, void* self, const char* name); +void WCDBRustStatementCreateTriggerClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); +void WCDBRustStatementCreateTriggerClassMethod(configTemp, void* self); +void WCDBRustStatementCreateTriggerClassMethod(configIfNotExist, void* self); + +void WCDBRustStatementCreateTriggerClassMethod(configBefore, void* self); +void WCDBRustStatementCreateTriggerClassMethod(configAfter, void* self); +void WCDBRustStatementCreateTriggerClassMethod(configInsteadOf, void* self); + +void WCDBRustStatementCreateTriggerClassMethod(configDelete, void* self); +void WCDBRustStatementCreateTriggerClassMethod(configInsert, void* self); +void WCDBRustStatementCreateTriggerClassMethod(configUpdate, void* self); + +void WCDBRustStatementCreateTriggerClassMethod(configColumns, + void* self, + WCDBRustObjectOrStringArrayParameter(columns)); +void WCDBRustStatementCreateTriggerClassMethod(configTable, void* self, const char* table); +void WCDBRustStatementCreateTriggerClassMethod(configForEachRow, void* self); +void WCDBRustStatementCreateTriggerClassMethod(configWhen, void* self, void* expression); + +void WCDBRustStatementCreateTriggerClassMethod(executeInsert, void* self, void* insert); +void WCDBRustStatementCreateTriggerClassMethod(executeUpdate, void* self, void* update); +void WCDBRustStatementCreateTriggerClassMethod(executeDelete, void* self, void* delete_); +void WCDBRustStatementCreateTriggerClassMethod(executeSelect, void* self, void* select); diff --git a/src/rust/cpp/winq/statement/StatementCreateViewRust.c b/src/rust/cpp/winq/statement/StatementCreateViewRust.c new file mode 100644 index 000000000..c9a7a27f7 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementCreateViewRust.c @@ -0,0 +1,70 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementCreateViewRust.h" + +#include "StatementCreateViewBridge.h" + +void* WCDBRustStatementCreateViewClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementCreateViewCreate().innerValue; +} + +void WCDBRustStatementCreateViewClassMethod(configView, void* self, const char* name) { + WCDBRustBridgeStruct(CPPStatementCreateView, self); + // WCDBRustGetStringCritical(name); + WCDBStatementCreateViewConfigView(selfStruct, name); + // WCDBRustReleaseStringCritical(name); +} + +void WCDBRustStatementCreateViewClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementCreateView, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementCreateViewConfigSchema2(selfStruct, schema_common); + // WCDBRustTryReleaseStringInCommonValue(schema); +} + +void WCDBRustStatementCreateViewClassMethod(configTemp, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateView, self); + WCDBStatementCreateViewConfigTemp(selfStruct); +} + +void WCDBRustStatementCreateViewClassMethod(configIfNotExist, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateView, self); + WCDBStatementCreateViewConfigIfNotExist(selfStruct); +} + +void WCDBRustStatementCreateViewClassMethod(configAs, void* self, void* select) { + WCDBRustBridgeStruct(CPPStatementCreateView, self); + WCDBRustBridgeStruct(CPPStatementSelect, select); + WCDBStatementCreateViewConfigAs(selfStruct, selectStruct); +} + +void WCDBRustStatementCreateViewClassMethod(configColumns, + void* self, + WCDBRustObjectOrStringArrayParameter(columns)) { + WCDBRustBridgeStruct(CPPStatementCreateView, self); + WCDBRustCreateObjectOrStringArrayCriticalWithAction( + columns, WCDBStatementCreateViewConfigColumns2(selfStruct, columns_commonArray)); +} diff --git a/src/rust/cpp/winq/statement/StatementCreateViewRust.h b/src/rust/cpp/winq/statement/StatementCreateViewRust.h new file mode 100644 index 000000000..4d73ed561 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementCreateViewRust.h @@ -0,0 +1,49 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementCreateViewFuncName(funcName) WCDBRust(StatementCreateView, funcName) +#define WCDBRustStatementCreateViewObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementCreateView, funcName, __VA_ARGS__) +#define WCDBRustStatementCreateViewObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementCreateView, funcName) +#define WCDBRustStatementCreateViewClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementCreateView, funcName) +#define WCDBRustStatementCreateViewClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementCreateView, funcName, __VA_ARGS__) + +void* WCDBRustStatementCreateViewClassMethodWithNoArg(createCppObj); + +void WCDBRustStatementCreateViewClassMethod(configView, void* self, const char* name); +void WCDBRustStatementCreateViewClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); +void WCDBRustStatementCreateViewClassMethod(configTemp, void* self); +void WCDBRustStatementCreateViewClassMethod(configIfNotExist, void* self); +void WCDBRustStatementCreateViewClassMethod(configAs, void* self, void* select); +void WCDBRustStatementCreateViewClassMethod(configColumns, + void* self, + WCDBRustObjectOrStringArrayParameter(columns)); diff --git a/src/rust/cpp/winq/statement/StatementCreateVirtualTableRust.c b/src/rust/cpp/winq/statement/StatementCreateVirtualTableRust.c new file mode 100644 index 000000000..acd69dc42 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementCreateVirtualTableRust.c @@ -0,0 +1,69 @@ +// Created by chenqiuwen on 2023/11/4. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementCreateVirtualTableRust.h" + +#include "StatementCreateVirtualTableBridge.h" + +void* WCDBRustStatementCreateVirtualTableClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementCreateVirtualTableCreate().innerValue; +} + +void WCDBRustStatementCreateVirtualTableClassMethod(configTable, void* self, const char* name) { + // WCDBRustGetString(name); + WCDBRustBridgeStruct(CPPStatementCreateVirtualTable, self); + WCDBStatementCreateVirtualTableConfigTable(selfStruct, name); + // WCDBRustReleaseString(name); +} + +void WCDBRustStatementCreateVirtualTableClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementCreateVirtualTable, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementCreateVirtualTableConfigSchema2(selfStruct, schema_common); + // WCDBRustTryReleaseStringInCommonValue(schema); +} + +void WCDBRustStatementCreateVirtualTableClassMethod(configIfNotExist, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateVirtualTable, self); + WCDBStatementCreateVirtualTableConfigIfNotExist(selfStruct); +} + +void WCDBRustStatementCreateVirtualTableClassMethod(configModule, void* self, const char* module) { + WCDBRustBridgeStruct(CPPStatementCreateVirtualTable, self); + // WCDBRustGetString(module); + WCDBStatementCreateVirtualTableConfigModule(selfStruct, module); + // WCDBRustReleaseString(module); +} + +void WCDBRustStatementCreateVirtualTableClassMethod(configArguments, + void* self, + const char** arguments, + int argumentsLength) { + WCDBRustBridgeStruct(CPPStatementCreateVirtualTable, self); + // WCDBRustGetStringArray(arguments); + WCDBStatementCreateVirtualTableConfigArguments(selfStruct, (const char* const*)arguments, + argumentsLength); + // WCDBRustReleaseStringArray(arguments); +} diff --git a/src/rust/cpp/winq/statement/StatementCreateVirtualTableRust.h b/src/rust/cpp/winq/statement/StatementCreateVirtualTableRust.h new file mode 100644 index 000000000..58eb54f88 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementCreateVirtualTableRust.h @@ -0,0 +1,50 @@ +// Created by chenqiuwen on 2023/11/4. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementCreateVirtualTableFuncName(funcName) \ + WCDBRust(StatementCreateVirtualTable, funcName) +#define WCDBRustStatementCreateVirtualTableObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementCreateVirtualTable, funcName, __VA_ARGS__) +#define WCDBRustStatementCreateVirtualTableObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementCreateVirtualTable, funcName) +#define WCDBRustStatementCreateVirtualTableClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementCreateVirtualTable, funcName) +#define WCDBRustStatementCreateVirtualTableClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementCreateVirtualTable, funcName, __VA_ARGS__) + +void* WCDBRustStatementCreateVirtualTableClassMethodWithNoArg(createCppObj); + +void WCDBRustStatementCreateVirtualTableClassMethod(configTable, void* self, const char* name); +void WCDBRustStatementCreateVirtualTableClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); +void WCDBRustStatementCreateVirtualTableClassMethod(configIfNotExist, void* self); +void WCDBRustStatementCreateVirtualTableClassMethod(configModule, void* self, const char* module); +void WCDBRustStatementCreateVirtualTableClassMethod(configArguments, + void* self, + const char** arguments, + int argumentsLength); diff --git a/src/rust/cpp/winq/statement/StatementCreateindexRust.c b/src/rust/cpp/winq/statement/StatementCreateindexRust.c new file mode 100644 index 000000000..bb92847f3 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementCreateindexRust.c @@ -0,0 +1,70 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementCreateindexRust.h" + +#include "StatementCreateIndexBridge.h" + +void* WCDBRustStatementCreateIndexClassMethodWithNoArg(create) { + return (void*)WCDBStatementCreateIndexCreate().innerValue; +} + +void WCDBRustStatementCreateIndexClassMethod(configIndex, void* self, const char* name) { + WCDBRustBridgeStruct(CPPStatementCreateIndex, self); + WCDBStatementCreateIndexConfigIndexName(selfStruct, name); +} + +void WCDBRustStatementCreateIndexClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementCreateIndex, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementCreateIndexConfigSchema2(selfStruct, schema_common); +} + +void WCDBRustStatementCreateIndexClassMethod(configUnique, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateIndex, self); + WCDBStatementCreateIndexConfigUniqe(selfStruct); +} + +void WCDBRustStatementCreateIndexClassMethod(configIfNotExist, void* self) { + WCDBRustBridgeStruct(CPPStatementCreateIndex, self); + WCDBStatementCreateIndexConfigIfNotExist(selfStruct); +} + +void WCDBRustStatementCreateIndexClassMethod(configTable, void* self, const char* tableName) { + WCDBRustBridgeStruct(CPPStatementCreateIndex, self); + WCDBStatementCreateIndexConfigTable(selfStruct, tableName); +} + +void WCDBRustStatementCreateIndexClassMethod(configIndexedColumns, + void* self, + WCDBRustObjectOrStringArrayParameter(indexColumns)) { + WCDBRustBridgeStruct(CPPStatementCreateIndex, self); + WCDBRustCreateObjectOrStringArrayCriticalWithAction( + indexColumns, + WCDBStatementCreateIndexConfigIndexColumns2(selfStruct, indexColumns_commonArray)); +} + +void WCDBRustStatementCreateIndexClassMethod(configWhere, void* self, void* condition) { + WCDBRustBridgeStruct(CPPStatementCreateIndex, self); + WCDBRustBridgeStruct(CPPExpression, condition); + WCDBStatementCreateIndexConfigWhere(selfStruct, conditionStruct); +} \ No newline at end of file diff --git a/src/rust/cpp/winq/statement/StatementCreateindexRust.h b/src/rust/cpp/winq/statement/StatementCreateindexRust.h new file mode 100644 index 000000000..16066bb54 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementCreateindexRust.h @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementCreateIndexFuncName(funcName) WCDBRust(StatementCreateIndex, funcName) +#define WCDBRustStatementCreateIndexObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementCreateIndex, funcName, __VA_ARGS__) +#define WCDBRustStatementCreateIndexObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementCreateIndex, funcName) +#define WCDBRustStatementCreateIndexClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementCreateIndex, funcName) +#define WCDBRustStatementCreateIndexClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementCreateIndex, funcName, __VA_ARGS__) + +void* WCDBRustStatementCreateIndexClassMethodWithNoArg(create); +void WCDBRustStatementCreateIndexClassMethod(configIndex, void* self, const char* name); +void WCDBRustStatementCreateIndexClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); +void WCDBRustStatementCreateIndexClassMethod(configUnique, void* self); +void WCDBRustStatementCreateIndexClassMethod(configIfNotExist, void* self); +void WCDBRustStatementCreateIndexClassMethod(configTable, void* self, const char* tableName); +void WCDBRustStatementCreateIndexClassMethod(configIndexedColumns, + void* self, + WCDBRustObjectOrStringArrayParameter(indexColumns)); +void WCDBRustStatementCreateIndexClassMethod(configWhere, void* self, void* condition); diff --git a/src/rust/cpp/winq/statement/StatementDeleteRust.c b/src/rust/cpp/winq/statement/StatementDeleteRust.c new file mode 100644 index 000000000..5a84f5cf5 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementDeleteRust.c @@ -0,0 +1,95 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementDeleteRust.h" + +#include "StatementDeleteBridge.h" + +void* WCDBRustStatementDeleteClassMethodWithNoArg(create) { + return (void*)WCDBStatementDeleteCreate().innerValue; +} + +// void WCDBRustStatementDeleteClassMethod(configWith, jlong self, jlongArray expressions) +//{ +// WCDBRustBridgeStruct(CPPStatementDelete, self); +// WCDBRustGetCppPointerArrayCritical(expressions); +// WCDBStatementDeleteConfigWith( +// selfStruct, (const CPPCommonTableExpression *) expressionsArray, expressionsLength); +// WCDBRustReleaseCppPointerArrayCritical(expressions); +// } +// +// void WCDBRustStatementDeleteClassMethod(configRecursive, jlong self) +//{ +// WCDBRustBridgeStruct(CPPStatementDelete, self); +// WCDBStatementDeleteConfigRecursive(selfStruct); +// } + +void WCDBRustStatementDeleteClassMethod(configTable, + void* self, + WCDBRustObjectOrStringParameter(table)) { + WCDBRustBridgeStruct(CPPStatementDelete, self); + WCDBRustCreateObjectOrStringCommonValue(table, true); + WCDBStatementDeleteConfigDeleteFrom2(selfStruct, table_common); +} + +void WCDBRustStatementDeleteClassMethod(configCondition, void* self, void* condition) { + WCDBRustBridgeStruct(CPPStatementDelete, self); + WCDBRustBridgeStruct(CPPExpression, condition); + WCDBStatementDeleteConfigWhere(selfStruct, conditionStruct); +} + +void WCDBRustStatementDeleteClassMethod(configOrders, void* self, void** orders, size_t len) { + WCDBRustBridgeStruct(CPPStatementDelete, self); + // WCDBRustGetCppPointerArrayCritical(orders, len); + WCDBStatementDeleteConfigOrder(selfStruct, (const CPPOrderingTerm*)orders, (int)len); + // WCDBRustReleaseCppPointerArrayCritical(orders); +} + +void WCDBRustStatementDeleteClassMethod(configLimitRange, + void* self, + int fromType, + long from, + int toType, + long to) { + WCDBRustBridgeStruct(CPPStatementDelete, self); + CPPCommonValue from_common; + from_common.type = fromType; + from_common.intValue = from; + CPPCommonValue to_common; + to_common.type = toType; + to_common.intValue = to; + WCDBStatementDeleteConfigLimitRange2(selfStruct, from_common, to_common); +} + +void WCDBRustStatementDeleteClassMethod(configLimitCount, void* self, int type, long limit) { + WCDBRustBridgeStruct(CPPStatementDelete, self); + CPPCommonValue limit_common; + limit_common.type = type; + limit_common.intValue = limit; + WCDBStatementDeleteConfigLimitCount2(selfStruct, limit_common); +} + +void WCDBRustStatementDeleteClassMethod(configOffset, void* self, int type, long offset) { + WCDBRustBridgeStruct(CPPStatementDelete, self); + CPPCommonValue offset_common; + offset_common.type = type; + offset_common.intValue = offset; + WCDBStatementDeleteConfigOffset2(selfStruct, offset_common); +} diff --git a/src/rust/cpp/winq/statement/StatementDeleteRust.h b/src/rust/cpp/winq/statement/StatementDeleteRust.h new file mode 100644 index 000000000..5b7832924 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementDeleteRust.h @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementDeleteFuncName(funcName) WCDBRust(StatementDelete, funcName) +#define WCDBRustStatementDeleteObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementDelete, funcName, __VA_ARGS__) +#define WCDBRustStatementDeleteObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementDelete, funcName) +#define WCDBRustStatementDeleteClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementDelete, funcName) +#define WCDBRustStatementDeleteClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementDelete, funcName, __VA_ARGS__) + +void* WCDBRustStatementDeleteClassMethodWithNoArg(create); + +// void WCDBRustStatementDeleteClassMethod(configWith, jlong self, jlongArray expressions); +// void WCDBRustStatementDeleteClassMethod(configRecursive, jlong self); +// +void WCDBRustStatementDeleteClassMethod(configTable, + void* self, + WCDBRustObjectOrStringParameter(table)); + +void WCDBRustStatementDeleteClassMethod(configCondition, void* self, void* condition); + +void WCDBRustStatementDeleteClassMethod(configOrders, void* self, void** orders, size_t len); + +void WCDBRustStatementDeleteClassMethod(configLimitRange, + void* self, + int fromType, + long from, + int toType, + long to); +void WCDBRustStatementDeleteClassMethod(configLimitCount, void* self, int type, long limit); +void WCDBRustStatementDeleteClassMethod(configOffset, void* self, int type, long offset); diff --git a/src/rust/cpp/winq/statement/StatementDetachRust.c b/src/rust/cpp/winq/statement/StatementDetachRust.c new file mode 100644 index 000000000..3d770462f --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementDetachRust.c @@ -0,0 +1,39 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementDetachRust.h" + +#include "StatementDetachBridge.h" + +void* WCDBRustStatementDetachClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementDetachCreate().innerValue; +} + +void WCDBRustStatementDetachClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementDetach, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementDetachConfigSchema2(selfStruct, schema_common); + // WCDBRustTryReleaseStringInCommonValue(schema); +} \ No newline at end of file diff --git a/src/rust/cpp/winq/statement/StatementDetachRust.h b/src/rust/cpp/winq/statement/StatementDetachRust.h new file mode 100644 index 000000000..672a8eb2d --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementDetachRust.h @@ -0,0 +1,41 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementDetachFuncName(funcName) WCDBRust(StatementDetach, funcName) +#define WCDBRustStatementDetachObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementDetach, funcName, __VA_ARGS__) +#define WCDBRustStatementDetachObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementDetach, funcName) +#define WCDBRustStatementDetachClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementDetach, funcName) +#define WCDBRustStatementDetachClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementDetach, funcName, __VA_ARGS__) + +void* WCDBRustStatementDetachClassMethodWithNoArg(createCppObj); +void WCDBRustStatementDetachClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); diff --git a/src/rust/cpp/winq/statement/StatementDropIndexRust.c b/src/rust/cpp/winq/statement/StatementDropIndexRust.c new file mode 100644 index 000000000..2a583e20e --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementDropIndexRust.c @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementDropIndexRust.h" + +#include "StatementDropIndexBridge.h" + +void* WCDBRustStatementDropIndexClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementDropIndexCreate().innerValue; +} + +void WCDBRustStatementDropIndexClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementDropIndex, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementDropIndexConfigSchema2(selfStruct, schema_common); +} + +void WCDBRustStatementDropIndexClassMethod(configIndex, void* self, const char* indexName) { + WCDBRustBridgeStruct(CPPStatementDropIndex, self); + WCDBStatementDropIndexConfigIndex(selfStruct, indexName); +} + +void WCDBRustStatementDropIndexClassMethod(configIfExist, void* self) { + WCDBRustBridgeStruct(CPPStatementDropIndex, self); + WCDBStatementDropIndexConfigIfExists(selfStruct); +} diff --git a/src/rust/cpp/winq/statement/StatementDropIndexRust.h b/src/rust/cpp/winq/statement/StatementDropIndexRust.h new file mode 100644 index 000000000..34b6aa5c6 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementDropIndexRust.h @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include "WCDBRust.h" + +#define WCDBRustStatementDropIndexFuncName(funcName) WCDBRust(StatementDropIndex, funcName) +#define WCDBRustStatementDropIndexObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementDropIndex, funcName, __VA_ARGS__) +#define WCDBRustStatementDropIndexObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementDropIndex, funcName) +#define WCDBRustStatementDropIndexClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementDropIndex, funcName) +#define WCDBRustStatementDropIndexClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementDropIndex, funcName, __VA_ARGS__) + +void* WCDBRustStatementDropIndexClassMethodWithNoArg(createCppObj); +void WCDBRustStatementDropIndexClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); +void WCDBRustStatementDropIndexClassMethod(configIndex, void* self, const char* indexName); +void WCDBRustStatementDropIndexClassMethod(configIfExist, void* self); diff --git a/src/rust/cpp/winq/statement/StatementDropTableRust.c b/src/rust/cpp/winq/statement/StatementDropTableRust.c new file mode 100644 index 000000000..456a6b680 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementDropTableRust.c @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementDropTableRust.h" + +#include "StatementDropTableBridge.h" + +void* WCDBRustStatementDropTableClassMethodWithNoArg(create) { + return (void*)WCDBStatementDropTableCreate().innerValue; +} + +void WCDBRustStatementDropTableClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementDropTable, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementDropTableConfigSchema2(selfStruct, schema_common); +} + +void WCDBRustStatementDropTableClassMethod(configTableName, void* self, const char* tableName) { + WCDBRustBridgeStruct(CPPStatementDropTable, self); + WCDBStatementDropTableConfigTable(selfStruct, tableName); +} + +void WCDBRustStatementDropTableClassMethod(configIfExist, void* self) { + WCDBRustBridgeStruct(CPPStatementDropTable, self); + WCDBStatementDropTableConfigIfExists(selfStruct); +} \ No newline at end of file diff --git a/src/rust/cpp/winq/statement/StatementDropTableRust.h b/src/rust/cpp/winq/statement/StatementDropTableRust.h new file mode 100644 index 000000000..ddb0dbf47 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementDropTableRust.h @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementDropTableFuncName(funcName) WCDBRust(StatementDropTable, funcName) +#define WCDBRustStatementDropTableObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementDropTable, funcName, __VA_ARGS__) +#define WCDBRustStatementDropTableObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementDropTable, funcName) +#define WCDBRustStatementDropTableClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementDropTable, funcName) +#define WCDBRustStatementDropTableClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementDropTable, funcName, __VA_ARGS__) + +void* WCDBRustStatementDropTableClassMethodWithNoArg(create); +void WCDBRustStatementDropTableClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); +void WCDBRustStatementDropTableClassMethod(configTableName, void* self, const char* tableName); +void WCDBRustStatementDropTableClassMethod(configIfExist, void* self); \ No newline at end of file diff --git a/src/rust/cpp/winq/statement/StatementDropTriggerRust.c b/src/rust/cpp/winq/statement/StatementDropTriggerRust.c new file mode 100644 index 000000000..876ea77da --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementDropTriggerRust.c @@ -0,0 +1,51 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementDropTriggerRust.h" + +#include "StatementDropTriggerBridge.h" + +void* WCDBRustStatementDropTriggerClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementDropTriggerCreate().innerValue; +} + +void WCDBRustStatementDropTriggerClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementDropTrigger, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementDropTriggerConfigSchema2(selfStruct, schema_common); + // WCDBRustTryReleaseStringInCommonValue(schema); +} + +void WCDBRustStatementDropTriggerClassMethod(configTrigger, void* self, const char* triggerName) { + WCDBRustBridgeStruct(CPPStatementDropTrigger, self); + // WCDBRustGetStringCritical(triggerName); + WCDBStatementDropTriggerConfigTrigger(selfStruct, triggerName); + // WCDBRustReleaseStringCritical(triggerName); +} + +void WCDBRustStatementDropTriggerClassMethod(configIfExist, void* self) { + WCDBRustBridgeStruct(CPPStatementDropTrigger, self); + WCDBStatementDropTriggerConfigIfExists(selfStruct); +} diff --git a/src/rust/cpp/winq/statement/StatementDropTriggerRust.h b/src/rust/cpp/winq/statement/StatementDropTriggerRust.h new file mode 100644 index 000000000..95f3731b4 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementDropTriggerRust.h @@ -0,0 +1,42 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include "WCDBRust.h" + +#define WCDBRustStatementDropTriggerFuncName(funcName) WCDBRust(StatementDropTrigger, funcName) +#define WCDBRustStatementDropTriggerObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementDropTrigger, funcName, __VA_ARGS__) +#define WCDBRustStatementDropTriggerObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementDropTrigger, funcName) +#define WCDBRustStatementDropTriggerClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementDropTrigger, funcName) +#define WCDBRustStatementDropTriggerClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementDropTrigger, funcName, __VA_ARGS__) + +void* WCDBRustStatementDropTriggerClassMethodWithNoArg(createCppObj); +void WCDBRustStatementDropTriggerClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); +void WCDBRustStatementDropTriggerClassMethod(configTrigger, void* self, const char* triggerName); +void WCDBRustStatementDropTriggerClassMethod(configIfExist, void* self); diff --git a/src/rust/cpp/winq/statement/StatementDropViewRust.c b/src/rust/cpp/winq/statement/StatementDropViewRust.c new file mode 100644 index 000000000..139d84c7c --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementDropViewRust.c @@ -0,0 +1,51 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementDropViewRust.h" + +#include "StatementDropViewBridge.h" + +void* WCDBRustStatementDropViewClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementDropViewCreate().innerValue; +} + +void WCDBRustStatementDropViewClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementDropView, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementDropViewConfigSchema2(selfStruct, schema_common); + // WCDBRustTryReleaseStringInCommonValue(schema); +} + +void WCDBRustStatementDropViewClassMethod(configView, void* self, const char* viewName) { + WCDBRustBridgeStruct(CPPStatementDropView, self); + // WCDBRustGetStringCritical(viewName); + WCDBStatementDropViewConfigView(selfStruct, viewName); + // WCDBRustReleaseStringCritical(viewName); +} + +void WCDBRustStatementDropViewClassMethod(configIfExist, void* self) { + WCDBRustBridgeStruct(CPPStatementDropView, self); + WCDBStatementDropViewConfigIfExists(selfStruct); +} diff --git a/src/rust/cpp/winq/statement/StatementDropViewRust.h b/src/rust/cpp/winq/statement/StatementDropViewRust.h new file mode 100644 index 000000000..ab9420038 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementDropViewRust.h @@ -0,0 +1,42 @@ +// Created by chenqiuwen on 2023/6/11. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include "WCDBRust.h" + +#define WCDBRustStatementDropViewFuncName(funcName) WCDBRust(StatementDropView, funcName) +#define WCDBRustStatementDropViewObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementDropView, funcName, __VA_ARGS__) +#define WCDBRustStatementDropViewObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementDropView, funcName) +#define WCDBRustStatementDropViewClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementDropView, funcName) +#define WCDBRustStatementDropViewClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementDropView, funcName, __VA_ARGS__) + +void* WCDBRustStatementDropViewClassMethodWithNoArg(createCppObj); +void WCDBRustStatementDropViewClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); +void WCDBRustStatementDropViewClassMethod(configView, void* self, const char* viewName); +void WCDBRustStatementDropViewClassMethod(configIfExist, void* self); diff --git a/src/rust/cpp/winq/statement/StatementExplainRust.c b/src/rust/cpp/winq/statement/StatementExplainRust.c new file mode 100644 index 000000000..6a7469585 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementExplainRust.c @@ -0,0 +1,36 @@ +// +// Created by qiuwenchen on 2023/6/12. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementExplainRust.h" + +#include "StatementExplainBridge.h" + +void* WCDBRustStatementExplainClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementExplainCreate().innerValue; +} + +void WCDBRustStatementExplainClassMethod(explain, void* self, void* statement, bool queryPlan) { + WCDBRustBridgeStruct(CPPStatementExplain, self); + WCDBStatementExplain(selfStruct, (CPPObject*)statement, queryPlan); +} \ No newline at end of file diff --git a/src/rust/cpp/winq/statement/StatementExplainRust.h b/src/rust/cpp/winq/statement/StatementExplainRust.h new file mode 100644 index 000000000..a4d05474d --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementExplainRust.h @@ -0,0 +1,38 @@ +// Created by qiuwenchen on 2023/6/12. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include "WCDBRust.h" + +#define WCDBRustStatementExplainFuncName(funcName) WCDBRust(StatementExplain, funcName) +#define WCDBRustStatementExplainObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementExplain, funcName, __VA_ARGS__) +#define WCDBRustStatementExplainObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementExplain, funcName) +#define WCDBRustStatementExplainClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementExplain, funcName) +#define WCDBRustStatementExplainClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementExplain, funcName, __VA_ARGS__) + +void* WCDBRustStatementExplainClassMethodWithNoArg(createCppObj); +void WCDBRustStatementExplainClassMethod(explain, void* self, void* statement, bool queryPlan); diff --git a/src/rust/cpp/winq/statement/StatementInsertRust.c b/src/rust/cpp/winq/statement/StatementInsertRust.c new file mode 100644 index 000000000..1c5946215 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementInsertRust.c @@ -0,0 +1,108 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementInsertRust.h" + +#include "StatementInsertBridge.h" + +void* WCDBRustStatementInsertClassMethodWithNoArg(create) { + return (void*)WCDBStatementInsertCreate().innerValue; +} + +// void WCDBRustStatementInsertClassMethod(configWith, jlong self, jlongArray expressions) +//{ +// WCDBRustBridgeStruct(CPPStatementInsert, self); +// WCDBRustGetCppPointerArrayCritical(expressions); +// WCDBStatementInsertConfigWith( +// selfStruct, (const CPPCommonTableExpression*) expressionsArray, expressionsLength); +// WCDBRustReleaseCppPointerArrayCritical(expressions); +// } +// +// void WCDBRustStatementInsertClassMethod(configRecursive, jlong self) +//{ +// WCDBRustBridgeStruct(CPPStatementInsert, self); +// WCDBStatementInsertConfigRecursive(selfStruct); +// } + +void WCDBRustStatementInsertClassMethod(configTableName, void* self, const char* tableName) { + WCDBRustBridgeStruct(CPPStatementInsert, self); + WCDBStatementInsertConfigTable(selfStruct, tableName); +} + +// void WCDBRustStatementInsertClassMethod(configSchema, +// jlong self, +// WCDBRustObjectOrStringParameter(schema)) +//{ +// WCDBRustBridgeStruct(CPPStatementInsert, self); +// WCDBRustCreateObjectOrStringCommonValue(schema, true); +// WCDBStatementInsertConfigSchema2(selfStruct, schema_common); +// WCDBRustTryReleaseStringInCommonValue(schema); +// } +// +void WCDBRustStatementInsertClassMethod(configConflictAction, void* self, int action) { + WCDBRustBridgeStruct(CPPStatementInsert, self); + WCDBStatementInsertConfigConfiction(selfStruct, action); +} +// +// void WCDBRustStatementInsertClassMethod(configAs, jlong self, jstring alias) +//{ +// WCDBRustBridgeStruct(CPPStatementInsert, self); +// WCDBRustGetStringCritical(alias); +// WCDBStatementInsertConfigAlias(selfStruct, aliasString); +// WCDBRustReleaseStringCritical(alias); +//} + +void WCDBRustStatementInsertClassMethod(configColumns, + void* self, + WCDBRustObjectOrStringArrayParameter(columns)) { + WCDBRustBridgeStruct(CPPStatementInsert, self); + WCDBRustCreateObjectOrStringArrayCriticalWithAction( + columns, WCDBStatementInsertConfigColumns2(selfStruct, columns_commonArray)); +} + +void WCDBRustStatementInsertClassMethod(configValues, + void* self, + WCDBRustMultiTypeArrayParameter(value)) { + WCDBRustBridgeStruct(CPPStatementInsert, self); + WCDBRustCreateMultiTypeArray(value); + WCDBStatementInsertConfigValuesWithMultiTypeArray(selfStruct, valueArray); +} + +void WCDBRustStatementInsertClassMethod(configValuesWithBindParameters, void* self, int count) { + WCDBRustBridgeStruct(CPPStatementInsert, self); + WCDBStatementInsertConfigValuesWithBindParameters(selfStruct, count); +} + +void WCDBRustStatementInsertClassMethod(configSelect, void* self, void* select) { + WCDBRustBridgeStruct(CPPStatementInsert, self); + WCDBRustBridgeStruct(CPPStatementSelect, select); + WCDBStatementInsertConfigSelect(selfStruct, selectStruct); +} + +void WCDBRustStatementInsertClassMethod(configDefaultValues, void* self) { + WCDBRustBridgeStruct(CPPStatementInsert, self); + WCDBStatementInsertConfigDefaultValues(selfStruct); +} + +void WCDBRustStatementInsertClassMethod(configUpsert, void* self, void* upsert) { + WCDBRustBridgeStruct(CPPStatementInsert, self); + WCDBRustBridgeStruct(CPPUpsert, upsert); + WCDBStatementInsertConfigUpsert(selfStruct, upsertStruct); +} diff --git a/src/rust/cpp/winq/statement/StatementInsertRust.h b/src/rust/cpp/winq/statement/StatementInsertRust.h new file mode 100644 index 000000000..472b710cf --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementInsertRust.h @@ -0,0 +1,57 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementInsertFuncName(funcName) WCDBRust(StatementInsert, funcName) +#define WCDBRustStatementInsertObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementInsert, funcName, __VA_ARGS__) +#define WCDBRustStatementInsertObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementInsert, funcName) +#define WCDBRustStatementInsertClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementInsert, funcName) +#define WCDBRustStatementInsertClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementInsert, funcName, __VA_ARGS__) + +void* WCDBRustStatementInsertClassMethodWithNoArg(create); + +// void WCDBRustStatementInsertClassMethod(configWith, jlong self, jlongArray expressions); +// void WCDBRustStatementInsertClassMethod(configRecursive, jlong self); +void WCDBRustStatementInsertClassMethod(configTableName, void* self, const char* tableName); + +// void WCDBRustStatementInsertClassMethod(configSchema, +// jlong self, +// WCDBRustObjectOrStringParameter(schema)); +void WCDBRustStatementInsertClassMethod(configConflictAction, void* self, int action); + +// void WCDBRustStatementInsertClassMethod(configAs, jlong self, jstring alias); +void WCDBRustStatementInsertClassMethod(configColumns, + void* self, + WCDBRustObjectOrStringArrayParameter(columns)); + +void WCDBRustStatementInsertClassMethod(configValues, + void* self, + WCDBRustMultiTypeArrayParameter(value)); +void WCDBRustStatementInsertClassMethod(configValuesWithBindParameters, void* self, int count); +void WCDBRustStatementInsertClassMethod(configSelect, void* self, void* select); +void WCDBRustStatementInsertClassMethod(configDefaultValues, void* self); +void WCDBRustStatementInsertClassMethod(configUpsert, void* self, void* upsert); diff --git a/src/rust/cpp/winq/statement/StatementPragmaRust.c b/src/rust/cpp/winq/statement/StatementPragmaRust.c new file mode 100644 index 000000000..a2f6f154f --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementPragmaRust.c @@ -0,0 +1,70 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementPragmaRust.h" + +#include "StatementPragmaBridge.h" + +void* WCDBRustStatementPragmaClassMethodWithNoArg(create) { + return (void*)WCDBStatementPragmaCreate().innerValue; +} + +// void WCDBJNIStatementPragmaClassMethod(configSchema, +// jlong self, +// WCDBJNIObjectOrStringParameter(schema)) +// { +// WCDBJNIBridgeStruct(CPPStatementPragma, self); +// WCDBJNICreateObjectOrStringCommonValue(schema, true); +// WCDBStatementPragmaConfigSchema2(selfStruct, schema_common); +// WCDBJNITryReleaseStringInCommonValue(schema); +// } + +void WCDBRustStatementPragmaClassMethod(configPragma, void* self, void* pragma) { + WCDBRustBridgeStruct(CPPStatementPragma, self); + WCDBRustBridgeStruct(CPPPragma, pragma); + WCDBStatementPragmaConfigPragma(selfStruct, pragmaStruct); +} + +void WCDBRustStatementPragmaClassMethod(configToValue, + void* self, + WCDBRustCommonValueParameter(value)) { + WCDBRustBridgeStruct(CPPStatementPragma, self); + WCDBRustCreateCommonValueWithIsCritical(value, true); + WCDBStatementPragmaConfigToValue2(selfStruct, value_common); + // WCDBRustTryReleaseStringInCommonValue(value); +} +// +// void WCDBJNIStatementPragmaClassMethod(configToValue, jlong self, +// WCDBJNICommonValueParameter(value)) +// { +// WCDBJNIBridgeStruct(CPPStatementPragma, self); +// WCDBJNICreateCommonValue(value, true); +// WCDBStatementPragmaConfigToValue2(selfStruct, value_common); +// WCDBJNITryReleaseStringInCommonValue(value); +// } +// +// void WCDBJNIStatementPragmaClassMethod(configWithValue, jlong self, +// WCDBJNICommonValueParameter(value)) +// { +// WCDBJNIBridgeStruct(CPPStatementPragma, self); +// WCDBJNICreateCommonValue(value, true); +// WCDBStatementPragmaConfigWithValue2(selfStruct, value_common); +// WCDBJNITryReleaseStringInCommonValue(value); +// } diff --git a/src/rust/cpp/winq/statement/StatementPragmaRust.h b/src/rust/cpp/winq/statement/StatementPragmaRust.h new file mode 100644 index 000000000..8aa163a9c --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementPragmaRust.h @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementPragmaFuncName(funcName) WCDBRust(StatementPragma, funcName) +#define WCDBRustStatementPragmaObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementPragma, funcName, __VA_ARGS__) +#define WCDBRustStatementPragmaObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementPragma, funcName) +#define WCDBRustStatementPragmaClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementPragma, funcName) +#define WCDBRustStatementPragmaClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementPragma, funcName, __VA_ARGS__) + +void* WCDBRustStatementPragmaClassMethodWithNoArg(create); +// void WCDBJNIStatementPragmaClassMethod(configSchema, +// jlong self, +// WCDBJNIObjectOrStringParameter(schema)); +void WCDBRustStatementPragmaClassMethod(configPragma, void* self, void* pragma); +void WCDBRustStatementPragmaClassMethod(configToValue, + void* self, + WCDBRustCommonValueParameter(value)); + +// void WCDBJNIStatementPragmaClassMethod(configWithValue, +// jlong self, +// WCDBJNICommonValueParameter(value)); diff --git a/src/rust/cpp/winq/statement/StatementReindexRust.c b/src/rust/cpp/winq/statement/StatementReindexRust.c new file mode 100644 index 000000000..7732024a3 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementReindexRust.c @@ -0,0 +1,60 @@ +// Created by qiuwenchen on 2023/6/12. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementReindexRust.h" + +#include "StatementReindexBridge.h" + +void* WCDBRustStatementReindexClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementReIndexCreate().innerValue; +} + +void WCDBRustStatementReindexClassMethod(configCollation, void* self, const char* collation) { + WCDBRustBridgeStruct(CPPStatementReIndex, self); + // WCDBRustGetStringCritical(collation); + WCDBStatementReIndexConfigCollation(selfStruct, collation); + // WCDBRustReleaseStringCritical(collation); +} + +void WCDBRustStatementReindexClassMethod(configTable, void* self, const char* name) { + WCDBRustBridgeStruct(CPPStatementReIndex, self); + // WCDBRustGetStringCritical(name); + WCDBStatementReIndexConfigTable(selfStruct, name); + // WCDBRustReleaseStringCritical(name); +} + +void WCDBRustStatementReindexClassMethod(configIndex, void* self, const char* name) { + WCDBRustBridgeStruct(CPPStatementReIndex, self); + // WCDBRustGetStringCritical(name); + WCDBStatementReIndexConfigIndex(selfStruct, name); + // WCDBRustReleaseStringCritical(name); +} + +void WCDBRustStatementReindexClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementReIndex, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementReIndexConfigSchema2(selfStruct, schema_common); + // WCDBRustTryReleaseStringInCommonValue(schema); +} diff --git a/src/rust/cpp/winq/statement/StatementReindexRust.h b/src/rust/cpp/winq/statement/StatementReindexRust.h new file mode 100644 index 000000000..f9a7f9235 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementReindexRust.h @@ -0,0 +1,45 @@ +// Created by qiuwenchen on 2023/6/12. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementReindexFuncName(funcName) WCDBRust(StatementReindex, funcName) +#define WCDBRustStatementReindexObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementReindex, funcName, __VA_ARGS__) +#define WCDBRustStatementReindexObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementReindex, funcName) +#define WCDBRustStatementReindexClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementReindex, funcName) +#define WCDBRustStatementReindexClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementReindex, funcName, __VA_ARGS__) + +void* WCDBRustStatementReindexClassMethodWithNoArg(createCppObj); + +void WCDBRustStatementReindexClassMethod(configCollation, void* self, const char* collation); +void WCDBRustStatementReindexClassMethod(configTable, void* self, const char* name); +void WCDBRustStatementReindexClassMethod(configIndex, void* self, const char* name); +void WCDBRustStatementReindexClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); \ No newline at end of file diff --git a/src/rust/cpp/winq/statement/StatementReleaseRust.c b/src/rust/cpp/winq/statement/StatementReleaseRust.c new file mode 100644 index 000000000..b8be67870 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementReleaseRust.c @@ -0,0 +1,37 @@ +// Created by qiuwenchen on 2023/6/12. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementReleaseRust.h" + +#include "StatementReleaseBridge.h" + +void* WCDBRustStatementReleaseClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementReleaseCreate().innerValue; +} + +void WCDBRustStatementReleaseClassMethod(configSavepoint, void* self, const char* savepoint) { + WCDBRustBridgeStruct(CPPStatementRelease, self); + // WCDBRustGetStringCritical(savepoint); + WCDBStatementReleaseConfigSavepoint(selfStruct, savepoint); + // WCDBRustReleaseStringCritical(savepoint); +} diff --git a/src/rust/cpp/winq/statement/StatementReleaseRust.h b/src/rust/cpp/winq/statement/StatementReleaseRust.h new file mode 100644 index 000000000..2bdb47f8c --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementReleaseRust.h @@ -0,0 +1,39 @@ +// Created by qiuwenchen on 2023/6/12. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementReleaseFuncName(funcName) WCDBRust(StatementRelease, funcName) +#define WCDBRustStatementReleaseObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementRelease, funcName, __VA_ARGS__) +#define WCDBRustStatementReleaseObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementRelease, funcName) +#define WCDBRustStatementReleaseClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementRelease, funcName) +#define WCDBRustStatementReleaseClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementRelease, funcName, __VA_ARGS__) + +void* WCDBRustStatementReleaseClassMethodWithNoArg(createCppObj); +void WCDBRustStatementReleaseClassMethod(configSavepoint, void* self, const char* savepoint); diff --git a/src/rust/cpp/winq/statement/StatementRollbackRust.c b/src/rust/cpp/winq/statement/StatementRollbackRust.c new file mode 100644 index 000000000..c46d03e62 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementRollbackRust.c @@ -0,0 +1,37 @@ +// Created by qiuwenchen on 2023/6/12. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementRollbackRust.h" + +#include "StatementRollbackBridge.h" + +void* WCDBRustStatementRollbackClassMethodWithNoArg(createCppObj) { + return (void*)WCDBStatementRollbackCreate().innerValue; +} + +void WCDBRustStatementRollbackClassMethod(configSavepoint, void* self, const char* savepoint) { + WCDBRustBridgeStruct(CPPStatementRollback, self); + // WCDBRustGetStringCritical(savepoint); + WCDBStatementRollbackConfigSavepoint(selfStruct, savepoint); + // WCDBRustReleaseStringCritical(savepoint); +} \ No newline at end of file diff --git a/src/rust/cpp/winq/statement/StatementRollbackRust.h b/src/rust/cpp/winq/statement/StatementRollbackRust.h new file mode 100644 index 000000000..0839d08f8 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementRollbackRust.h @@ -0,0 +1,39 @@ +// Created by qiuwenchen on 2023/6/12. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementRollbackFuncName(funcName) WCDBRust(StatementRollback, funcName) +#define WCDBRustStatementRollbackObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementRollback, funcName, __VA_ARGS__) +#define WCDBRustStatementRollbackObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementRollback, funcName) +#define WCDBRustStatementRollbackClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementRollback, funcName) +#define WCDBRustStatementRollbackClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementRollback, funcName, __VA_ARGS__) + +void* WCDBRustStatementRollbackClassMethodWithNoArg(createCppObj); +void WCDBRustStatementRollbackClassMethod(configSavepoint, void* self, const char* savepoint); diff --git a/src/rust/cpp/winq/statement/StatementSelectRust.c b/src/rust/cpp/winq/statement/StatementSelectRust.c new file mode 100644 index 000000000..93e4e544b --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementSelectRust.c @@ -0,0 +1,146 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementSelectRust.h" + +#include "StatementSelectBridge.h" + +void* WCDBRustStatementSelectClassMethodWithNoArg(create) { + return (void*)WCDBStatementSelectCreate().innerValue; +} + +// void WCDBRustStatementSelectClassMethod(configWith, jlong self, jlongArray expressions) +//{ +// WCDBRustBridgeStruct(CPPStatementSelect, self); +// WCDBRustGetCppPointerArrayCritical(expressions); +// WCDBStatementSelectConfigWith( +// selfStruct, (const CPPCommonTableExpression*) expressionsArray, expressionsLength); +// WCDBRustReleaseCppPointerArrayCritical(expressions); +// } +// +// void WCDBRustStatementSelectClassMethod(configRecursive, jlong self) +//{ +// WCDBRustBridgeStruct(CPPStatementSelect, self); +// WCDBStatementSelectConfigRecursive(selfStruct); +// } +// +void WCDBRustStatementSelectClassMethod(configResultColumns, + void* self, + WCDBRustMultiTypeArrayParameter(resultColumns)) { + WCDBRustBridgeStruct(CPPStatementSelect, self); + WCDBRustCreateMultiTypeArray(resultColumns); + WCDBStatementSelectConfigResultColumns2(selfStruct, resultColumnsArray); + // WCDBRustReleaseMultiTypeArray(resultColumns); +} + +// +// void WCDBRustStatementSelectClassMethod(configDistiction, jlong self) +//{ +// WCDBRustBridgeStruct(CPPStatementSelect, self); +// WCDBStatementSelectConfigDistinct(selfStruct); +//} +// +void WCDBRustStatementSelectClassMethod(configTableOrSubqueries, + void* self, + WCDBRustMultiTypeArrayParameter(tableOrSubqueries)) { + WCDBRustBridgeStruct(CPPStatementSelect, self); + WCDBRustCreateMultiTypeArray(tableOrSubqueries); + WCDBStatementSelectConfigFromTableOrSubqueries2(selfStruct, tableOrSubqueriesArray); + // WCDBRustReleaseMultiTypeArray(tableOrSubqueries); +} + +// +void WCDBRustStatementSelectClassMethod(configCondition, void* self, void* condition) { + WCDBRustBridgeStruct(CPPStatementSelect, self); + WCDBRustBridgeStruct(CPPExpression, condition); + WCDBStatementSelectConfigWhere(selfStruct, conditionStruct); +} + +void WCDBRustStatementSelectClassMethod(configGroups, + void* self, + WCDBRustMultiTypeArrayParameter(groups)) { + WCDBRustBridgeStruct(CPPStatementSelect, self); + WCDBRustCreateMultiTypeArray(groups); + WCDBStatementSelectConfigGroups2(selfStruct, groupsArray); +} +// +// void WCDBRustStatementSelectClassMethod(configHaving, jlong self, jlong expression) +//{ +// WCDBRustBridgeStruct(CPPStatementSelect, self); +// WCDBRustBridgeStruct(CPPExpression, expression); +// WCDBStatementSelectConfigHaving(selfStruct, expressionStruct); +//} +// +void WCDBRustStatementSelectClassMethod(configUnion, void* self) { + WCDBRustBridgeStruct(CPPStatementSelect, self); + WCDBStatementSelectConfigUnion(selfStruct); +} + +void WCDBRustStatementSelectClassMethod(configUnionAll, void* self) { + WCDBRustBridgeStruct(CPPStatementSelect, self); + WCDBStatementSelectConfigUnionAll(selfStruct); +} +// +// void WCDBRustStatementSelectClassMethod(configIntersect, jlong self) +//{ +// WCDBRustBridgeStruct(CPPStatementSelect, self); +// WCDBStatementSelectConfigIntersect(selfStruct); +//} +// +// void WCDBRustStatementSelectClassMethod(configExcept, jlong self) +//{ +// WCDBRustBridgeStruct(CPPStatementSelect, self); +// WCDBStatementSelectConfigExcept(selfStruct); +//} + +void WCDBRustStatementSelectClassMethod(configOrders, void* self, void** orders, int ordersLength) { + WCDBRustBridgeStruct(CPPStatementSelect, self); + WCDBStatementSelectConfigOrders(selfStruct, (const CPPOrderingTerm*)orders, ordersLength); +} + +// +// void WCDBRustStatementSelectClassMethod( +// configLimitRange, jlong self, jint fromType, jlong from, jint toType, jlong to) +//{ +// WCDBRustBridgeStruct(CPPStatementSelect, self); +// CPPCommonValue from_common; +// from_common.type = fromType; +// from_common.intValue = from; +// CPPCommonValue to_common; +// to_common.type = toType; +// to_common.intValue = to; +// WCDBStatementSelectConfigLimitRange2(selfStruct, from_common, to_common); +//} + +void WCDBRustStatementSelectClassMethod(configLimitCount, void* self, int type, long limit) { + WCDBRustBridgeStruct(CPPStatementSelect, self); + CPPCommonValue limit_common; + limit_common.type = type; + limit_common.intValue = limit; + WCDBStatementSelectConfigLimitCount2(selfStruct, limit_common); +} + +void WCDBRustStatementSelectClassMethod(configOffset, void* self, int type, long offset) { + WCDBRustBridgeStruct(CPPStatementSelect, self); + CPPCommonValue offset_common; + offset_common.type = type; + offset_common.intValue = offset; + WCDBStatementSelectConfigOffset2(selfStruct, offset_common); +} diff --git a/src/rust/cpp/winq/statement/StatementSelectRust.h b/src/rust/cpp/winq/statement/StatementSelectRust.h new file mode 100644 index 000000000..20c581b4e --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementSelectRust.h @@ -0,0 +1,62 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementSelectFuncName(funcName) WCDBRust(StatementSelect, funcName) +#define WCDBRustStatementSelectObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementSelect, funcName, __VA_ARGS__) +#define WCDBRustStatementSelectObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementSelect, funcName) +#define WCDBRustStatementSelectClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementSelect, funcName) +#define WCDBRustStatementSelectClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementSelect, funcName, __VA_ARGS__) + +void* WCDBRustStatementSelectClassMethodWithNoArg(create); + +// void WCDBRustStatementSelectClassMethod(configWith, jlong self, jlongArray expressions); +// void WCDBRustStatementSelectClassMethod(configRecursive, jlong self); +// +void WCDBRustStatementSelectClassMethod(configResultColumns, + void* self, + WCDBRustMultiTypeArrayParameter(resultColumns)); + +// void WCDBRustStatementSelectClassMethod(configDistiction, jlong self); +void WCDBRustStatementSelectClassMethod(configTableOrSubqueries, + void* self, + WCDBRustMultiTypeArrayParameter(tableOrSubqueries)); + +void WCDBRustStatementSelectClassMethod(configCondition, void* self, void* condition); +void WCDBRustStatementSelectClassMethod(configGroups, + void* self, + WCDBRustMultiTypeArrayParameter(groups)); +// void WCDBRustStatementSelectClassMethod(configHaving, jlong self, jlong expression); +void WCDBRustStatementSelectClassMethod(configUnion, void* self); +void WCDBRustStatementSelectClassMethod(configUnionAll, void* self); +// void WCDBRustStatementSelectClassMethod(configIntersect, jlong self); +// void WCDBRustStatementSelectClassMethod(configExcept, jlong self); +void WCDBRustStatementSelectClassMethod(configOrders, void* self, void** orders, int ordersLength); +// void WCDBRustStatementSelectClassMethod( +// configLimitRange, jlong self, jint fromType, jlong from, jint toType, jlong to); +void WCDBRustStatementSelectClassMethod(configLimitCount, void* self, int type, long limit); +void WCDBRustStatementSelectClassMethod(configOffset, void* self, int type, long offset); diff --git a/src/rust/cpp/winq/statement/StatementUpdateRust.c b/src/rust/cpp/winq/statement/StatementUpdateRust.c new file mode 100644 index 000000000..13b909184 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementUpdateRust.c @@ -0,0 +1,136 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementUpdateRust.h" + +#include "StatementUpdateBridge.h" + +void* WCDBRustStatementUpdateClassMethodWithNoArg(create) { + return (void*)WCDBStatementUpdateCreate().innerValue; +} + +void WCDBRustStatementUpdateClassMethod(configWith, + void* self, + void** expressions, + int expressionsLength) { + WCDBRustBridgeStruct(CPPStatementUpdate, self); + WCDBStatementUpdateConfigWith(selfStruct, (const CPPCommonTableExpression*)expressions, + expressionsLength); +} + +void WCDBRustStatementUpdateClassMethod(configRecursive, void* self) { + WCDBRustBridgeStruct(CPPStatementUpdate, self); + WCDBStatementUpdateConfigRecursive(selfStruct); +} + +void WCDBRustStatementUpdateClassMethod(configTable, + void* self, + WCDBRustObjectOrStringParameter(table)) { + WCDBRustBridgeStruct(CPPStatementUpdate, self); + WCDBRustCreateObjectOrStringCommonValue(table, true); + WCDBStatementUpdateConfigTable2(selfStruct, table_common); +} + +void WCDBRustStatementUpdateClassMethod(configConfliction, void* self, int action) { + WCDBRustBridgeStruct(CPPStatementUpdate, self); + WCDBStatementUpdateConfigConfiction(selfStruct, action); +} + +void WCDBRustStatementUpdateClassMethod(configColumns, + void* self, + WCDBRustObjectOrStringArrayParameter(columns)) { + WCDBRustBridgeStruct(CPPStatementUpdate, self); + WCDBRustCreateObjectOrStringArrayCriticalWithAction( + columns, WCDBStatementUpdateConfigColumns2(selfStruct, columns_commonArray)); +} + +void WCDBRustStatementUpdateClassMethod(configValue, + void* self, + WCDBRustCommonValueParameter(value)) { + WCDBRustBridgeStruct(CPPStatementUpdate, self); + WCDBRustCreateCommonValue(value); + WCDBStatementUpdateConfigValue2(selfStruct, value_common); +} + +// void WCDBRustStatementUpdateClassMethod(configColumnsToValues, +// void* self, +// WCDBRustObjectOrStringArrayParameter(columns), +// WCDBRustMultiTypeArrayParameter(values)) +//{ +// WCDBRustBridgeStruct(CPPStatementUpdate, self); +// WCDBRustCreateMultiTypeArray(values); +// WCDBRustCreateObjectOrStringArrayCriticalWithAction( +// columns, +// WCDBStatementUpdateConfigColumnsToValues(selfStruct, columns_commonArray, valuesArray)); +// WCDBRustReleaseMultiTypeArray(values); +// } + +void WCDBRustStatementUpdateClassMethod(configColumnsToBindParameters, + void* self, + WCDBRustObjectOrStringArrayParameter(columns)) { + WCDBRustBridgeStruct(CPPStatementUpdate, self); + WCDBRustCreateObjectOrStringArrayCriticalWithAction( + columns, WCDBStatementUpdateConfigColumnsToBindParameters(selfStruct, columns_commonArray)); +} + +void WCDBRustStatementUpdateClassMethod(configCondition, void* self, void* condition) { + WCDBRustBridgeStruct(CPPStatementUpdate, self); + WCDBRustBridgeStruct(CPPExpression, condition); + WCDBStatementUpdateConfigCondition(selfStruct, conditionStruct); +} + +void WCDBRustStatementUpdateClassMethod(configOrders, void* self, void** orders, size_t len) { + WCDBRustBridgeStruct(CPPStatementUpdate, self); + // WCDBRustGetCppPointerArrayCritical(orders); + WCDBStatementUpdateConfigOrders(selfStruct, (const CPPOrderingTerm*)orders, (int)len); + // WCDBRustReleaseCppPointerArrayCritical(orders); +} + +void WCDBRustStatementUpdateClassMethod(configLimitRange, + void* self, + int fromType, + long from, + int toType, + long to) { + WCDBRustBridgeStruct(CPPStatementUpdate, self); + CPPCommonValue from_common; + from_common.type = fromType; + from_common.intValue = from; + CPPCommonValue to_common; + to_common.type = toType; + to_common.intValue = to; + WCDBStatementUpdateConfigLimitRange2(selfStruct, from_common, to_common); +} + +void WCDBRustStatementUpdateClassMethod(configLimitCount, void* self, int type, long limit) { + WCDBRustBridgeStruct(CPPStatementUpdate, self); + CPPCommonValue limit_common; + limit_common.type = type; + limit_common.intValue = limit; + WCDBStatementUpdateConfigLimitCount2(selfStruct, limit_common); +} + +void WCDBRustStatementUpdateClassMethod(configOffset, void* self, int type, long offset) { + WCDBRustBridgeStruct(CPPStatementUpdate, self); + CPPCommonValue offset_common; + offset_common.type = type; + offset_common.intValue = offset; + WCDBStatementUpdateConfigOffset2(selfStruct, offset_common); +} diff --git a/src/rust/cpp/winq/statement/StatementUpdateRust.h b/src/rust/cpp/winq/statement/StatementUpdateRust.h new file mode 100644 index 000000000..235e66aff --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementUpdateRust.h @@ -0,0 +1,70 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementUpdateFuncName(funcName) WCDBRust(StatementUpdate, funcName) +#define WCDBRustStatementUpdateObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementUpdate, funcName, __VA_ARGS__) +#define WCDBRustStatementUpdateObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementUpdate, funcName) +#define WCDBRustStatementUpdateClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementUpdate, funcName) +#define WCDBRustStatementUpdateClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementUpdate, funcName, __VA_ARGS__) + +void* WCDBRustStatementUpdateClassMethodWithNoArg(create); + +void WCDBRustStatementUpdateClassMethod(configWith, + void* self, + void** expressions, + int expressionsLength); +void WCDBRustStatementUpdateClassMethod(configRecursive, void* self); + +void WCDBRustStatementUpdateClassMethod(configTable, + void* self, + WCDBRustObjectOrStringParameter(table)); + +void WCDBRustStatementUpdateClassMethod(configConfliction, void* self, int action); +void WCDBRustStatementUpdateClassMethod(configColumns, + void* self, + WCDBRustObjectOrStringArrayParameter(columns)); +void WCDBRustStatementUpdateClassMethod(configValue, + void* self, + WCDBRustCommonValueParameter(value)); +// void WCDBRustStatementUpdateClassMethod(configColumnsToValues, +// void* self, +// WCDBRustObjectOrStringArrayParameter(columns), +// WCDBRustMultiTypeArrayParameter(values)); +void WCDBRustStatementUpdateClassMethod(configColumnsToBindParameters, + void* self, + WCDBRustObjectOrStringArrayParameter(columns)); +void WCDBRustStatementUpdateClassMethod(configCondition, void* self, void* condition); +void WCDBRustStatementUpdateClassMethod(configOrders, void* self, void** orders, size_t len); +void WCDBRustStatementUpdateClassMethod(configLimitRange, + void* self, + int fromType, + long from, + int toType, + long to); +void WCDBRustStatementUpdateClassMethod(configLimitCount, void* self, int type, long limit); +void WCDBRustStatementUpdateClassMethod(configOffset, void* self, int type, long offset); diff --git a/src/rust/cpp/winq/statement/StatementVacuumRust.c b/src/rust/cpp/winq/statement/StatementVacuumRust.c new file mode 100644 index 000000000..558124143 --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementVacuumRust.c @@ -0,0 +1,41 @@ +// Created by qiuwenchen on 2023/6/13. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StatementVacuumRust.h" + +#include "StatementVacuumBridge.h" + +void* WCDBRustStatementVacuumClassMethodWithNoArg(createCppObj) { + CPPStatementVacuum vacuum = WCDBStatementVacuumCreate(); + WCDBStatementVacuumConfigAll(vacuum); + return (void*)vacuum.innerValue; +} + +void WCDBRustStatementVacuumClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)) { + WCDBRustBridgeStruct(CPPStatementVacuum, self); + WCDBRustCreateObjectOrStringCommonValue(schema, true); + WCDBStatementVacuumConfigSchema2(selfStruct, schema_common); + // WCDBRustTryReleaseStringInCommonValue(schema); +} \ No newline at end of file diff --git a/src/rust/cpp/winq/statement/StatementVacuumRust.h b/src/rust/cpp/winq/statement/StatementVacuumRust.h new file mode 100644 index 000000000..83f4d3dfa --- /dev/null +++ b/src/rust/cpp/winq/statement/StatementVacuumRust.h @@ -0,0 +1,41 @@ +// Created by qiuwenchen on 2023/6/13. +// + +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "WCDBRust.h" + +#define WCDBRustStatementVacuumFuncName(funcName) WCDBRust(StatementVacuum, funcName) +#define WCDBRustStatementVacuumObjectMethod(funcName, ...) \ + WCDBRustObjectMethod(StatementVacuum, funcName, __VA_ARGS__) +#define WCDBRustStatementVacuumObjectMethodWithNoArg(funcName) \ + WCDBRustObjectMethodWithNoArg(StatementVacuum, funcName) +#define WCDBRustStatementVacuumClassMethodWithNoArg(funcName) \ + WCDBRustClassMethodWithNoArg(StatementVacuum, funcName) +#define WCDBRustStatementVacuumClassMethod(funcName, ...) \ + WCDBRustClassMethod(StatementVacuum, funcName, __VA_ARGS__) + +void* WCDBRustStatementVacuumClassMethodWithNoArg(createCppObj); +void WCDBRustStatementVacuumClassMethod(configSchema, + void* self, + WCDBRustObjectOrStringParameter(schema)); diff --git a/src/rust/examples/Cargo.toml b/src/rust/examples/Cargo.toml new file mode 100644 index 000000000..3a2b3b12e --- /dev/null +++ b/src/rust/examples/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "examples" +version = "0.1.0" +edition = "2021" + +[dependencies] +wcdb = { path = "../wcdb" } +wcdb_derive = { path = "../wcdb_derive" } +once_cell = "1.19.0" +lazy_static = "1.5.0" + +[dev-dependencies] +rand = "0.8.5" +criterion = { version = "0.4", features = ["html_reports"] } +phf = { version = "0.11", features = ["macros"] } + +[[bench]] +name = "db_performance_test_case" +harness = false + +[[example]] +name = "demo" +path = "example/main.rs" diff --git a/src/rust/examples/benches/db_performance_test_case.rs b/src/rust/examples/benches/db_performance_test_case.rs new file mode 100644 index 000000000..1130cccb3 --- /dev/null +++ b/src/rust/examples/benches/db_performance_test_case.rs @@ -0,0 +1,216 @@ +use criterion::{black_box, criterion_group, criterion_main, Bencher, Criterion}; +use rand::prelude::SliceRandom; +use std::sync::Arc; +use std::time::{SystemTime, UNIX_EPOCH}; +use wcdb::base::value::Value; +use wcdb::base::wcdb_exception::WCDBResult; +use wcdb::core::database::Database; +use wcdb::core::handle_operation::HandleOperationTrait; +use wcdb::core::handle_orm_operation::HandleORMOperationTrait; +use wcdb::core::table::Table; +use wcdb_derive::WCDBTableCoding; + +use wcdb::core::table_orm_operation::TableORMOperationTrait; +use wcdb::winq::column::Column; +use wcdb::winq::expression_operable::ExpressionOperableTrait; +use wcdb::winq::identifier::IdentifierTrait; +use wcdb::winq::statement_create_index::StatementCreateIndex; +use wcdb::winq::statement_delete::StatementDelete; +use wcdb::winq::statement_select::StatementSelect; +use wcdb::winq::statement_update::StatementUpdate; + +fn current_time_millis() -> u128 { + let now = SystemTime::now(); + now.duration_since(UNIX_EPOCH) + .expect("Time went backwards") + .as_millis() +} + +pub fn string_by_length(length: i32) -> String { + let chars: Vec = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + .chars() + .collect(); + let mut rng = rand::thread_rng(); + (0..length) + .map(|_| *chars.choose(&mut rng).unwrap()) + .collect() +} + +#[derive(WCDBTableCoding)] +#[WCDBTable] +pub struct FriendProfileTable { + #[WCDBField(is_primary = true)] + pub user_id: String, + #[WCDBField] + pub remark: String, + #[WCDBField(default(i32_value = 1))] + pub friend_type: i32, + #[WCDBField] + pub is_top: bool, + #[WCDBField] + pub add_time: i64, +} + +impl FriendProfileTable { + pub fn new(user_id: &str, time: i64) -> Self { + FriendProfileTable { + user_id: user_id.to_string(), + remark: "remark1".to_string(), + friend_type: 2, + is_top: false, + add_time: time, + } + } +} + +fn insert_data_performance( + table: &Arc>, + size: i32, +) { + let mut vec: Vec = Vec::with_capacity(100); + for _ in 0..size { + vec.push(FriendProfileTable::new( + &*string_by_length(10), + current_time_millis() as i64, + )); + } + let _ = table.insert_objects(vec, Some(DbFriendProfileTable::all_fields())); +} + +fn select_data_performance(database: &Database, size: i64) { + let column_vec: Vec = vec![ + Column::new("user_id", None), + Column::new("remark", None), + Column::new("friend_type", None), + Column::new("is_top", None), + Column::new("add_time", None), + ]; + let binding = StatementSelect::new(); + let condition = Column::new("add_time", None).gt(1); + let statement = binding + .select(&column_vec) + .from(vec!["FriendProfileTable"]) + .where_(&condition) + .limit(size); + // SELECT user_id, remark, friend_type, is_top, add_time FROM FriendProfileTable WHERE add_time > 1 LIMIT 1 + let _: WCDBResult>> = database.get_all_rows_from_statement(statement); +} + +fn update_data_performance(database: &Database, size: i64) { + let column = Column::new("is_top", None); + let column_vec: Vec<&Column> = vec![&column]; + let statement = StatementUpdate::new(); + let condition = Column::new("is_top", None) + .not_eq(true) + .and(&Column::new("add_time", None).gt(1)); + statement + .update("FriendProfileTable") + .set(column_vec) + .to(true) + .where_(&condition) + .limit(size); + // UPDATE FriendProfileTable SET is_top = TRUE WHERE (is_top != TRUE) AND (add_time > 1) LIMIT 1 + let _ = database.execute(&statement); +} + +fn delete_data_performance(database: &Database, size: i64) { + let statement = StatementDelete::new(); + let condition = Column::new("add_time", None).gt(1); + statement + .delete_from("FriendProfileTable") + .where_(&condition) + .limit(size); + // DELETE FROM FriendProfileTable WHERE add_time > 1 LIMIT 1 + let _ = database.execute(&statement); +} + +fn index_data_performance(database: &Database) { + let statement_create_index = StatementCreateIndex::new(); + let column1 = Column::new("add_time", None); + let statement = statement_create_index + .create_index("add_time_index") + .on("FriendProfileTable") + .indexed_by(vec![&column1]); + // CREATE INDEX add_time_index ON FriendProfileTable(add_time) + database.execute(statement).unwrap(); +} + +fn benchmark_function(c: &mut Criterion) { + { + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database.remove_files().unwrap(); + } + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database + .create_table("FriendProfileTable", &*DB_FRIEND_PROFILE_TABLE_INSTANCE) + .unwrap(); + let conversation_table = + database.get_table("FriendProfileTable", &*DB_FRIEND_PROFILE_TABLE_INSTANCE); + + // 插入测试 + let mut group = c.benchmark_group("db-performance-example"); + group.significance_level(0.05).sample_size(10); + group.bench_function("insert_1", |b: &mut Bencher| { + b.iter(|| insert_data_performance(black_box(&conversation_table.clone()), black_box(1))) + }); + group.bench_function("insert_10k", |b: &mut Bencher| { + b.iter(|| insert_data_performance(black_box(&conversation_table.clone()), black_box(1000))) + }); + group.bench_function("insert_1m", |b: &mut Bencher| { + b.iter(|| { + insert_data_performance(black_box(&conversation_table.clone()), black_box(1000000)) + }) + }); + + // 查询测试 select_data_performance + group.bench_function("select_1", |b: &mut Bencher| { + b.iter(|| select_data_performance(black_box(&database), black_box(1))) + }); + group.bench_function("select_10k", |b: &mut Bencher| { + b.iter(|| select_data_performance(black_box(&database), black_box(10000))) + }); + group.bench_function("select_1m", |b: &mut Bencher| { + b.iter(|| select_data_performance(black_box(&database), black_box(1000000))) + }); + + // 修改测试 + group.bench_function("update_1", |b: &mut Bencher| { + b.iter(|| update_data_performance(black_box(&database), black_box(1))) + }); + group.bench_function("update_10k", |b: &mut Bencher| { + b.iter(|| update_data_performance(black_box(&database), black_box(10000))) + }); + group.bench_function("update_1m", |b: &mut Bencher| { + b.iter(|| update_data_performance(black_box(&database), black_box(1000000))) + }); + + // 创建索引 + index_data_performance(&database); + group.bench_function("index_select_1", |b: &mut Bencher| { + b.iter(|| select_data_performance(black_box(&database), black_box(1))) + }); + group.bench_function("index_select_10k", |b: &mut Bencher| { + b.iter(|| select_data_performance(black_box(&database), black_box(10000))) + }); + group.bench_function("index_select_1m", |b: &mut Bencher| { + b.iter(|| select_data_performance(black_box(&database), black_box(1000000))) + }); + + // 删除测试 + group.bench_function("delete_1", |b: &mut Bencher| { + b.iter(|| delete_data_performance(black_box(&database), black_box(1))) + }); + group.bench_function("delete_10k", |b: &mut Bencher| { + b.iter(|| delete_data_performance(black_box(&database), black_box(10000))) + }); + group.bench_function("delete_1m", |b: &mut Bencher| { + b.iter(|| update_data_performance(black_box(&database), black_box(1000000))) + }); + + group.finish(); + database.remove_files().unwrap(); + database.close(Some(|| {})); +} + +criterion_group!(benches, benchmark_function); +criterion_main!(benches); diff --git a/src/rust/examples/example/main.rs b/src/rust/examples/example/main.rs new file mode 100644 index 000000000..b6b0d737c --- /dev/null +++ b/src/rust/examples/example/main.rs @@ -0,0 +1,101 @@ +use wcdb::base::wcdb_exception::WCDBException; +use wcdb::core::database::{Database, PerformanceInfo}; +use wcdb::core::handle_orm_operation::HandleORMOperationTrait; +use wcdb::winq::identifier::IdentifierTrait; +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding)] +#[WCDBTable( + multi_indexes(name = "specifiedNameIndex", columns = ["item_i32", "message_id"]), +)] +pub struct TableMessageBox { + #[WCDBField] + item_bool: bool, + #[WCDBField] + item_byte: i8, + #[WCDBField] + item_short: i16, + #[WCDBField] + item_i32: i32, + #[WCDBField(column_name = "message_id")] + item_i64: i64, + #[WCDBField] + item_float: f32, + #[WCDBField] + item_double: f64, + #[WCDBField] + item_text: String, + #[WCDBField] + item_blob: Vec, + #[WCDBField] + item_blob_opt: Option>, +} + +impl TableMessageBox { + pub fn new() -> Self { + Self { + item_bool: false, + item_byte: 1, + item_short: 2, + item_i32: 32, + item_i64: 64, + item_float: 32.1f32, + item_double: 64.1f64, + item_text: "hello".to_string(), + item_blob: vec![1, 2, 3, 4, 5], + item_blob_opt: Some(vec![6, 7, 8, 9, 10]), + } + } +} + +fn main() { + global_trace(); + let db = Database::new("./target/tmp/test.db", None); + db.create_table("rct_message_box", &*DB_TABLE_MESSAGE_BOX_INSTANCE) + .unwrap(); + test_func(&db); +} + +fn global_trace() { + let ret = Database::global_trace_sql(Some( + |tag: i64, path: String, handle_id: i64, sql: String, info: String| { + println!( + "global_trace_sql tag: {}, path: {}, handle_id: {}, info: {}, sql: {}", + tag, path, handle_id, info, sql + ); + }, + )); + assert!(ret.is_ok()); + + let ret = Database::global_trace_exception(Some(|exception: WCDBException| { + println!("global_trace_exception: {}", exception.message()) + })); + assert!(ret.is_ok()); + + let ret = Database::global_trace_performance(Some( + |tag: i64, path: String, handle_id: i64, sql: String, info: PerformanceInfo| { + println!( + "global_trace_performance tag: {}, path: {}, handle_id: {}, sql: {}, info: {:?}", + tag, path, handle_id, sql, info + ); + }, + )); + assert!(ret.is_ok()); +} + +fn test_func(db: &Database) { + let msg_box = TableMessageBox::new(); + db.insert_object(msg_box, DbTableMessageBox::all_fields(), "rct_message_box") + .unwrap(); + + let msg_box_opt = db + .get_first_object( + DbTableMessageBox::all_fields(), + "rct_message_box", + None, + None, + None, + ) + .unwrap(); + println!("qxb test_func"); +} diff --git a/src/rust/examples/tests/base/base_test_case.rs b/src/rust/examples/tests/base/base_test_case.rs new file mode 100644 index 000000000..f6f8a648f --- /dev/null +++ b/src/rust/examples/tests/base/base_test_case.rs @@ -0,0 +1,76 @@ +use lazy_static::lazy_static; +use std::fmt::Display; +use std::time::Duration; +use std::{env, thread}; +use wcdb::base::wcdb_exception::WCDBResult; +use wcdb::core::database::Database; + +lazy_static! { + static ref GLOBAL_SETUP: () = { + BaseTestCase::global_set_up(); + }; +} + +pub trait TestCaseTrait { + fn setup(&self) -> WCDBResult<()>; + + fn teardown(&self) -> WCDBResult<()>; +} + +#[derive(Clone)] +pub struct BaseTestCase { + current_directory: String, +} + +impl BaseTestCase { + pub fn new() -> BaseTestCase { + let _ = &*GLOBAL_SETUP; + let current_dir = env::current_dir().expect("Failed to get current directory"); + let current_dir = current_dir.join("BaseTestCase"); + BaseTestCase { + current_directory: current_dir.display().to_string().clone(), + } + } + + // current_directory: "/Users/xxx/Rust/wcdb_rust/src/rust/wcdb_rust/BaseTestCase" + pub fn get_current_directory(&self) -> &str { + self.current_directory.as_str() + } + + pub fn global_set_up() { + // Database::global_trace_performance(Some(|tag, path, handle_id, sql, info| { + // println!( + // "global_trace_performance tag:{} path:{} handle_id:{} sql:{} info:{:?}", + // tag, path, handle_id, sql, info + // ); + // })); + + let ret = Database::global_trace_sql(Some(|tag, path, handle_id, sql, info| { + println!( + "global_trace_sql tag:{} path:{} handle_id:{} info:{:?} sql:{}", + tag, path, handle_id, info, sql + ); + })); + assert!(ret.is_ok()); + + // Database::global_trace_exception(Some(|exception| { + // println!("global_trace_exception exception:{:?}", exception); + // })); + } + + pub fn sleep(millisecond: i64) { + let duration = Duration::from_millis(millisecond as u64); + thread::sleep(duration); + } +} + +impl TestCaseTrait for BaseTestCase { + fn setup(&self) -> WCDBResult<()> { + println!("Current directory: {}", self.current_directory); + Ok(()) + } + + fn teardown(&self) -> WCDBResult<()> { + Ok(()) + } +} diff --git a/src/rust/examples/tests/base/database_test_case.rs b/src/rust/examples/tests/base/database_test_case.rs new file mode 100644 index 000000000..afc0f1250 --- /dev/null +++ b/src/rust/examples/tests/base/database_test_case.rs @@ -0,0 +1,370 @@ +use crate::base::base_test_case::{BaseTestCase, TestCaseTrait}; +use crate::base::wrapped_value::WrappedValue; +use std::cmp::PartialEq; +use std::fs::OpenOptions; +use std::io::{Seek, SeekFrom, Write}; +use std::path::{Path, MAIN_SEPARATOR}; +use std::sync::{Arc, Mutex, RwLock}; +use std::thread; +use std::thread::ThreadId; +use wcdb::base::wcdb_exception::WCDBResult; +use wcdb::core::database::{Database, TraceExceptionCallbackTrait}; +use wcdb::core::handle_operation::HandleOperationTrait; +use wcdb::core::handle_orm_operation::HandleORMOperationTrait; +use wcdb::orm::field::Field; +use wcdb::orm::table_binding::TableBinding; +use wcdb::winq::statement::StatementTrait; + +#[derive(Clone)] +pub struct DatabaseTestCase { + base_test_case: BaseTestCase, + path: Arc>, + file_name: Arc>, + database: Arc>, + expect_mode: Arc>, +} + +impl DatabaseTestCase { + pub fn new() -> Self { + DatabaseTestCase { + base_test_case: BaseTestCase::new(), + path: Arc::new(Mutex::new("".to_string())), + file_name: Arc::new(Mutex::new("".to_string())), + database: Arc::new(RwLock::new(Database::new("./target/tmp/test.db", None))), + expect_mode: Arc::new(Mutex::new(Expect::AllSQLs)), + } + } + + pub fn create_table>( + &self, + table_name: &str, + binding: &R, + ) -> WCDBResult<()> { + self.get_database() + .write() + .unwrap() + .create_table(table_name, binding)?; + Ok(()) + } + + pub fn execute(&self, statement: &T) -> WCDBResult<()> { + self.get_database().read().unwrap().execute(statement) + } + + pub fn trace_exception(&self, cb_opt: Option) + where + CB: TraceExceptionCallbackTrait + 'static, + { + let ret = self.get_database().read().unwrap().trace_exception(cb_opt); + assert!(ret.is_ok()); + } + + pub fn drop_table(&self, table_name: &str) -> WCDBResult<()> { + self.get_database().read().unwrap().drop_table(table_name) + } + + pub fn insert_object( + &self, + object: T, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + self.get_database() + .read() + .unwrap() + .insert_object(object, fields, table_name) + } + + pub fn do_test_sql(&self, sql: &str, operation: CB) + where + CB: FnOnce() -> WCDBResult<()>, + { + let vec = vec![sql.to_string()]; + let _ = self.do_test_sql_vec(vec, operation); + } + + pub fn do_test_sql_vec(&self, sql_vec: Vec, operation: CB) -> WCDBResult<()> + where + CB: FnOnce() -> WCDBResult<()>, + { + assert!(sql_vec.len() > 0); + loop { + let mut trace = Arc::new(Mutex::new(WrappedValue::new())); + { + trace.lock().unwrap().bool_value = false; + } + let expected_sql_vec_mutex = Arc::new(Mutex::new(sql_vec.clone())); + let expected_sql_vec_mutex_clone = expected_sql_vec_mutex.clone(); + let mode_ref = self.get_expect_mode().clone(); + let current_id: ThreadId = thread::current().id(); + let current_thread = Arc::new(format!("{:?}", current_id)); + let trace_clone = Arc::clone(&trace); + let current_thread_clone = Arc::clone(¤t_thread); + let ret = self.get_database().read().unwrap().trace_sql(Some( + move |tag: i64, path: String, handle_id: i64, sql: String, info: String| { + let current_id_trace = format!("{:?}", thread::current().id()); + if current_thread_clone.as_str() != current_id_trace { + return; + } + + if !trace_clone.lock().unwrap().bool_value { + return; + } + DatabaseTestCase::do_test_sql_as_expected( + &mode_ref, + &expected_sql_vec_mutex_clone, + sql, + ); + }, + )); + assert!(ret.is_ok()); + + let mode_ref = self.get_expect_mode(); + if mode_ref != Expect::SomeSQLs { + { + if !self.get_database().read().unwrap().can_open() { + assert!(false, "database can not open"); + break; + } + } + } + let trace_clone = Arc::clone(&trace); + { + trace_clone.lock().unwrap().bool_value = true; + } + operation()?; + let expected_sql_vec_mutex_clone2 = expected_sql_vec_mutex.clone(); + let expected_sql_vec_mutex_clone_lock = expected_sql_vec_mutex_clone2.lock().unwrap(); + if expected_sql_vec_mutex_clone_lock.len() != 0 { + eprintln!("Reminding: {:?}", expected_sql_vec_mutex_clone_lock); + assert!(false, "expectedSQLs not empty"); + break; + } + { + trace_clone.lock().unwrap().bool_value = false; + } + break; + } + { + let ret = self.get_database().read().unwrap().trace_sql(Some( + move |tag: i64, path: String, handle_id: i64, sql: String, info: String| {}, + )); + assert!(ret.is_ok()); + } + Ok(()) + } + + fn do_test_sql_as_expected( + mode_ref: &Expect, + expected_sql_vec_mutex: &Arc>>, + sql: String, + ) { + match mode_ref { + Expect::AllSQLs => { + let mut expected_sql_vec = expected_sql_vec_mutex.lock().unwrap(); + let first_sql = match expected_sql_vec.first() { + None => "".to_string(), + Some(str) => str.clone(), + }; + if first_sql == sql { + expected_sql_vec.remove(0); + } else { + assert!(first_sql.eq(&sql)); + } + } + Expect::FirstFewSQLs => { + let mut expected_sql_vec = expected_sql_vec_mutex.lock().unwrap(); + let first_sql = match expected_sql_vec.first() { + None => "".to_string(), + Some(str) => str.clone(), + }; + if expected_sql_vec.len() > 0 && first_sql == sql { + expected_sql_vec.remove(0); + } else { + assert!(first_sql.eq(&sql)); + } + } + Expect::SomeSQLs => { + let vec_lock = expected_sql_vec_mutex.lock(); + match vec_lock { + Ok(mut expected_sql_vec) => { + for i in 0..expected_sql_vec.len() { + let sql_ = match expected_sql_vec.get(i) { + None => "".to_string(), + Some(str) => str.clone(), + }; + if sql_ == sql { + expected_sql_vec.remove(i); + break; + } + } + } + Err(error) => { + println!("expected_sql_vec_mutex error : {}", error); + } + } + } + } + } + + pub fn trace_sql(&self) {} +} + +impl TestCaseTrait for DatabaseTestCase { + fn setup(&self) -> WCDBResult<()> { + self.base_test_case.setup()?; + self.set_expect_mode(Expect::AllSQLs); + let file_name = "testDatabase.db"; + self.set_file_name(file_name.to_string()); + // "/Users/xxx/Rust/wcdb_rust/src/rust/wcdb_rust/BaseTestCase/target/tmp/testDatabase" + let path = format!( + "{}{}{}{}{}{}{}", + self.base_test_case.get_current_directory(), + MAIN_SEPARATOR, + "target", + MAIN_SEPARATOR, + "tmp", + MAIN_SEPARATOR, + file_name + ); + if Path::new(&path).exists() { + let _ = std::fs::remove_file(path.as_str()); + } + self.set_path(path.clone()); + self.set_database(Database::new(path.as_str(), None)); + let binding = self.get_database(); + let database = binding.read().unwrap(); + database.set_tag(10001); + Ok(()) + } + + fn teardown(&self) -> WCDBResult<()> { + self.base_test_case.teardown()?; + let binding = self.get_database(); + let database = binding.read().unwrap(); + database.close(Some(|| {})); + Ok(()) + } +} + +impl DatabaseTestCase { + pub fn set_path(&self, path: String) { + let mut data = self.path.lock().unwrap(); + *data = path; + } + + pub fn get_path(&self) -> String { + let data = self.path.lock().unwrap(); + data.clone() + } + + pub fn set_file_name(&self, file_name: String) { + let mut data = self.file_name.lock().unwrap(); + *data = file_name; + } + + pub fn get_file_name(&self) -> String { + let data = self.file_name.lock().unwrap(); + data.clone() + } + + pub fn set_database(&self, database: Database) { + let mut data = self.database.write().expect("TODO: panic message"); + *data = database; + } + + pub fn set_expect_mode(&self, expect_mode: Expect) { + let mut data = self.expect_mode.lock().unwrap(); + *data = expect_mode; + } + + pub fn get_expect_mode(&self) -> Expect { + let data = self.expect_mode.lock().unwrap(); + data.clone() + } + + pub fn get_database(&self) -> Arc> { + Arc::clone(&self.database) + } + + pub fn first_material_path(&self) -> String { + let path_clone = Arc::clone(&self.path); + let path = path_clone.lock().unwrap(); + format!("{}{}", path, "-first.material") + } + + pub fn last_material_path(&self) -> String { + let path_clone = Arc::clone(&self.path); + let path = path_clone.lock().unwrap(); + format!("{}{}", path, "-last.material") + } + + pub fn factory_path(&self) -> String { + let path_clone = Arc::clone(&self.path); + let path = path_clone.lock().unwrap(); + format!("{}{}", path, ".factory") + } + + pub fn header_size(&self) -> i32 { + 100 + } + + pub fn page_size(&self) -> i32 { + 4096 + } + + pub fn wal_header_size(&self) -> i32 { + 32 + } + + pub fn wal_frame_header_size(&self) -> i32 { + 24 + } + + pub fn wal_frame_size(&self) -> i32 { + self.wal_frame_header_size() + self.page_size() + } + + pub fn wal_path(&self) -> String { + let path_clone = Arc::clone(&self.path); + let path = path_clone.lock().unwrap(); + format!("{}{}", path, "-wal") + } + + pub fn corrupt_header(&self) { + let binding = self.get_database(); + let database = binding.read().unwrap(); + let database_clone = Arc::clone(&self.get_database()); + let header_size = self.header_size(); + database.close(Some(move || { + database_clone + .read() + .unwrap() + .truncate_check_point() + .unwrap(); + let path_str = database_clone.read().unwrap().get_path(); + if !Path::new(&path_str).exists() { + println!("File not founded"); + return; + } + let mut file = OpenOptions::new() + .read(true) + .write(true) + .open(path_str) + .unwrap(); + file.seek(SeekFrom::Start(0)).unwrap(); + let mut header = Vec::::new(); + header.push(header_size as u8); + file.write_all(&header).unwrap(); + })); + } +} + +#[derive(PartialEq, Clone)] +pub enum Expect { + AllSQLs, + FirstFewSQLs, + SomeSQLs, +} + +pub type TestOperation = Box WCDBResult<()> + Send + 'static>; diff --git a/src/rust/examples/tests/base/exception_test.rs b/src/rust/examples/tests/base/exception_test.rs new file mode 100644 index 000000000..16556f64e --- /dev/null +++ b/src/rust/examples/tests/base/exception_test.rs @@ -0,0 +1,109 @@ +use wcdb::base::value::Value; +use wcdb::winq::column::Column; +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding, Debug)] +#[WCDBTable( + multi_primaries(columns = ["category", "target_id", "channel_id"]) +)] +pub struct ExceptionObject { + #[WCDBField] + pub category: i32, + #[WCDBField] + pub target_id: String, + #[WCDBField] + pub channel_id: String, + #[WCDBField] + pub value: String, +} + +impl ExceptionObject { + pub fn get_object() -> ExceptionObject { + ExceptionObject { + category: 1, + target_id: "target_id".to_string(), + channel_id: "channel_id".to_string(), + value: "value".to_string(), + } + } + + pub fn get_values(&self) -> Vec { + vec![ + Value::new(self.category as i64), + Value::new(self.target_id.as_str()), + Value::new(self.channel_id.as_str()), + Value::new(self.value.as_str()), + ] + } + + pub fn get_all_columns() -> Vec { + vec![ + Column::new("category", None), + Column::new("target_id", None), + Column::new("channel_id", None), + Column::new("value", None), + ] + } +} + +#[cfg(test)] +pub mod exception_test { + use crate::base::exception_test::{ExceptionObject, DB_EXCEPTION_OBJECT_INSTANCE}; + use wcdb::base::wcdb_exception::ExceptionExtendCode; + use wcdb::core::database::Database; + use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + use wcdb::core::table_operation::{TableOperation, TableOperationTrait}; + + #[test] + pub fn test() { + let db_path = "./target/tmp/exception_test.db"; + let database = Database::new(db_path, Some(false)); + + let table_name = "test_table"; + // 需要删除表,验证没有表的情况。 + let ret = database.drop_table(table_name); + assert!(ret.is_ok()); + + /// 验证没有表的情况下,插入数据包错。 + let operation = TableOperation::new(table_name, &database); + let obj = ExceptionObject::get_object(); + let ret = operation.insert_rows(vec![obj.get_values()], ExceptionObject::get_all_columns()); + assert!(ret.is_err()); + + let error = ret.unwrap_err(); + assert_eq!(error.message(), "no such table: test_table"); + assert!(error.path().contains("exception_test.db")); + assert_eq!( + error.sql(), + "INSERT INTO test_table(category, target_id, channel_id, value) VALUES(?1, ?2, ?3, ?4)" + ); + assert_eq!(error.extend_code(), ExceptionExtendCode::Unknown); + + // 创建表 + let ret = database.create_table(table_name, &*DB_EXCEPTION_OBJECT_INSTANCE); + assert!(ret.is_ok()); + + /// 验证重复插入数据。 + // 第一次插入数据。 + let values = obj.get_values(); + let ret = operation.insert_rows(vec![values], ExceptionObject::get_all_columns()); + assert!(ret.is_ok()); + + // 第二次插入数据。 + let values = obj.get_values(); + let ret = operation.insert_rows(vec![values], ExceptionObject::get_all_columns()); + assert!(ret.is_err()); + + let error = ret.unwrap_err(); + assert_eq!(error.message(), "UNIQUE constraint failed: test_table.category, test_table.target_id, test_table.channel_id"); + assert!(error.path().contains("exception_test.db")); + assert_eq!( + error.sql(), + "INSERT INTO test_table(category, target_id, channel_id, value) VALUES(?1, ?2, ?3, ?4)" + ); + assert_eq!( + error.extend_code(), + ExceptionExtendCode::ConstraintPrimaryKey + ); + } +} diff --git a/src/rust/examples/tests/base/file_tool.rs b/src/rust/examples/tests/base/file_tool.rs new file mode 100644 index 000000000..a0f0bf11a --- /dev/null +++ b/src/rust/examples/tests/base/file_tool.rs @@ -0,0 +1,29 @@ +use std::fs; +use std::path::Path; + +pub struct FileTool {} + +impl FileTool { + pub fn new() -> Self { + FileTool {} + } + + pub fn new_box() -> Box { + Box::new(FileTool {}) + } + + pub fn file_exist(path: &str) -> bool { + let ret = Path::new(path).try_exists().unwrap(); + ret + } + + pub fn get_file_size(path: &str) -> i64 { + let ret = Path::new(path).try_exists().unwrap(); + if ret { + let result = fs::metadata(path).unwrap().len() as i64; + result + } else { + 0 + } + } +} diff --git a/src/rust/examples/tests/base/mod.rs b/src/rust/examples/tests/base/mod.rs new file mode 100644 index 000000000..1ee9a1d72 --- /dev/null +++ b/src/rust/examples/tests/base/mod.rs @@ -0,0 +1,11 @@ +pub(crate) mod base_test_case; +pub(crate) mod database_test_case; +pub(crate) mod exception_test; +pub(crate) mod file_tool; +pub(crate) mod pinyin_object; +pub(crate) mod random_tool; +pub(crate) mod table_test_case; +pub(crate) mod test_object; +pub(crate) mod traditional_chinese_object; +pub(crate) mod winq_tool; +pub(crate) mod wrapped_value; diff --git a/src/rust/examples/tests/base/pinyin_object.rs b/src/rust/examples/tests/base/pinyin_object.rs new file mode 100644 index 000000000..48929e921 --- /dev/null +++ b/src/rust/examples/tests/base/pinyin_object.rs @@ -0,0 +1,16 @@ +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding, PartialEq, Clone)] +#[WCDBTable(fts_module(version = "FTS5", tokenizer = "wcdb_pinyin"))] +pub struct PinyinObject { + #[WCDBField] + pub content: String, +} + +impl PinyinObject { + pub fn new() -> Self { + Self { + content: String::new(), + } + } +} diff --git a/src/rust/examples/tests/base/random_tool.rs b/src/rust/examples/tests/base/random_tool.rs new file mode 100644 index 000000000..e6eea8ea6 --- /dev/null +++ b/src/rust/examples/tests/base/random_tool.rs @@ -0,0 +1,41 @@ +use crate::base::test_object::TestObject; +use rand::seq::SliceRandom; +use rand::Rng; + +pub struct RandomTool {} + +impl RandomTool { + pub fn string() -> String { + Self::string_by_length(100) + } + + pub fn string_by_length(length: i32) -> String { + let chars: Vec = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + .chars() + .collect(); + let mut rng = rand::thread_rng(); + (0..length) + .map(|_| *chars.choose(&mut rng).unwrap()) + .collect() + } + + pub fn bytes() -> Vec { + Self::bytes_by_length(100) + } + + pub fn bytes_by_length(length: i32) -> Vec { + let mut rng = rand::thread_rng(); // 创建随机数生成器 + let mut bytes = vec![0u8; length as usize]; // 初始化字节数组 + rng.fill(bytes.as_mut_slice()); // 填充随机数据 + bytes + } + + pub fn auto_increment_test_case_objects(count: i32) -> Vec { + let mut vec = Vec::new(); + for x in 0..count { + let obj = TestObject::create_auto_increment_object(Self::string()); + vec.push(obj); + } + vec + } +} diff --git a/src/rust/examples/tests/base/table_test_case.rs b/src/rust/examples/tests/base/table_test_case.rs new file mode 100644 index 000000000..87fe02cf0 --- /dev/null +++ b/src/rust/examples/tests/base/table_test_case.rs @@ -0,0 +1,110 @@ +use crate::base::base_test_case::TestCaseTrait; +use crate::base::database_test_case::DatabaseTestCase; +use crate::base::test_object::{TestObject, DB_TEST_OBJECT_INSTANCE}; +use lazy_static::lazy_static; +use std::sync::{Arc, RwLock}; +use wcdb::base::wcdb_exception::WCDBResult; +use wcdb::core::database::Database; +use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + +pub trait SelectingObjectOperationTrait { + fn execute() -> Vec; +} + +pub struct TableTestCase { + table_name: String, + pub(crate) data_base_test_case: DatabaseTestCase, + is_virtual_table: bool, +} + +impl TestCaseTrait for TableTestCase { + fn setup(&self) -> WCDBResult<()> { + self.data_base_test_case.setup() + } + + fn teardown(&self) -> WCDBResult<()> { + self.data_base_test_case.teardown() + } +} + +lazy_static! { + static ref TABLE_TEST_CASE: TableTestCase = { + let table_test = TableTestCase::new(); + table_test + }; +} + +impl TableTestCase { + pub fn new() -> Self { + TableTestCase { + table_name: "testTable".to_string(), + data_base_test_case: DatabaseTestCase::new(), + is_virtual_table: false, + } + } + + pub fn new_with_table_name(table_name: &str) -> Self { + TableTestCase { + table_name: table_name.to_string(), + data_base_test_case: DatabaseTestCase::new(), + is_virtual_table: false, + } + } + + pub fn get_database(&self) -> Arc> { + self.data_base_test_case.get_database() + } + + pub fn get_path(&self) -> String { + self.data_base_test_case.get_path() + } + + pub fn do_test_object_by_selecting( + &self, + object: TestObject, + sql: String, + operation: &T, + ) { + self.do_test_objects_by_selecting(vec![object], vec![sql], operation); + } + + pub fn create_table(&self) -> WCDBResult<()> { + let database_clone = Arc::clone(&self.data_base_test_case.get_database()); + let database = database_clone.read().unwrap(); + if !self.is_virtual_table { + database.create_table(&*self.table_name, &*DB_TEST_OBJECT_INSTANCE)?; + } else { + // todo dengxudong + // database.createVirtualTable(tableName, tableBinding); + } + Ok(()) + } + + pub fn do_test_objects_by_selecting( + &self, + objects: Vec, + sqls: Vec, + operation: &T, + ) { + self.data_base_test_case + .do_test_sql_vec(sqls, move || Ok(())) + .unwrap(); + } + + // pub fn get_table(&self) -> Arc> { + // match &self.table { + // None => { + // panic!("Table is None"); + // } + // Some(table) => Arc::clone(table), + // } + // } + + pub fn get_data_base_test_case(&self) -> &DatabaseTestCase { + &self.data_base_test_case + } + + pub fn get_table_name(&self) -> &str { + &self.table_name + } +} diff --git a/src/rust/examples/tests/base/test_object.rs b/src/rust/examples/tests/base/test_object.rs new file mode 100644 index 000000000..07fc2782f --- /dev/null +++ b/src/rust/examples/tests/base/test_object.rs @@ -0,0 +1,40 @@ +use wcdb_derive::WCDBTableCoding; + +#[derive(PartialEq, Debug, WCDBTableCoding)] +#[WCDBTable] +pub struct TestObject { + #[WCDBField(is_primary = true, is_auto_increment = true)] + id: i32, + #[WCDBField] + content: String, +} +impl TestObject { + pub fn new(content: String) -> TestObject { + TestObject { + id: 0, + content: content.clone(), + } + } + + pub fn create_object(id: i32, content: String) -> TestObject { + TestObject { + id, + content: content.clone(), + } + } + + pub fn create_auto_increment_object(content: String) -> TestObject { + TestObject { + id: 0, + content: content.clone(), + } + } + + pub fn id(&self) -> i32 { + self.id + } + + pub fn content(&self) -> &str { + &self.content + } +} diff --git a/src/rust/examples/tests/base/traditional_chinese_object.rs b/src/rust/examples/tests/base/traditional_chinese_object.rs new file mode 100644 index 000000000..ce062f538 --- /dev/null +++ b/src/rust/examples/tests/base/traditional_chinese_object.rs @@ -0,0 +1,8 @@ +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding, PartialEq, Clone)] +#[WCDBTable(fts_module(version = "FTS5", tokenizer = "wcdb_verbatim" , tokenizer_parameters = ["chinese_traditional_to_simplified"]))] +pub struct TraditionalChineseObject { + #[WCDBField] + pub content: String, +} diff --git a/src/rust/examples/tests/base/winq_tool.rs b/src/rust/examples/tests/base/winq_tool.rs new file mode 100644 index 000000000..a1ac3e4a4 --- /dev/null +++ b/src/rust/examples/tests/base/winq_tool.rs @@ -0,0 +1,9 @@ +use wcdb::winq::identifier::IdentifierTrait; + +pub struct WinqTool {} + +impl WinqTool { + pub fn winq_equal(winq: &impl IdentifierTrait, sql: &str) { + assert_eq!(sql, winq.get_description()) + } +} diff --git a/src/rust/examples/tests/base/wrapped_value.rs b/src/rust/examples/tests/base/wrapped_value.rs new file mode 100644 index 000000000..5c6e7ace1 --- /dev/null +++ b/src/rust/examples/tests/base/wrapped_value.rs @@ -0,0 +1,28 @@ +use std::time::SystemTime; + +pub struct WrappedValue { + pub bool_value: bool, + pub int_value: i64, + pub string_value: String, + pub double_value: f64, +} + +impl WrappedValue { + pub fn new() -> Self { + WrappedValue { + bool_value: false, + int_value: 0, + string_value: "".to_string(), + double_value: 0.0, + } + } + + pub fn current_time() -> Self { + let mut value = WrappedValue::new(); + value.int_value = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_millis() as i64; + value + } +} diff --git a/src/rust/examples/tests/core/mod.rs b/src/rust/examples/tests/core/mod.rs new file mode 100644 index 000000000..eb87f9d4a --- /dev/null +++ b/src/rust/examples/tests/core/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod table_operation_object; +pub(crate) mod table_operation_test; +pub(crate) mod table_orm_operation_test; diff --git a/src/rust/examples/tests/core/table_operation_object.rs b/src/rust/examples/tests/core/table_operation_object.rs new file mode 100644 index 000000000..a474a4120 --- /dev/null +++ b/src/rust/examples/tests/core/table_operation_object.rs @@ -0,0 +1,78 @@ +use crate::base::random_tool::RandomTool; +use std::time::SystemTime; +use wcdb::base::value::Value; +use wcdb::winq::column::Column; +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding, Debug, Clone)] +#[WCDBTable( + multi_primaries(columns = ["category", "target_id", "channel_id"]) +)] +pub struct TableOperationObject { + #[WCDBField] + pub category: i32, + #[WCDBField] + pub target_id: String, + #[WCDBField] + pub channel_id: String, + #[WCDBField] + pub value: String, +} + +impl TableOperationObject { + pub fn new() -> Self { + TableOperationObject { + category: 0, + target_id: "".to_string(), + channel_id: "".to_string(), + value: "".to_string(), + } + } + pub fn get_obj() -> TableOperationObject { + let time = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("Time went backwards") + .as_millis(); + let mut obj = TableOperationObject::new(); + obj.category = 3; + obj.target_id = format!("target_id-{}", RandomTool::string()); + obj.channel_id = time.to_string(); + obj.value = format!("value-{}", RandomTool::string()); + obj + } + + pub fn get_obj_vec(data_num: i32) -> Vec { + let time = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("Time went backwards") + .as_millis(); + let mut obj_vec = vec![]; + for i in 0..data_num { + let mut obj = TableOperationObject::new(); + obj.category = 3; + obj.target_id = format!("target_id-{}", RandomTool::string()); + obj.channel_id = (time * 10 + i as u128).to_string(); + obj.value = time.to_string(); + obj_vec.push(obj); + } + obj_vec + } + + pub fn get_values_vec(&self) -> Vec { + vec![ + Value::new(self.category as i64), + Value::new(self.target_id.as_str()), + Value::new(self.channel_id.as_str()), + Value::new(self.value.as_str()), + ] + } + + pub fn get_all_columns() -> Vec { + vec![ + Column::new("category", None), + Column::new("target_id", None), + Column::new("channel_id", None), + Column::new("value", None), + ] + } +} diff --git a/src/rust/examples/tests/core/table_operation_test.rs b/src/rust/examples/tests/core/table_operation_test.rs new file mode 100644 index 000000000..4de5e099c --- /dev/null +++ b/src/rust/examples/tests/core/table_operation_test.rs @@ -0,0 +1,177 @@ +use crate::base::base_test_case::TestCaseTrait; +use crate::base::table_test_case::TableTestCase; +use crate::core::table_operation_object::TableOperationObject; +use lazy_static::lazy_static; +use std::sync::{Arc, RwLock}; +use wcdb::base::wcdb_exception::WCDBResult; + +static TABLE_NAME: &'static str = "table_option_test_case"; +static TABLE_NAME_X: &'static str = "table_option_test_case_x"; + +pub struct TableOperationTest { + table_test_case: TableTestCase, +} + +impl TestCaseTrait for TableOperationTest { + fn setup(&self) -> WCDBResult<()> { + self.table_test_case.setup() + } + + fn teardown(&self) -> WCDBResult<()> { + self.table_test_case.teardown() + } +} + +impl TableOperationTest { + pub fn new() -> Self { + TableOperationTest { + table_test_case: TableTestCase::new_with_table_name(TABLE_NAME), + } + } + + pub fn get_table_test_case(&self) -> &TableTestCase { + &self.table_test_case + } + + pub fn get_mut_table_test_case(&mut self) -> &mut TableTestCase { + &mut self.table_test_case + } +} + +lazy_static! { + static ref TABLE_OPERATION_TEST: Arc> = + Arc::new(RwLock::new(TableOperationTest::new())); + static ref PRE_INSERT_OBJECTS: Vec = TableOperationObject::get_obj_vec(2); +} + +#[cfg(test)] +pub mod table_operation_test_case { + use crate::base::base_test_case::TestCaseTrait; + use crate::core::table_operation_object::{ + DbTableOperationObject, TableOperationObject, DB_TABLE_OPERATION_OBJECT_INSTANCE, + }; + use crate::core::table_operation_test::{TABLE_NAME, TABLE_OPERATION_TEST}; + use std::sync::{Arc, RwLock}; + use wcdb::base::value::Value; + use wcdb::core::database::Database; + use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + use wcdb::core::table_operation::{TableOperation, TableOperationTrait}; + use wcdb::winq::column::Column; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + + pub fn setup() { + let arc_clone = Arc::clone(&TABLE_OPERATION_TEST); + let arc_test = arc_clone.read().expect("test read failure"); + arc_test.setup().expect("setup failure"); + } + pub fn teardown() { + let arc_clone = Arc::clone(&TABLE_OPERATION_TEST); + let arc_test = arc_clone.read().expect("test read failure"); + arc_test.teardown().expect("teardown failure"); + } + + pub fn get_arc_database() -> Arc> { + let ret = Arc::clone(&TABLE_OPERATION_TEST) + .read() + .unwrap() + .get_table_test_case() + .get_database(); + Arc::clone(&ret) + } + + #[test] + pub fn test() { + setup(); + + // 获取数据库 + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + // 删除表 + let _ = database.drop_table(TABLE_NAME); + + let ret = database.create_table(TABLE_NAME, &*DB_TABLE_OPERATION_OBJECT_INSTANCE); + assert!(ret.is_ok()); + + let operation = TableOperation::new(TABLE_NAME, &database); + let obj = TableOperationObject::get_obj(); + let field_channel_id = DbTableOperationObject::channel_id(); + + // 测试插入数据。 + // insert row + let ret = operation.insert_rows( + vec![obj.get_values_vec()], + TableOperationObject::get_all_columns(), + ); + assert!(ret.is_ok()); + + // insert row + let ret = operation.insert_rows( + vec![obj.get_values_vec()], + TableOperationObject::get_all_columns(), + ); + assert!(!ret.is_ok()); + + // insert or replace + let ret = operation.insert_or_replace_rows( + vec![obj.get_values_vec()], + TableOperationObject::get_all_columns(), + ); + assert!(ret.is_ok()); + + // insert or ignore + let objs = TableOperationObject::get_obj_vec(2); + let values = objs.iter().map(|v| v.get_values_vec()).collect(); + let ret = operation.insert_or_ignore_rows(values, TableOperationObject::get_all_columns()); + assert!(ret.is_ok()); + + // 测试更新数据。 + // update row + let updated_text = "updated_row"; + let updated_value = Value::new(updated_text); + let expression = field_channel_id.eq(obj.channel_id.as_str()); + let ret = operation.update_row( + &vec![updated_value], + &vec![Column::new("value", None)], + Some(&expression), + None, + None, + None, + ); + assert!(ret.is_ok()); + + // select value + let expression = field_channel_id.eq(obj.channel_id.as_str()); + let ret = operation.get_values( + vec![&Column::new("value", None)], + Some(&expression), + None, + None, + None, + ); + assert!(ret.is_ok()); + + let ret_value = ret.unwrap(); + let item = ret_value.first().unwrap().first().unwrap(); + assert_eq!(item.get_text(), updated_text); + + // 测试删除数据。 + // delete row + let expression = field_channel_id.eq(obj.channel_id.as_str()); + let ret = operation.delete_value(Some(&expression), None, None, None); + assert!(ret.is_ok()); + + // select value + let expression = field_channel_id.eq(obj.channel_id.as_str()); + let ret = operation.get_values( + vec![&Column::new("value", None)], + Some(&expression), + None, + None, + None, + ); + assert!(ret.is_ok()); + assert_eq!(ret.unwrap().len(), 0); + + teardown(); + } +} diff --git a/src/rust/examples/tests/core/table_orm_operation_test.rs b/src/rust/examples/tests/core/table_orm_operation_test.rs new file mode 100644 index 000000000..ccf7c934e --- /dev/null +++ b/src/rust/examples/tests/core/table_orm_operation_test.rs @@ -0,0 +1,156 @@ +use crate::base::base_test_case::TestCaseTrait; +use crate::base::table_test_case::TableTestCase; +use crate::core::table_operation_object::TableOperationObject; +use lazy_static::lazy_static; +use std::sync::{Arc, RwLock}; +use wcdb::base::wcdb_exception::WCDBResult; + +static TABLE_NAME: &'static str = "table_orm_operation_test_case"; +static TABLE_NAME_X: &'static str = "table_orm_operation_test_case_x"; + +pub struct TableORMOperationTest { + table_test_case: TableTestCase, +} + +impl TestCaseTrait for TableORMOperationTest { + fn setup(&self) -> WCDBResult<()> { + self.table_test_case.setup() + } + + fn teardown(&self) -> WCDBResult<()> { + self.table_test_case.teardown() + } +} + +impl TableORMOperationTest { + pub fn new() -> Self { + TableORMOperationTest { + table_test_case: TableTestCase::new_with_table_name(TABLE_NAME), + } + } + + pub fn get_table_test_case(&self) -> &TableTestCase { + &self.table_test_case + } + + pub fn get_mut_table_test_case(&mut self) -> &mut TableTestCase { + &mut self.table_test_case + } +} + +lazy_static! { + static ref table_orm_operation_TEST: Arc> = + Arc::new(RwLock::new(TableORMOperationTest::new())); + static ref PRE_INSERT_OBJECTS: Vec = TableOperationObject::get_obj_vec(2); +} + +#[cfg(test)] +pub mod table_orm_operation_test_case { + use crate::base::base_test_case::TestCaseTrait; + use crate::core::table_operation_object::{ + DbTableOperationObject, TableOperationObject, DB_TABLE_OPERATION_OBJECT_INSTANCE, + }; + use crate::core::table_orm_operation_test::{table_orm_operation_TEST, TABLE_NAME}; + use core::clone::Clone; + use core::{assert, assert_eq}; + use std::sync::{Arc, RwLock}; + use wcdb::core::database::Database; + use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + + pub fn setup() { + let arc_clone = Arc::clone(&table_orm_operation_TEST); + let arc_test = arc_clone.read().expect("test read failure"); + arc_test.setup().expect("setup failure"); + } + pub fn teardown() { + let arc_clone = Arc::clone(&table_orm_operation_TEST); + let arc_test = arc_clone.read().expect("test read failure"); + arc_test.teardown().expect("teardown failure"); + } + + pub fn get_arc_database() -> Arc> { + let ret = Arc::clone(&table_orm_operation_TEST) + .read() + .unwrap() + .get_table_test_case() + .get_database(); + Arc::clone(&ret) + } + + // #[test] // TODO: 一起运行会失败 + pub fn test() { + setup(); + + // 获取数据库 + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + // 删除表 + let _ = database.drop_table(TABLE_NAME); + + let ret = database.create_table(TABLE_NAME, &*DB_TABLE_OPERATION_OBJECT_INSTANCE); + assert!(ret.is_ok()); + + let obj = TableOperationObject::get_obj(); + + let ret = database.insert_object( + obj.clone(), + DbTableOperationObject::all_fields(), + TABLE_NAME, + ); + assert!(ret.is_ok()); + + let ret = database.insert_or_replace_objects( + vec![obj.clone()], + DbTableOperationObject::all_fields(), + TABLE_NAME, + ); + assert!(ret.is_ok()); + + let ret = database.insert_or_ignore_objects( + vec![obj.clone()], + DbTableOperationObject::all_fields(), + TABLE_NAME, + ); + assert!(ret.is_ok()); + + let field_channel_id = DbTableOperationObject::channel_id(); + let field_value = DbTableOperationObject::value(); + + let updated_text = "updated_row"; + let expression = field_channel_id.eq(obj.channel_id.as_str()); + let update_obj = TableOperationObject { + value: updated_text.to_string(), + ..obj.clone() + }; + let ret = database.update_object( + update_obj, + vec![field_value], + TABLE_NAME, + Some(&expression), + None, + None, + None, + ); + assert!(ret.is_ok()); + + let expression = field_channel_id.eq(obj.channel_id.as_str()); + let ret = database.get_first_object( + vec![&field_value], + TABLE_NAME, + Some(&expression), + None, + None, + ); + assert!(ret.is_ok()); + + let ret_value_opt = ret.unwrap(); + assert_eq!(ret_value_opt.unwrap().value, updated_text); + + let expression = field_channel_id.eq(obj.channel_id.as_str()); + let ret = database.delete_objects(TABLE_NAME, Some(&expression), None, None, None); + assert!(ret.is_ok()); + + teardown(); + } +} diff --git a/src/rust/examples/tests/crud/fts_test.rs b/src/rust/examples/tests/crud/fts_test.rs new file mode 100644 index 000000000..9509179cb --- /dev/null +++ b/src/rust/examples/tests/crud/fts_test.rs @@ -0,0 +1,363 @@ +use crate::base::base_test_case::TestCaseTrait; +use crate::base::database_test_case::{DatabaseTestCase, Expect}; +use wcdb::base::wcdb_exception::WCDBResult; + +pub struct FTSTest { + database_test_case: DatabaseTestCase, + table_name: String, +} + +impl TestCaseTrait for FTSTest { + fn setup(&self) -> WCDBResult<()> { + self.database_test_case.setup()?; + self.database_test_case.set_expect_mode(Expect::SomeSQLs); + Ok(()) + } + + fn teardown(&self) -> WCDBResult<()> { + self.database_test_case.teardown() + } +} + +impl FTSTest { + pub fn new() -> Self { + Self { + database_test_case: DatabaseTestCase::new(), + table_name: "ftsTable".to_string(), + } + } +} + +#[cfg(test)] +pub mod fts_test { + use crate::base::base_test_case::TestCaseTrait; + use crate::base::pinyin_object::{DbPinyinObject, PinyinObject, DB_PINYIN_OBJECT_INSTANCE}; + use crate::base::test_object::{TestObject, DB_TEST_OBJECT_INSTANCE}; + use crate::base::traditional_chinese_object::{ + DbTraditionalChineseObject, TraditionalChineseObject, + DB_TRADITIONAL_CHINESE_OBJECT_INSTANCE, + }; + use crate::crud::fts_test::FTSTest; + use crate::orm::testclass::fts3_test_object::{ + DbFts3TestObject, Fts3TestObject, DB_FTS3_TEST_OBJECT_INSTANCE, + }; + use crate::orm::testclass::fts5_test_object::{ + DbFts5TestObject, Fts5TestObject, DB_FTS5_TEST_OBJECT_INSTANCE, + }; + use crate::orm::testclass::mmicu_test_object::{ + DbMmicuTestObject, MmicuTestObject, DB_MMICU_TEST_OBJECT_INSTANCE, + }; + use std::collections::HashMap; + use wcdb::core::database::Database; + use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + use wcdb::core::table_orm_operation::TableORMOperationTrait; + use wcdb::fts::builtin_tokenizer::BuiltinTokenizer; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + + fn setup(fts_test: &FTSTest) { + fts_test.setup().unwrap(); + } + + fn teardown(fts_test: &FTSTest) { + fts_test.teardown().unwrap(); + } + + #[test] + fn test_fts3() { + let fts_test = FTSTest::new(); + setup(&fts_test); + + let arc_db = fts_test.database_test_case.get_database(); + let database = arc_db.read().unwrap(); + + database.add_tokenizer(BuiltinTokenizer::ONE_OR_BINARY); + database + .create_virtual_table(&fts_test.table_name, &*DB_FTS3_TEST_OBJECT_INSTANCE) + .unwrap(); + let table = database.get_table(&fts_test.table_name, &*DB_FTS3_TEST_OBJECT_INSTANCE); + let english_object = Fts3TestObject::new(1, "This is English test content"); + let chinese_object = Fts3TestObject::new(2, "这是中文测试内容"); + let numeric_object = Fts3TestObject::new(1, "123456"); + let symbolic_object = Fts3TestObject::new(1, "abc..def"); + + let obj_vec = vec![ + english_object.clone(), + chinese_object.clone(), + numeric_object.clone(), + symbolic_object.clone(), + ]; + + table.insert_objects(obj_vec, None).unwrap(); + + // English + let exp = DbFts3TestObject::content().match_("Engl*"); + let vec = table + .get_all_objects(None, Some(&exp), None, None, None) + .unwrap(); + assert_eq!(vec.len(), 1); + assert_eq!(vec[0].content, english_object.content); + + // Chinese + let exp = DbFts3TestObject::content().match_("中文"); + let vec = table + .get_all_objects(None, Some(&exp), None, None, None) + .unwrap(); + assert_eq!(vec.len(), 1); + assert_eq!(vec[0].content, chinese_object.content); + + // Numeric + let exp = DbFts3TestObject::content().match_("123*"); + let vec = table + .get_all_objects(None, Some(&exp), None, None, None) + .unwrap(); + assert_eq!(vec.len(), 1); + assert_eq!(vec[0].content, numeric_object.content); + + // Symbolic + let exp = DbFts3TestObject::content().match_("def*"); + let vec = table + .get_all_objects(None, Some(&exp), None, None, None) + .unwrap(); + assert_eq!(vec.len(), 1); + assert_eq!(vec[0].content, symbolic_object.content); + + teardown(&fts_test); + } + + // todo qixinbing 该用例建表失败,导致插入失败,原因待查 + // #[test] + // fn test_mmicu() { + // let fts_test = FTSTest::new(); + // setup(&fts_test); + // + // let arc_db = fts_test.database_test_case.get_database(); + // let database = arc_db.read().unwrap(); + // let table_name = fts_test.table_name.as_str(); + // database.add_tokenizer(BuiltinTokenizer::MMICU); + // database.create_virtual_table(table_name, &*DB_MMICU_TEST_OBJECT_INSTANCE).unwrap(); + // let table = database.get_table(&fts_test.table_name, &*DB_MMICU_TEST_OBJECT_INSTANCE); + // let english_object = MmicuTestObject::new(1, "This is English test content"); + // let chinese_object = MmicuTestObject::new(2, "这是中文测试内容"); + // let numeric_object = MmicuTestObject::new(1, "123456"); + // let symbolic_object = MmicuTestObject::new(1, "abc..def"); + // + // let obj_vec = vec![ + // english_object.clone(), + // chinese_object.clone(), + // numeric_object.clone(), + // symbolic_object.clone(), + // ]; + // + // table.insert_objects(obj_vec, None).unwrap(); + // + // // English + // let exp = DbMmicuTestObject::content().match_("Engl*"); + // let vec = table + // .get_all_objects(None, Some(&exp), None, None, None) + // .unwrap(); + // assert_eq!(vec.len(), 1); + // assert_eq!(vec[0].content, english_object.content); + // + // // Chinese + // let exp = DbMmicuTestObject::content().match_("中文"); + // let vec = table + // .get_all_objects(None, Some(&exp), None, None, None) + // .unwrap(); + // assert_eq!(vec.len(), 1); + // assert_eq!(vec[0].content, chinese_object.content); + // + // // Numeric + // let exp = DbMmicuTestObject::content().match_("123*"); + // let vec = table + // .get_all_objects(None, Some(&exp), None, None, None) + // .unwrap(); + // assert_eq!(vec.len(), 1); + // assert_eq!(vec[0].content, numeric_object.content); + // + // // Symbolic + // let exp = DbMmicuTestObject::content().match_("def*"); + // let vec = table + // .get_all_objects(None, Some(&exp), None, None, None) + // .unwrap(); + // assert_eq!(vec.len(), 1); + // assert_eq!(vec[0].content, symbolic_object.content); + // + // teardown(&fts_test); + // } + + #[test] + fn test_fts5() { + let fts_test = FTSTest::new(); + setup(&fts_test); + let arc_db = fts_test.database_test_case.get_database(); + let database = arc_db.read().unwrap(); + let table_name = &fts_test.table_name; + + database.add_tokenizer(BuiltinTokenizer::VERBATIM); + database + .create_virtual_table(table_name, &*DB_FTS5_TEST_OBJECT_INSTANCE) + .unwrap(); + let table = database.get_table(table_name, &*DB_FTS5_TEST_OBJECT_INSTANCE); + let english_object = Fts5TestObject::new(1, "This is English test content"); + let chinese_object = Fts5TestObject::new(2, "这是中文测试内容"); + let numeric_object = Fts5TestObject::new(3, "123456"); + let symbolic_object = Fts5TestObject::new(4, "abc..def"); + + let fts5_obj_vec = vec![ + english_object.clone(), + chinese_object.clone(), + numeric_object.clone(), + symbolic_object.clone(), + ]; + + table.insert_objects(fts5_obj_vec.clone(), None).unwrap(); + + // External content object + database + .create_table("contentTable", &*DB_TEST_OBJECT_INSTANCE) + .unwrap(); + let content_table = database.get_table("contentTable", &*DB_TEST_OBJECT_INSTANCE); + let mut all_content_objects = vec![]; + for object in fts5_obj_vec { + let content_object = TestObject::create_object(object.id, object.content); + all_content_objects.push(content_object); + } + content_table + .insert_objects(all_content_objects, None) + .unwrap(); + + // English + let exp = DbFts5TestObject::content().match_("Engl*"); + let vec = table + .get_all_objects(None, Some(&exp), None, None, None) + .unwrap(); + assert_eq!(vec.len(), 1); + assert_eq!(vec[0].content, english_object.content); + + // Chinese + let exp = DbFts5TestObject::content().match_("中文"); + let vec = table + .get_all_objects(None, Some(&exp), None, None, None) + .unwrap(); + assert_eq!(vec.len(), 1); + assert_eq!(vec[0].content, chinese_object.content); + + // Numeric + let exp = DbFts5TestObject::content().match_("123*"); + let vec = table + .get_all_objects(None, Some(&exp), None, None, None) + .unwrap(); + assert_eq!(vec.len(), 1); + assert_eq!(vec[0].content, numeric_object.content); + + // Symbolic + let exp = DbFts5TestObject::content().match_("def*"); + let vec = table + .get_all_objects(None, Some(&exp), None, None, None) + .unwrap(); + assert_eq!(vec.len(), 1); + assert_eq!(vec[0].content, symbolic_object.content); + + teardown(&fts_test); + } + + #[test] + fn test_pinyin() { + Database::config_pinyin_dict(&HashMap::from([ + ("单", vec!["shan", "dan", "chan"]), + ("于", vec!["yu"]), + ("骑", vec!["qi"]), + ("模", vec!["mo", "mu"]), + ("具", vec!["ju"]), + ("车", vec!["che"]), + ])); + + let fts_test = FTSTest::new(); + setup(&fts_test); + + let arc_db = fts_test.database_test_case.get_database(); + let database = arc_db.read().unwrap(); + + database.add_tokenizer(BuiltinTokenizer::PINYIN); + database + .create_virtual_table(&fts_test.table_name, &*DB_PINYIN_OBJECT_INSTANCE) + .unwrap(); + + let mut obj = PinyinObject::new(); + let content = "单于骑模具单车"; + obj.content = content.to_string(); + database + .insert_object(obj, DbPinyinObject::all_fields(), &fts_test.table_name) + .unwrap(); + + // todo 1 拼音如何加空格 + // todo 2 汉语如何分词 + // todo 3 确保端和服务的分词方案一致:同时支持中文搜索和拼音搜索 + let queries = vec![ + "\"shan yu qi mu ju dan che\"", + "\"chan yu qi mo ju shan che\"", + "\"dan yu qi mo ju chan che\"", + "\"dan yu qi mu ju ch\"*", + "\"dan yu qi mo ju d\"*", + "\"s y q m j d c\"", + "\"c y q m j s c\"", + "\"c y q m j\"", + ]; + + for query in queries { + let exp = DbPinyinObject::content().match_(query); + let results = database + .get_all_objects( + DbPinyinObject::all_fields(), + &fts_test.table_name, + Some(&exp), + None, + None, + None, + ) + .unwrap(); + assert_eq!(results.len(), 1); + assert_eq!(results[0].content, content); + } + + teardown(&fts_test); + } + + #[test] + fn test_traditional_chinese() { + let fts_test = FTSTest::new(); + setup(&fts_test); + + let arc_db = fts_test.database_test_case.get_database(); + let database = arc_db.read().unwrap(); + let table_name = &fts_test.table_name; + + Database::config_traditional_chinese_dict(&HashMap::from([("們", "们"), ("員", "员")])); + database.add_tokenizer(BuiltinTokenizer::VERBATIM); + database + .create_virtual_table(table_name, &*DB_TRADITIONAL_CHINESE_OBJECT_INSTANCE) + .unwrap(); + + let table = database.get_table(table_name, &*DB_TRADITIONAL_CHINESE_OBJECT_INSTANCE); + + let mut obj = TraditionalChineseObject::default(); + obj.content = "我們是程序員".to_string(); + table.insert_object(obj.clone(), None).unwrap(); + + let exp = DbTraditionalChineseObject::content().match_("我們是程序員"); + let vec = table + .get_all_objects(None, Some(&exp), None, None, None) + .unwrap(); + assert_eq!(vec.len(), 1); + assert_eq!(vec[0].content, obj.content); + + let exp = DbTraditionalChineseObject::content().match_("我们是程序员"); + let vec = table + .get_all_objects(None, Some(&exp), None, None, None) + .unwrap(); + assert_eq!(vec.len(), 1); + assert_eq!(vec[0].content, obj.content); + + teardown(&fts_test); + } +} diff --git a/src/rust/examples/tests/crud/fts_trigger_perf_test.rs b/src/rust/examples/tests/crud/fts_trigger_perf_test.rs new file mode 100644 index 000000000..2d42f7793 --- /dev/null +++ b/src/rust/examples/tests/crud/fts_trigger_perf_test.rs @@ -0,0 +1,351 @@ +use wcdb_derive::WCDBTableCoding; + +pub static TABLE_FTS_REBUILD_PROGRESS_NAME: &str = "table_fts_rebuild_progress"; + +#[derive(WCDBTableCoding)] +pub struct TableFtsRebuildProgress { + #[WCDBField(is_unique = true, default(text_value = ""))] + table_name: String, + #[WCDBField(default(i32_value = 0))] + last_processed_id: i64, + #[WCDBField(default(i32_value = 0))] + total_id: i64, +} + +impl TableFtsRebuildProgress { + pub(crate) fn new(table_name: &str, last_processed_id: i64, total_id: i64) -> Self { + Self { + table_name: table_name.to_string(), + last_processed_id, + total_id, + } + } + + pub(crate) fn default() -> Self { + Self { + table_name: "".to_string(), + last_processed_id: 0, + total_id: 0, + } + } +} + +#[cfg(test)] +pub mod fts_trigger_perf_test { + use crate::base::base_test_case::BaseTestCase; + use crate::crud::fts_trigger_perf_test::{ + DbTableFtsRebuildProgress, TableFtsRebuildProgress, DB_TABLE_FTS_REBUILD_PROGRESS_INSTANCE, + TABLE_FTS_REBUILD_PROGRESS_NAME, + }; + use crate::crud::fts_trigger_test::{ + DbTableMessage, DbTableMessageFtsCn, DbTableMessageFtsPinyin, + DB_TABLE_MESSAGE_FTS_CN_INSTANCE, DB_TABLE_MESSAGE_FTS_PINYIN_INSTANCE, + DB_TABLE_MESSAGE_INSTANCE, TABLE_MESSAGE_FTS_CN_NAME, TABLE_MESSAGE_FTS_PINYIN_NAME, + TABLE_MESSAGE_NAME, + }; + use crate::crud::pinyin_dict::pinyin_map::PINYIN_MAP; + use crate::crud::pinyin_dict::traditional_chinese_map::TRADITIONAL_CHINESE_MAP; + use std::collections::HashMap; + use std::fs; + use std::time::{Duration, Instant}; + use wcdb::core::database::Database; + use wcdb::core::handle_operation::HandleOperationTrait; + use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + use wcdb::fts::builtin_tokenizer::BuiltinTokenizer; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + use wcdb::winq::statement_select::StatementSelect; + + fn setup(db_name: &str) -> Database { + BaseTestCase::global_set_up(); + let db = Database::new(&format!("./target/tmp/{}", db_name), None); + db.remove_files().unwrap(); + config_pinyin_dict(); + copy_db_file(db_name); + create_tables(&db); + create_trigger(&db); + db + } + + // #[test] + fn test_trigger_rebuild() { + let db_name = "cn_100k.db"; + let db = setup(db_name); + bench( + || { + // trigger_rebuild_all(&db); + trigger_rebuild_by_batch(&db); + }, + db_name, + 10, + ); + + search_data(&db); + } + + fn search_data(db: &Database) { + let queries = vec!["的", "de", "实践", "shi jian"]; + for query in queries { + let stat = { + let statement_select_v = StatementSelect::new(); + statement_select_v.select(vec![DbTableMessageFtsPinyin::id()]); + statement_select_v.from(vec![TABLE_MESSAGE_FTS_PINYIN_NAME]); + let exp = DbTableMessageFtsPinyin::search_word().match_(query); + statement_select_v.where_(&exp); + statement_select_v.union(); + statement_select_v.select(vec![DbTableMessageFtsCn::id()]); + statement_select_v.from(vec![TABLE_MESSAGE_FTS_CN_NAME]); + let exp = DbTableMessageFtsPinyin::search_word().match_(query); + statement_select_v.where_(&exp); + // let desc = statement_select_v.get_description(); + // println!("statement_select: {}", desc); + statement_select_v + }; + + let statement_select = StatementSelect::new(); + statement_select.select(DbTableMessage::all_fields()); + statement_select.from(vec![TABLE_MESSAGE_NAME]); + let exp = DbTableMessage::id().in_statement_select(&stat); + statement_select.where_(&exp); + let vec = db.get_all_rows_from_statement(&statement_select).unwrap(); + // assert_eq!(vec.len(), 1); + assert!(vec.len() >= 1); + } + } + + fn bench(mut f: F, db_name: &str, times: usize) + where + F: FnMut(), + { + let mut results = Vec::::new(); + + for _ in 0..times { + let start = Instant::now(); + f(); + results.push(start.elapsed()); + } + + let min = results.iter().min().unwrap(); + let max = results.iter().max().unwrap(); + let avg = results.iter().map(|d| d.as_nanos()).sum::() / results.len() as u128; + + println!("-------------- Benchmark Result --------------"); + println!("{} 次数: {}", db_name, times); + println!("最小耗时: {:?} (≈ {} µs)", min, min.as_micros()); + println!("最大耗时: {:?} (≈ {} µs)", max, max.as_micros()); + println!("平均耗时: {:?}ns (≈ {} µs)", avg, avg / 1000); + } + + fn trigger_rebuild_by_batch(db: &Database) { + init_fts_rebuild_progress(&db); + let batch_number = 1000; + let (mut last_processed_id, total_id) = get_current_rebuild_progress(&db); + while last_processed_id < total_id { + let end_id = if last_processed_id + batch_number < total_id { + last_processed_id + batch_number + } else { + total_id + }; + println!("qxb rebuild from {} to {}", last_processed_id, end_id); + + let sql = "BEGIN"; + db.execute_sql(sql).unwrap(); + let sql_pinyin = format!("INSERT INTO {}(rowid, id, search_word) SELECT id, id, search_word FROM table_message WHERE id > {} AND id <= {};", TABLE_MESSAGE_FTS_PINYIN_NAME, last_processed_id, end_id); + db.execute_sql(&sql_pinyin).unwrap(); + let sql_cn = format!("INSERT INTO {}(rowid, id, search_word) SELECT id, id, search_word FROM table_message WHERE id > {} AND id <= {};", TABLE_MESSAGE_FTS_CN_NAME, last_processed_id, end_id); + db.execute_sql(&sql_cn).unwrap(); + last_processed_id = end_id; + let mut table_progress = TableFtsRebuildProgress::default(); + table_progress.last_processed_id = last_processed_id; + let exp = DbTableFtsRebuildProgress::table_name().eq(TABLE_MESSAGE_NAME); + let fields = vec![DbTableFtsRebuildProgress::last_processed_id()]; + db.update_object( + table_progress, + fields, + TABLE_FTS_REBUILD_PROGRESS_NAME, + Some(&exp), + None, + None, + None, + ) + .unwrap(); + let sql = "COMMIT"; + db.execute_sql(sql).unwrap(); + } + } + + fn get_current_rebuild_progress(db: &Database) -> (i64, i64) { + let progress_opt = db + .get_first_object( + DbTableFtsRebuildProgress::all_fields(), + TABLE_FTS_REBUILD_PROGRESS_NAME, + None, + None, + None, + ) + .unwrap(); + match progress_opt { + None => (0, 0), + Some(progress) => (progress.last_processed_id, progress.total_id), + } + } + + fn init_fts_rebuild_progress(db: &Database) { + let id_max_exp = DbTableMessage::id().max(); + let statement_select = StatementSelect::new(); + statement_select.select(&vec![id_max_exp]); + statement_select.from(vec![TABLE_MESSAGE_NAME]); + let value = db.get_value_from_statement(&statement_select).unwrap(); + let id_max = value.get_i64(); + + let table_progress = TableFtsRebuildProgress::new(TABLE_MESSAGE_NAME, 0, id_max); + db.insert_or_ignore_object( + table_progress, + DbTableFtsRebuildProgress::all_fields(), + TABLE_FTS_REBUILD_PROGRESS_NAME, + ) + .unwrap(); + } + + fn trigger_rebuild_all(db: &Database) { + db.execute_sql(&format!( + "INSERT INTO {}({}) VALUES('rebuild');", + TABLE_MESSAGE_FTS_PINYIN_NAME, TABLE_MESSAGE_FTS_PINYIN_NAME + )) + .unwrap(); + db.execute_sql(&format!( + "INSERT INTO {}({}) VALUES('rebuild');", + TABLE_MESSAGE_FTS_CN_NAME, TABLE_MESSAGE_FTS_CN_NAME + )) + .unwrap(); + } + + fn copy_db_file(db_name: &str) { + let src_path = format!("./target/{}", db_name); + let target_path = format!("./target/tmp/{}", db_name); + if !fs::exists(&src_path).unwrap() { + panic!("{} does not exist", src_path); + } + fs::copy(src_path, target_path).unwrap(); + } + + fn config_pinyin_dict() { + let hash_map: HashMap<&str, Vec<&str>> = PINYIN_MAP + .entries() + .map(|(k, v)| (*k, v.to_vec())) + .collect(); + Database::config_pinyin_dict(&hash_map); + + let map: HashMap<&str, &str> = TRADITIONAL_CHINESE_MAP + .entries() + .map(|(k, v)| (*k, *v)) + .collect(); + Database::config_traditional_chinese_dict(&map); + } + + fn create_tables(db: &Database) { + db.add_tokenizer(BuiltinTokenizer::VERBATIM); + db.add_tokenizer(BuiltinTokenizer::PINYIN); + + db.create_table(TABLE_MESSAGE_NAME, &*DB_TABLE_MESSAGE_INSTANCE) + .unwrap(); + db.create_table( + TABLE_FTS_REBUILD_PROGRESS_NAME, + &*DB_TABLE_FTS_REBUILD_PROGRESS_INSTANCE, + ) + .unwrap(); + db.create_virtual_table( + TABLE_MESSAGE_FTS_PINYIN_NAME, + &*DB_TABLE_MESSAGE_FTS_PINYIN_INSTANCE, + ) + .unwrap(); + db.create_virtual_table( + TABLE_MESSAGE_FTS_CN_NAME, + &*DB_TABLE_MESSAGE_FTS_CN_INSTANCE, + ) + .unwrap(); + } + + fn create_trigger(db: &Database) { + // 处理 insert_or_replace : sql 逻辑是内部先 delete 后 insert,但是不会触发 delete 的触发器 + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_bi_cn BEFORE INSERT ON table_message + BEGIN + INSERT INTO table_message_fts_cn(table_message_fts_cn, rowid, search_word) + SELECT 'delete', id, search_word FROM table_message WHERE id = new.id; + END;"; + + db.execute_sql(&sql).unwrap(); + + // 处理 insert + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_ai_cn AFTER INSERT ON table_message + BEGIN + INSERT INTO table_message_fts_cn(rowid, id, search_word) + VALUES (new.id, new.id, new.search_word); + END;"; + + db.execute_sql(&sql).unwrap(); + + // 处理 delete + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_ad_cn AFTER DELETE ON table_message + BEGIN + INSERT INTO table_message_fts_cn(table_message_fts_cn, rowid, search_word) + VALUES ('delete', old.id, old.search_word); + END;"; + + db.execute_sql(&sql).unwrap(); + + // 处理 update : 先删后加 + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_au_cn AFTER UPDATE ON table_message + BEGIN + INSERT INTO table_message_fts_cn(table_message_fts_cn, rowid, search_word) + VALUES ('delete', old.id, old.search_word); + + INSERT INTO table_message_fts_cn(rowid, id, search_word) + VALUES (new.id, new.id, new.search_word); + END;"; + + db.execute_sql(&sql).unwrap(); + + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_bi_pinyin BEFORE INSERT ON table_message + BEGIN + INSERT INTO table_message_fts_pinyin(table_message_fts_pinyin, rowid, search_word) + SELECT 'delete', id, search_word FROM table_message WHERE id = new.id; + END;"; + + db.execute_sql(&sql).unwrap(); + + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_ai_pinyin AFTER INSERT ON table_message + BEGIN + INSERT INTO table_message_fts_pinyin(rowid, id, search_word) + VALUES (new.id, new.id, new.search_word); + END;"; + + db.execute_sql(&sql).unwrap(); + + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_ad_pinyin AFTER DELETE ON table_message + BEGIN + INSERT INTO table_message_fts_pinyin(table_message_fts_pinyin, rowid, search_word) + VALUES ('delete', old.id, old.search_word); + END;"; + + db.execute_sql(&sql).unwrap(); + + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_au_pinyin AFTER UPDATE ON table_message + BEGIN + INSERT INTO table_message_fts_pinyin(table_message_fts_pinyin, rowid, search_word) + VALUES ('delete', old.id, old.search_word); + + INSERT INTO table_message_fts_pinyin(rowid, id, search_word) + VALUES (new.id, new.id, new.search_word); + END;"; + + db.execute_sql(&sql).unwrap(); + } +} diff --git a/src/rust/examples/tests/crud/fts_trigger_test.rs b/src/rust/examples/tests/crud/fts_trigger_test.rs new file mode 100644 index 000000000..b64f4d993 --- /dev/null +++ b/src/rust/examples/tests/crud/fts_trigger_test.rs @@ -0,0 +1,443 @@ +use wcdb::core::handle_operation::HandleOperationTrait; +use wcdb_derive::WCDBTableCoding; + +pub static TABLE_MESSAGE_NAME: &str = "table_message"; + +#[derive(WCDBTableCoding)] +pub struct TableMessage { + #[WCDBField(is_primary = true, is_auto_increment = true)] + id: i64, + #[WCDBField] + content: String, + #[WCDBField] + search_word: String, +} + +impl TableMessage { + pub fn new() -> Self { + Self { + id: 1, + content: "".to_string(), + search_word: "".to_string(), + } + } +} + +pub static TABLE_MESSAGE_FTS_PINYIN_NAME: &str = "table_message_fts_pinyin"; + +#[derive(WCDBTableCoding, PartialEq, Clone)] +#[WCDBTable(fts_module( + version = "FTS5", + tokenizer = "wcdb_pinyin", + external_table = "table_message" +))] +pub struct TableMessageFtsPinyin { + #[WCDBField] + pub(crate) id: i64, + #[WCDBField] + pub(crate) search_word: String, +} + +impl TableMessageFtsPinyin { + pub(crate) fn new(search_word: &str) -> Self { + Self { + id: -1, + search_word: String::from(search_word), + } + } +} + +pub static TABLE_MESSAGE_FTS_CN_NAME: &str = "table_message_fts_cn"; +#[derive(WCDBTableCoding, PartialEq, Clone)] +#[WCDBTable( + fts_module(version = "FTS5", tokenizer = "wcdb_verbatim", tokenizer_parameters = ["skip_stemming", "chinese_traditional_to_simplified"], external_table = "table_message") +)] +pub struct TableMessageFtsCn { + #[WCDBField] + pub(crate) id: i64, + #[WCDBField] + pub(crate) search_word: String, +} + +impl TableMessageFtsCn { + pub(crate) fn new(search_word: &str) -> Self { + Self { + id: -1, + search_word: String::from(search_word), + } + } +} + +#[cfg(test)] +pub mod fts_trigger_test { + use crate::base::base_test_case::BaseTestCase; + use crate::crud::fts_trigger_test::{ + DbTableMessage, DbTableMessageFtsCn, DbTableMessageFtsPinyin, TableMessage, + DB_TABLE_MESSAGE_FTS_CN_INSTANCE, DB_TABLE_MESSAGE_FTS_PINYIN_INSTANCE, + DB_TABLE_MESSAGE_INSTANCE, TABLE_MESSAGE_FTS_CN_NAME, TABLE_MESSAGE_FTS_PINYIN_NAME, + TABLE_MESSAGE_NAME, + }; + use std::collections::HashMap; + use wcdb::core::database::Database; + use wcdb::core::handle_operation::HandleOperationTrait; + use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + use wcdb::fts::builtin_tokenizer::BuiltinTokenizer; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + use wcdb::winq::statement_select::StatementSelect; + + fn setup() -> Database { + BaseTestCase::global_set_up(); + let db = Database::new("./target/tmp/test.db", None); + db.remove_files().unwrap(); + config_pinyin_dict(); + create_tables(&db); + create_trigger(&db); + + db + } + + #[test] + fn test_insert() { + let db = setup(); + let (content, queries) = get_content1(); + insert_data(&db, content); + sync_all_data(&db); + search_all(&db, content, &queries, true); + + let (content, queries) = get_content2(); + insert_data(&db, content); + search_all(&db, content, &queries, true); + + let (content, queries) = get_content3(); + insert_data(&db, content); + search_all(&db, content, &queries, true); + } + + #[test] + fn test_update() { + let db = setup(); + // 插入新数据,触发器更新虚表。确保能搜到 + let (content1, queries1) = get_content1(); + insert_data(&db, content1); + search_all(&db, content1, &queries1, true); + + // 更新数据,触发器先删除旧数据,后更新新数据。确保旧数据搜不到,新数据能搜到 + let (content2, queries2) = get_content2(); + update_data(&db, content2); + search_all(&db, content1, &queries1, false); // 旧数据搜不到 + search_all(&db, content2, &queries2, true); // 新数据能搜到 + } + + #[test] + fn test_insert_or_replace() { + let db = setup(); + // 插入新数据,触发器更新虚表。确保能搜到 + let (content1, queries1) = get_content1(); + insert_data(&db, content1); + search_all(&db, content1, &queries1, true); + + // 替换数据,触发器先删除旧数据,后更新新数据。确保旧数据搜不到,新数据能搜到 + let (content2, queries2) = get_content2(); + insert_or_replace_data(&db, content2); + search_all(&db, content1, &queries1, false); + search_all(&db, content2, &queries2, true); + } + + // 这是一个异常的用例,触发器无法支持 insert_or_ignore + #[test] + fn test_insert_or_ignore() { + let db = setup(); + // 插入新数据,触发器更新虚表。确保能搜到 + let (content1, queries1) = get_content1(); + insert_data(&db, content1); + search_all(&db, content1, &queries1, true); + + let (content2, queries2) = get_content2(); + insert_or_ignore_data(&db, content2); + search_all(&db, content2, &queries2, false); + search_all(&db, content1, &queries1, false); // 此处被错误删除 + } + + #[test] + fn test_delete() { + let db = setup(); + // 插入新数据,触发器更新虚表。确保能搜到 + let (content1, queries1) = get_content1(); + insert_data(&db, content1); + search_all(&db, content1, &queries1, true); + + // 删除数据,触发器更新虚表。确保搜不到 + delete_data(&db, content1); + search_all(&db, content1, &queries1, false); + } + + fn search_all(db: &Database, content: &str, queries: &Vec<&str>, with_data: bool) { + for query in queries { + let stat = { + let statement_select_v = StatementSelect::new(); + statement_select_v.select(vec![DbTableMessageFtsPinyin::id()]); + statement_select_v.from(vec![TABLE_MESSAGE_FTS_PINYIN_NAME]); + let exp = DbTableMessageFtsPinyin::search_word().match_(query); + statement_select_v.where_(&exp); + statement_select_v.union(); + statement_select_v.select(vec![DbTableMessageFtsCn::id()]); + statement_select_v.from(vec![TABLE_MESSAGE_FTS_CN_NAME]); + let exp = DbTableMessageFtsPinyin::search_word().match_(query); + statement_select_v.where_(&exp); + // let desc = statement_select_v.get_description(); + // println!("statement_select: {}", desc); + statement_select_v + }; + + let statement_select = StatementSelect::new(); + statement_select.select(DbTableMessage::all_fields()); + statement_select.from(vec![TABLE_MESSAGE_NAME]); + let exp = DbTableMessage::id().in_statement_select(&stat); + statement_select.where_(&exp); + let vec = db.get_all_rows_from_statement(&statement_select).unwrap(); + // let desc = statement_select.get_description(); + // println!("statement_select: {}", desc); + if with_data { + assert_eq!(vec.len(), 1); + assert_eq!(vec[0].len(), 3); + assert_eq!(vec[0][2].get_text(), content); + } else { + assert_eq!(vec.len(), 0); + } + } + } + + fn insert_data(db: &Database, content: &str) { + let mut msg_box = TableMessage::new(); + msg_box.content = content.to_string(); + msg_box.search_word = content.to_string(); + let mut fields = DbTableMessage::all_fields(); + fields.retain(|field| field.get_name() != "id"); + db.insert_object(msg_box, fields, TABLE_MESSAGE_NAME) + .unwrap(); + } + + fn update_data(db: &Database, content: &str) { + let exp = DbTableMessage::id().eq(1); + let mut msg_box = TableMessage::new(); + msg_box.id = 1; + msg_box.content = content.to_string(); + msg_box.search_word = content.to_string(); + let fields = DbTableMessage::all_fields(); + + db.update_object( + msg_box, + fields, + TABLE_MESSAGE_NAME, + Some(&exp), + None, + None, + None, + ) + .unwrap(); + } + + fn insert_or_replace_data(db: &Database, content: &str) { + let mut msg_box = TableMessage::new(); + msg_box.id = 1; + msg_box.content = content.to_string(); + msg_box.search_word = content.to_string(); + let fields = DbTableMessage::all_fields(); + + db.insert_or_replace_object(msg_box, fields, TABLE_MESSAGE_NAME) + .unwrap(); + } + + fn insert_or_ignore_data(db: &Database, content: &str) { + let mut msg_box = TableMessage::new(); + msg_box.id = 1; + msg_box.content = content.to_string(); + msg_box.search_word = content.to_string(); + let fields = DbTableMessage::all_fields(); + + db.insert_or_ignore_object(msg_box, fields, TABLE_MESSAGE_NAME) + .unwrap(); + } + + fn delete_data(db: &Database, content: &str) { + let exp = DbTableMessage::content().eq(content); + db.delete_objects(TABLE_MESSAGE_NAME, Some(&exp), None, None, None) + .unwrap(); + } + + fn sync_all_data(db: &Database) { + db.execute_sql(&format!( + "INSERT INTO {}({}) VALUES('rebuild');", + TABLE_MESSAGE_FTS_PINYIN_NAME, TABLE_MESSAGE_FTS_PINYIN_NAME + )) + .unwrap(); + + db.execute_sql(&format!( + "INSERT INTO {}({}) VALUES('rebuild');", + TABLE_MESSAGE_FTS_CN_NAME, TABLE_MESSAGE_FTS_CN_NAME + )) + .unwrap(); + } + + fn get_content3() -> (&'static str, Vec<&'static str>) { + let content = "what's wrong with you?"; + let queries = vec!["wrong with you", "what", "wrong", "with", "you"]; + (content, queries) + } + + fn get_content2() -> (&'static str, Vec<&'static str>) { + let content = "中国人"; + let queries = vec![ + "中国人", + "中国", + "国人", + "中", + "国", + "人", + "zhong guo ren", + "zhong guo", + "guo ren", + "zhong", + "guo", + "ren", + ]; + (content, queries) + } + + fn get_content1() -> (&'static str, Vec<&'static str>) { + let content = "单于骑模具单车"; + let queries = vec![ + "\"shan yu qi mu ju dan che\"", + "\"chan yu\"", + "\"dan yu qi mo ju chan che\"", + "\"dan yu qi mu ju ch\"*", + "\"dan yu qi mo ju d\"*", + "\"s y q m j d c\"", + "\"c y q m j s c\"", + "\"c y q m j\"", + "单于", + "\"单yu\"", + "单于骑模具单车", + "模具", + "单车", + "车", + ]; + (content, queries) + } + + fn create_trigger(db: &Database) { + // 处理 insert_or_replace : sql 逻辑是内部先 delete 后 insert,但是不会触发 delete 的触发器 + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_bi_cn BEFORE INSERT ON table_message + BEGIN + INSERT INTO table_message_fts_cn(table_message_fts_cn, rowid, search_word) + SELECT 'delete', id, search_word FROM table_message WHERE id = new.id; + END;"; + + db.execute_sql(&sql).unwrap(); + + // 处理 insert + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_ai_cn AFTER INSERT ON table_message + BEGIN + INSERT INTO table_message_fts_cn(rowid, id, search_word) + VALUES (new.id, new.id, new.search_word); + END;"; + + db.execute_sql(&sql).unwrap(); + + // 处理 delete + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_ad_cn AFTER DELETE ON table_message + BEGIN + INSERT INTO table_message_fts_cn(table_message_fts_cn, rowid, search_word) + VALUES ('delete', old.id, old.search_word); + END;"; + + db.execute_sql(&sql).unwrap(); + + // 处理 update : 先删后加 + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_au_cn AFTER UPDATE ON table_message + BEGIN + INSERT INTO table_message_fts_cn(table_message_fts_cn, rowid, search_word) + VALUES ('delete', old.id, old.search_word); + + INSERT INTO table_message_fts_cn(rowid, id, search_word) + VALUES (new.id, new.id, new.search_word); + END;"; + + db.execute_sql(&sql).unwrap(); + + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_bi_pinyin BEFORE INSERT ON table_message + BEGIN + INSERT INTO table_message_fts_pinyin(table_message_fts_pinyin, rowid, search_word) + SELECT 'delete', id, search_word FROM table_message WHERE id = new.id; + END;"; + + db.execute_sql(&sql).unwrap(); + + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_ai_pinyin AFTER INSERT ON table_message + BEGIN + INSERT INTO table_message_fts_pinyin(rowid, id, search_word) + VALUES (new.id, new.id, new.search_word); + END;"; + + db.execute_sql(&sql).unwrap(); + + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_ad_pinyin AFTER DELETE ON table_message + BEGIN + INSERT INTO table_message_fts_pinyin(table_message_fts_pinyin, rowid, search_word) + VALUES ('delete', old.id, old.search_word); + END;"; + + db.execute_sql(&sql).unwrap(); + + let sql = " + CREATE TRIGGER IF NOT EXISTS table_message_au_pinyin AFTER UPDATE ON table_message + BEGIN + INSERT INTO table_message_fts_pinyin(table_message_fts_pinyin, rowid, search_word) + VALUES ('delete', old.id, old.search_word); + + INSERT INTO table_message_fts_pinyin(rowid, id, search_word) + VALUES (new.id, new.id, new.search_word); + END;"; + + db.execute_sql(&sql).unwrap(); + } + + fn create_tables(db: &Database) { + db.add_tokenizer(BuiltinTokenizer::VERBATIM); + db.add_tokenizer(BuiltinTokenizer::PINYIN); + + db.create_table(TABLE_MESSAGE_NAME, &*DB_TABLE_MESSAGE_INSTANCE) + .unwrap(); + db.create_virtual_table( + TABLE_MESSAGE_FTS_PINYIN_NAME, + &*DB_TABLE_MESSAGE_FTS_PINYIN_INSTANCE, + ) + .unwrap(); + db.create_virtual_table( + TABLE_MESSAGE_FTS_CN_NAME, + &*DB_TABLE_MESSAGE_FTS_CN_INSTANCE, + ) + .unwrap(); + } + + fn config_pinyin_dict() { + Database::config_pinyin_dict(&HashMap::from([ + ("单", vec!["shan", "dan", "chan"]), + ("于", vec!["yu"]), + ("骑", vec!["qi"]), + ("模", vec!["mo", "mu"]), + ("具", vec!["ju"]), + ("车", vec!["che"]), + ("中", vec!["zhong"]), + ("国", vec!["guo"]), + ("人", vec!["ren"]), + ])); + } +} diff --git a/src/rust/examples/tests/crud/mod.rs b/src/rust/examples/tests/crud/mod.rs new file mode 100644 index 000000000..119006532 --- /dev/null +++ b/src/rust/examples/tests/crud/mod.rs @@ -0,0 +1,5 @@ +pub mod fts_test; +pub mod fts_trigger_perf_test; +pub mod fts_trigger_test; +pub mod object_select_test; +mod pinyin_dict; diff --git a/src/rust/examples/tests/crud/object_select_test.rs b/src/rust/examples/tests/crud/object_select_test.rs new file mode 100644 index 000000000..783ca39e2 --- /dev/null +++ b/src/rust/examples/tests/crud/object_select_test.rs @@ -0,0 +1,2 @@ +#[cfg(test)] +pub mod object_select_test {} diff --git a/src/rust/examples/tests/crud/pinyin_dict/mod.rs b/src/rust/examples/tests/crud/pinyin_dict/mod.rs new file mode 100644 index 000000000..0713b1af6 --- /dev/null +++ b/src/rust/examples/tests/crud/pinyin_dict/mod.rs @@ -0,0 +1,4 @@ +pub(crate) mod pinyin_map; +pub(crate) mod pinyin_set; +pub(crate) mod pinyin_set_util; +pub(crate) mod traditional_chinese_map; diff --git a/src/rust/examples/tests/crud/pinyin_dict/pinyin_map.rs b/src/rust/examples/tests/crud/pinyin_dict/pinyin_map.rs new file mode 100644 index 000000000..8b3a0e32b --- /dev/null +++ b/src/rust/examples/tests/crud/pinyin_dict/pinyin_map.rs @@ -0,0 +1,20387 @@ +//! Auto-generated file. DO NOT EDIT MANUALLY. +// Generated at: 2025-12-10 11:11:07 UTC +// Source URL: https://raw.githubusercontent.com/infinilabs/analysis-pinyin/refs/heads/master/pinyin-core/src/main/resources/pinyin.txt +// Total records: 20378 + +use phf::phf_map; + +pub static PINYIN_MAP: phf::Map<&'static str, &'static [&'static str]> = phf_map! { + "一" => &["yi"], + "丁" => &["ding","zheng"], + "丂" => &["kao"], + "七" => &["qi"], + "丄" => &["shang"], + "丅" => &["xia"], + "万" => &["wan","mo"], + "丈" => &["zhang"], + "三" => &["san"], + "上" => &["shang"], + "下" => &["xia"], + "丌" => &["ji"], + "不" => &["bu","fou"], + "与" => &["yu"], + "丏" => &["mian"], + "丐" => &["gai"], + "丑" => &["chou"], + "丒" => &["chou"], + "专" => &["zhuan"], + "且" => &["qie","ju"], + "丕" => &["pi"], + "世" => &["shi"], + "丗" => &["shi"], + "丘" => &["qiu"], + "丙" => &["bing"], + "业" => &["ye"], + "丛" => &["cong"], + "东" => &["dong"], + "丝" => &["si"], + "丞" => &["cheng"], + "丟" => &["diu"], + "丠" => &["qiu"], + "両" => &["liang"], + "丢" => &["diu"], + "丣" => &["you"], + "两" => &["liang"], + "严" => &["yan"], + "並" => &["bing"], + "丧" => &["sang"], + "丨" => &["shu"], + "丩" => &["jiu"], + "个" => &["ge"], + "丫" => &["ya"], + "丬" => &["qiang","pan"], + "中" => &["zhong"], + "丮" => &["ji"], + "丯" => &["jie"], + "丰" => &["feng"], + "丱" => &["guan"], + "串" => &["chuan"], + "丳" => &["chan"], + "临" => &["lin"], + "丵" => &["zhuo"], + "丶" => &["zhu","dian"], + "丸" => &["wan"], + "丹" => &["dan"], + "为" => &["wei"], + "主" => &["zhu"], + "丼" => &["jing","dan"], + "丽" => &["li"], + "举" => &["ju"], + "丿" => &["pie"], + "乀" => &["fu"], + "乁" => &["yi"], + "乂" => &["yi","ai"], + "乃" => &["nai"], + "久" => &["jiu"], + "乆" => &["jiu"], + "乇" => &["tuo"], + "么" => &["me","ma","yao"], + "义" => &["yi"], + "之" => &["zhi"], + "乌" => &["wu"], + "乍" => &["zha"], + "乎" => &["hu"], + "乏" => &["fa"], + "乐" => &["le","yue"], + "乑" => &["zhong"], + "乒" => &["ping"], + "乓" => &["pang"], + "乔" => &["qiao"], + "乕" => &["hu"], + "乖" => &["guai"], + "乗" => &["cheng","sheng"], + "乘" => &["cheng","sheng"], + "乙" => &["yi"], + "乚" => &["yin"], + "乜" => &["mie","nie"], + "九" => &["jiu"], + "乞" => &["qi"], + "也" => &["ye"], + "习" => &["xi"], + "乡" => &["xiang"], + "乢" => &["gai"], + "乣" => &["diu"], + "书" => &["shu"], + "乨" => &["shi"], + "乩" => &["ji"], + "乪" => &["nang"], + "乫" => &["jia"], + "乭" => &["shi"], + "买" => &["mai"], + "乱" => &["luan"], + "乳" => &["ru"], + "乴" => &["xi"], + "乵" => &["yan"], + "乶" => &["fu"], + "乷" => &["sha"], + "乸" => &["na"], + "乹" => &["gan","qian"], + "乾" => &["qian","gan"], + "乿" => &["zhi"], + "亀" => &["gui","jun","qiu"], + "亁" => &["gan"], + "亂" => &["luan"], + "亃" => &["lin"], + "亄" => &["yi"], + "亅" => &["jue"], + "了" => &["le","liao"], + "予" => &["yu"], + "争" => &["zheng"], + "亊" => &["shi"], + "事" => &["shi"], + "二" => &["er"], + "亍" => &["chu"], + "于" => &["yu"], + "亏" => &["kui"], + "亐" => &["yu"], + "云" => &["yun"], + "互" => &["hu"], + "亓" => &["qi"], + "五" => &["wu"], + "井" => &["jing"], + "亖" => &["si"], + "亗" => &["sui"], + "亘" => &["gen"], + "亙" => &["gen","geng"], + "亚" => &["ya"], + "些" => &["xie"], + "亜" => &["ya"], + "亝" => &["qi"], + "亞" => &["ya"], + "亟" => &["ji","qi"], + "亠" => &["tou"], + "亡" => &["wang","wu"], + "亢" => &["kang"], + "亣" => &["ta"], + "交" => &["jiao"], + "亥" => &["hai"], + "亦" => &["yi"], + "产" => &["chan"], + "亨" => &["heng"], + "亩" => &["mu"], + "享" => &["xiang"], + "京" => &["jing"], + "亭" => &["ting"], + "亮" => &["liang"], + "亯" => &["heng"], + "亰" => &["jing"], + "亱" => &["ye"], + "亲" => &["qin","qing"], + "亳" => &["bo"], + "亴" => &["you"], + "亵" => &["xie"], + "亶" => &["dan"], + "亷" => &["lian"], + "亸" => &["duo"], + "亹" => &["wei"], + "人" => &["ren"], + "亻" => &["ren"], + "亼" => &["ji"], + "亾" => &["wang"], + "亿" => &["yi"], + "什" => &["shen","shi","she"], + "仁" => &["ren"], + "仂" => &["le"], + "仃" => &["ding"], + "仄" => &["ze"], + "仅" => &["jin"], + "仆" => &["pu"], + "仇" => &["chou","qiu"], + "仈" => &["ba"], + "仉" => &["zhang"], + "今" => &["jin"], + "介" => &["jie"], + "仌" => &["bing"], + "仍" => &["reng"], + "从" => &["cong"], + "仏" => &["fo","fu"], + "仐" => &["san"], + "仑" => &["lun"], + "仓" => &["cang"], + "仔" => &["zi","zai"], + "仕" => &["shi"], + "他" => &["ta"], + "仗" => &["zhang"], + "付" => &["fu"], + "仙" => &["xian"], + "仚" => &["xian"], + "仛" => &["cha"], + "仜" => &["hong"], + "仝" => &["tong"], + "仞" => &["ren"], + "仟" => &["qian"], + "仠" => &["gan"], + "仡" => &["ge","yi"], + "仢" => &["di"], + "代" => &["dai"], + "令" => &["ling"], + "以" => &["yi"], + "仦" => &["chao"], + "仧" => &["chang"], + "仨" => &["sa"], + "仩" => &["shang"], + "仪" => &["yi"], + "仫" => &["mu"], + "们" => &["men"], + "仭" => &["ren"], + "仮" => &["jia"], + "仯" => &["chao"], + "仰" => &["yang"], + "仱" => &["qian"], + "仲" => &["zhong"], + "仳" => &["pi"], + "仴" => &["wan"], + "仵" => &["wu"], + "件" => &["jian"], + "价" => &["jia","jie"], + "仸" => &["yao"], + "仹" => &["feng"], + "仺" => &["cang"], + "任" => &["ren"], + "仼" => &["wang"], + "份" => &["fen"], + "仾" => &["di"], + "仿" => &["fang"], + "伀" => &["zhong"], + "企" => &["qi"], + "伂" => &["pei"], + "伃" => &["yu"], + "伄" => &["diao"], + "伅" => &["dun"], + "伆" => &["wen"], + "伇" => &["yi"], + "伈" => &["xin"], + "伉" => &["kang"], + "伊" => &["yi"], + "伋" => &["ji"], + "伌" => &["ai"], + "伍" => &["wu"], + "伎" => &["ji"], + "伏" => &["fu"], + "伐" => &["fa"], + "休" => &["xiu"], + "伒" => &["jin"], + "伓" => &["bei"], + "伔" => &["chen"], + "伕" => &["fu"], + "伖" => &["tang"], + "众" => &["zhong"], + "优" => &["you"], + "伙" => &["huo"], + "会" => &["hui","kuai"], + "伛" => &["yu"], + "伜" => &["cui","zu"], + "伝" => &["yun"], + "伞" => &["san"], + "伟" => &["wei"], + "传" => &["chuan","zhuan"], + "伡" => &["che"], + "伢" => &["ya"], + "伣" => &["xian"], + "伤" => &["shang"], + "伥" => &["chang","tang"], + "伦" => &["lun"], + "伧" => &["cang","chen"], + "伨" => &["xun"], + "伩" => &["xin"], + "伪" => &["wei"], + "伫" => &["zhu"], + "伬" => &["chi"], + "伭" => &["xuan"], + "伮" => &["nao","nu"], + "伯" => &["bo","bai","ba"], + "估" => &["gu"], + "伱" => &["ni"], + "伲" => &["ni"], + "伳" => &["xie"], + "伴" => &["ban"], + "伵" => &["xu"], + "伶" => &["ling"], + "伷" => &["zhou"], + "伸" => &["shen"], + "伹" => &["qu"], + "伺" => &["si","ci"], + "伻" => &["beng"], + "似" => &["si","shi"], + "伽" => &["jia","ga","qie","qia"], + "伾" => &["pi"], + "伿" => &["yi"], + "佀" => &["si","shi"], + "佁" => &["ai"], + "佂" => &["zheng"], + "佃" => &["dian","tian"], + "佄" => &["han"], + "佅" => &["mai"], + "但" => &["dan"], + "佇" => &["zhu"], + "佈" => &["bu"], + "佉" => &["qu"], + "佊" => &["bi"], + "佋" => &["shao"], + "佌" => &["ci"], + "位" => &["wei"], + "低" => &["di"], + "住" => &["zhu"], + "佐" => &["zuo"], + "佑" => &["you"], + "佒" => &["yang"], + "体" => &["ti","ben"], + "佔" => &["zhan"], + "何" => &["he"], + "佖" => &["bi"], + "佗" => &["tuo"], + "佘" => &["she"], + "余" => &["yu","tu"], + "佚" => &["yi","die"], + "佛" => &["fo","fu"], + "作" => &["zuo"], + "佝" => &["gou"], + "佞" => &["ning"], + "佟" => &["tong"], + "你" => &["ni"], + "佡" => &["xuan","san"], + "佢" => &["ju"], + "佣" => &["yong"], + "佤" => &["wa"], + "佥" => &["qian"], + "佧" => &["ka"], + "佩" => &["pei"], + "佪" => &["huai"], + "佫" => &["he"], + "佬" => &["lao"], + "佭" => &["xiang"], + "佮" => &["ge"], + "佯" => &["yang"], + "佰" => &["bai"], + "佱" => &["fa"], + "佲" => &["ming"], + "佳" => &["jia"], + "佴" => &["nai","er"], + "併" => &["bing"], + "佶" => &["ji"], + "佷" => &["heng"], + "佸" => &["huo"], + "佹" => &["gui"], + "佺" => &["quan"], + "佻" => &["tiao"], + "佼" => &["jiao","jia"], + "佽" => &["ci"], + "佾" => &["yi"], + "使" => &["shi"], + "侀" => &["xing"], + "侁" => &["shen"], + "侂" => &["tuo"], + "侃" => &["kan"], + "侄" => &["zhi"], + "侅" => &["gai","kai"], + "來" => &["lai"], + "侇" => &["yi"], + "侈" => &["chi"], + "侉" => &["kua"], + "侊" => &["guang"], + "例" => &["li"], + "侌" => &["yin"], + "侍" => &["shi"], + "侎" => &["mi"], + "侏" => &["zhu"], + "侐" => &["xu"], + "侑" => &["you"], + "侒" => &["an"], + "侓" => &["lu"], + "侔" => &["mou"], + "侕" => &["er"], + "侖" => &["lun"], + "侗" => &["dong","tong"], + "侘" => &["cha"], + "侙" => &["chi"], + "侚" => &["xun"], + "供" => &["gong"], + "侜" => &["zhou"], + "依" => &["yi"], + "侞" => &["ru"], + "侟" => &["jian"], + "侠" => &["xia"], + "価" => &["jia","jie"], + "侢" => &["zai"], + "侣" => &["lu:"], + "侥" => &["jiao","yao","jia"], + "侦" => &["zhen"], + "侧" => &["ce","ze","zhai"], + "侨" => &["qiao"], + "侩" => &["kuai"], + "侪" => &["chai"], + "侫" => &["ning"], + "侬" => &["nong"], + "侭" => &["jin"], + "侮" => &["wu"], + "侯" => &["hou"], + "侰" => &["jiong"], + "侱" => &["cheng"], + "侲" => &["zhen"], + "侳" => &["cuo"], + "侴" => &["chou"], + "侵" => &["qin"], + "侶" => &["lu:"], + "侷" => &["ju"], + "侸" => &["shu"], + "侹" => &["ting"], + "侺" => &["shen"], + "侻" => &["tuo"], + "侼" => &["bo"], + "侽" => &["nan"], + "侾" => &["hao"], + "便" => &["bian","pian"], + "俀" => &["tui"], + "俁" => &["yu"], + "係" => &["xi"], + "促" => &["cu"], + "俄" => &["e"], + "俅" => &["qiu"], + "俆" => &["xu"], + "俇" => &["kuang"], + "俈" => &["ku"], + "俉" => &["wu"], + "俊" => &["jun","zun","juan"], + "俋" => &["yi"], + "俌" => &["fu"], + "俍" => &["lang"], + "俎" => &["zu"], + "俏" => &["qiao"], + "俐" => &["li"], + "俑" => &["yong"], + "俒" => &["hun"], + "俓" => &["jing"], + "俔" => &["xian"], + "俕" => &["san"], + "俖" => &["pai"], + "俗" => &["su"], + "俘" => &["fu"], + "俙" => &["xi"], + "俚" => &["li"], + "俛" => &["mian"], + "俜" => &["ping"], + "保" => &["bao"], + "俞" => &["yu","shu"], + "俟" => &["si","qi"], + "俠" => &["xia"], + "信" => &["xin","shen"], + "俢" => &["xiu"], + "俣" => &["yu"], + "俤" => &["ti"], + "俥" => &["che"], + "俦" => &["chou"], + "俨" => &["yan"], + "俩" => &["liang","lia"], + "俪" => &["li"], + "俫" => &["lai"], + "俬" => &["si"], + "俭" => &["jian"], + "修" => &["xiu"], + "俯" => &["fu"], + "俰" => &["he"], + "俱" => &["ju"], + "俲" => &["xiao"], + "俳" => &["pai"], + "俴" => &["jian"], + "俵" => &["biao"], + "俶" => &["chu","ti"], + "俷" => &["fei"], + "俸" => &["feng"], + "俹" => &["ya"], + "俺" => &["an"], + "俻" => &["bei"], + "俼" => &["yu","zhou"], + "俽" => &["xin"], + "俾" => &["bi","bei"], + "俿" => &["chi"], + "倀" => &["chang"], + "倁" => &["zhi"], + "倂" => &["bing"], + "倃" => &["zan"], + "倄" => &["yao"], + "倅" => &["cui"], + "倆" => &["lia","liang"], + "倇" => &["wan"], + "倈" => &["lai"], + "倉" => &["cang"], + "倊" => &["zong"], + "個" => &["ge"], + "倌" => &["guan"], + "倍" => &["bei"], + "倎" => &["tian"], + "倏" => &["shu"], + "倐" => &["shu"], + "們" => &["men"], + "倒" => &["dao"], + "倓" => &["tan"], + "倔" => &["jue"], + "倕" => &["chui"], + "倖" => &["xing"], + "倗" => &["peng"], + "倘" => &["tang","chang"], + "候" => &["hou"], + "倚" => &["yi"], + "倛" => &["qi"], + "倜" => &["ti"], + "倝" => &["gan"], + "倞" => &["jing","liang"], + "借" => &["jie"], + "倠" => &["xu"], + "倡" => &["chang"], + "倢" => &["jie"], + "倣" => &["fang"], + "値" => &["zhi"], + "倥" => &["kong"], + "倦" => &["juan"], + "倧" => &["zong"], + "倨" => &["ju"], + "倩" => &["qian"], + "倪" => &["ni"], + "倫" => &["lun"], + "倬" => &["zhuo"], + "倭" => &["wo"], + "倮" => &["luo"], + "倯" => &["song"], + "倰" => &["leng"], + "倱" => &["hun"], + "倲" => &["dong"], + "倳" => &["zi"], + "倴" => &["ben"], + "倵" => &["wu"], + "倶" => &["ju"], + "倷" => &["nai"], + "倸" => &["cai"], + "倹" => &["jian"], + "债" => &["zhai"], + "倻" => &["ye"], + "值" => &["zhi"], + "倽" => &["sha"], + "倾" => &["qing"], + "偀" => &["ying"], + "偁" => &["cheng"], + "偂" => &["qian"], + "偃" => &["yan"], + "偄" => &["nuan"], + "偅" => &["zhong"], + "偆" => &["chun"], + "假" => &["jia"], + "偈" => &["jie","ji"], + "偉" => &["wei"], + "偊" => &["yu"], + "偋" => &["bing"], + "偌" => &["ruo"], + "偍" => &["ti"], + "偎" => &["wei"], + "偏" => &["pian"], + "偐" => &["yan"], + "偑" => &["feng"], + "偒" => &["tang"], + "偓" => &["wo"], + "偔" => &["e"], + "偕" => &["xie","jie"], + "偖" => &["che"], + "偗" => &["sheng"], + "偘" => &["kan"], + "偙" => &["di"], + "做" => &["zuo"], + "偛" => &["cha"], + "停" => &["ting"], + "偝" => &["bei"], + "偞" => &["ye"], + "偟" => &["huang"], + "偠" => &["yao"], + "偡" => &["zhan"], + "偢" => &["qiu"], + "偣" => &["yan"], + "偤" => &["you"], + "健" => &["jian"], + "偦" => &["xu"], + "偧" => &["zha"], + "偨" => &["chai"], + "偩" => &["fu"], + "偪" => &["bi"], + "偫" => &["zhi"], + "偬" => &["zong"], + "偭" => &["mian"], + "偮" => &["ji"], + "偯" => &["yi"], + "偰" => &["xie"], + "偱" => &["xun"], + "偲" => &["si","cai"], + "偳" => &["duan"], + "側" => &["ce","ze"], + "偵" => &["zhen"], + "偶" => &["ou"], + "偷" => &["tou"], + "偸" => &["tou"], + "偹" => &["bei"], + "偺" => &["za","zan"], + "偻" => &["lou","lu:"], + "偼" => &["jie"], + "偽" => &["wei"], + "偾" => &["fen"], + "偿" => &["chang"], + "傀" => &["kui","gui"], + "傁" => &["sou"], + "傂" => &["chi"], + "傃" => &["su"], + "傄" => &["xia"], + "傅" => &["fu"], + "傆" => &["yuan"], + "傇" => &["rong"], + "傈" => &["li"], + "傉" => &["ru"], + "傊" => &["yun"], + "傋" => &["gou"], + "傌" => &["ma"], + "傍" => &["bang"], + "傎" => &["dian"], + "傏" => &["tang"], + "傐" => &["hao"], + "傑" => &["jie"], + "傒" => &["xi"], + "傓" => &["shan"], + "傔" => &["qian"], + "傕" => &["jue"], + "傖" => &["cang"], + "傗" => &["chu"], + "傘" => &["san"], + "備" => &["bei"], + "傚" => &["xiao"], + "傛" => &["yong"], + "傜" => &["yao"], + "傝" => &["ta"], + "傞" => &["suo"], + "傟" => &["wang"], + "傠" => &["fa"], + "傡" => &["bing"], + "傢" => &["jia"], + "傣" => &["dai"], + "傤" => &["zai"], + "傥" => &["tang"], + "傧" => &["bin"], + "储" => &["chu"], + "傩" => &["nuo"], + "傪" => &["zan"], + "傫" => &["lei"], + "催" => &["cui"], + "傭" => &["yong"], + "傮" => &["zao"], + "傯" => &["zong"], + "傰" => &["peng"], + "傱" => &["song"], + "傲" => &["ao"], + "傳" => &["chuan","zhuan"], + "傴" => &["yu"], + "債" => &["zhai"], + "傶" => &["zu"], + "傷" => &["shang"], + "傸" => &["qiang"], + "傹" => &["qiang"], + "傺" => &["chi"], + "傻" => &["sha"], + "傼" => &["han"], + "傽" => &["zhang"], + "傾" => &["qing"], + "傿" => &["yan"], + "僀" => &["di"], + "僁" => &["xi"], + "僂" => &["lou","lu:"], + "僃" => &["bei"], + "僄" => &["piao"], + "僅" => &["jin"], + "僆" => &["lian"], + "僇" => &["lu"], + "僈" => &["man"], + "僉" => &["qian"], + "僊" => &["xian"], + "僋" => &["qiu"], + "僌" => &["ying"], + "働" => &["dong"], + "僎" => &["zhuan"], + "像" => &["xiang"], + "僐" => &["shan"], + "僑" => &["qiao"], + "僒" => &["jiong"], + "僓" => &["tui"], + "僔" => &["zun"], + "僕" => &["pu"], + "僖" => &["xi"], + "僗" => &["lao"], + "僘" => &["chang"], + "僙" => &["guang"], + "僚" => &["liao"], + "僛" => &["qi"], + "僜" => &["deng"], + "僝" => &["chan"], + "僞" => &["wei"], + "僟" => &["zhang"], + "僠" => &["fan"], + "僡" => &["hui"], + "僢" => &["chuan"], + "僣" => &["tie"], + "僤" => &["dan"], + "僥" => &["jiao","yao"], + "僦" => &["jiu"], + "僧" => &["seng"], + "僨" => &["fen"], + "僩" => &["xian"], + "僪" => &["jue"], + "僫" => &["e"], + "僬" => &["jiao"], + "僭" => &["jian"], + "僮" => &["tong","zhuang"], + "僯" => &["lin"], + "僰" => &["bo"], + "僱" => &["gu"], + "僲" => &["xian"], + "僳" => &["su"], + "僴" => &["xian"], + "僵" => &["jiang"], + "僶" => &["min"], + "僷" => &["ye"], + "僸" => &["jin"], + "價" => &["jia"], + "僺" => &["qiao"], + "僻" => &["pi"], + "僼" => &["feng"], + "僽" => &["zhou"], + "僾" => &["ai"], + "僿" => &["sai"], + "儀" => &["yi"], + "儁" => &["jun","juan"], + "儂" => &["nong"], + "儃" => &["shan"], + "億" => &["yi"], + "儅" => &["dang"], + "儆" => &["jing"], + "儇" => &["xuan"], + "儈" => &["kuai"], + "儉" => &["jian"], + "儊" => &["chu"], + "儋" => &["dan"], + "儌" => &["jiao"], + "儍" => &["sha"], + "儎" => &["zai"], + "儐" => &["bin"], + "儑" => &["an"], + "儒" => &["ru"], + "儓" => &["tai"], + "儔" => &["chou"], + "儕" => &["chai"], + "儖" => &["lan"], + "儗" => &["ni"], + "儘" => &["jin"], + "儙" => &["qian"], + "儚" => &["meng"], + "儛" => &["wu"], + "儜" => &["neng"], + "儝" => &["qiong"], + "儞" => &["ni"], + "償" => &["chang"], + "儠" => &["lie"], + "儡" => &["lei"], + "儢" => &["lu:"], + "儣" => &["kuang"], + "儤" => &["bao"], + "儥" => &["du"], + "儦" => &["biao"], + "儧" => &["zan"], + "儨" => &["zhi"], + "儩" => &["si"], + "優" => &["you"], + "儫" => &["hao"], + "儬" => &["qin"], + "儭" => &["chen"], + "儮" => &["li"], + "儯" => &["teng"], + "儰" => &["wei"], + "儱" => &["long"], + "儲" => &["chu"], + "儳" => &["chan"], + "儴" => &["rang"], + "儵" => &["shu"], + "儶" => &["hui"], + "儷" => &["li"], + "儸" => &["luo"], + "儹" => &["zan","zuan"], + "儺" => &["nuo"], + "儻" => &["tang"], + "儼" => &["yan"], + "儽" => &["lei"], + "儾" => &["nang"], + "儿" => &["er","r"], + "兀" => &["wu"], + "允" => &["yun"], + "兂" => &["zan"], + "元" => &["yuan"], + "兄" => &["xiong"], + "充" => &["chong"], + "兆" => &["zhao"], + "兇" => &["xiong"], + "先" => &["xian"], + "光" => &["guang"], + "兊" => &["dui"], + "克" => &["ke"], + "兌" => &["dui"], + "免" => &["mian","wen"], + "兎" => &["tu"], + "兏" => &["chang","zhang"], + "児" => &["er"], + "兑" => &["dui"], + "兒" => &["er"], + "兓" => &["jin"], + "兔" => &["tu"], + "兕" => &["si"], + "兖" => &["yan"], + "兗" => &["yan"], + "兘" => &["shi"], + "兙" => &["shi","ke"], + "党" => &["dang"], + "兛" => &["qian"], + "兜" => &["dou"], + "兝" => &["fen"], + "兞" => &["mao"], + "兟" => &["xin"], + "兠" => &["dou"], + "兡" => &["bai","ke"], + "兢" => &["jing"], + "兣" => &["li"], + "兤" => &["kuang"], + "入" => &["ru"], + "兦" => &["wang","wu"], + "內" => &["nei"], + "全" => &["quan"], + "兩" => &["liang"], + "兪" => &["yu","shu"], + "八" => &["ba"], + "公" => &["gong"], + "六" => &["liu","lu"], + "兮" => &["xi"], + "兰" => &["lan"], + "共" => &["gong"], + "兲" => &["tian"], + "关" => &["guan"], + "兴" => &["xing"], + "兵" => &["bing"], + "其" => &["qi","ji"], + "具" => &["ju"], + "典" => &["dian"], + "兹" => &["zi","ci"], + "养" => &["yang"], + "兼" => &["jian"], + "兽" => &["shou"], + "兾" => &["ji"], + "兿" => &["yi"], + "冀" => &["ji"], + "冁" => &["chan"], + "冂" => &["jiong"], + "冃" => &["mao"], + "冄" => &["ran"], + "内" => &["nei"], + "円" => &["yuan"], + "冇" => &["mao","mou"], + "冈" => &["gang"], + "冉" => &["ran"], + "冊" => &["ce"], + "冋" => &["jiong"], + "册" => &["ce"], + "再" => &["zai"], + "冎" => &["gua"], + "冏" => &["jiong"], + "冐" => &["mao","mo"], + "冑" => &["zhou"], + "冒" => &["mao","mo"], + "冓" => &["gou"], + "冔" => &["xu"], + "冕" => &["mian"], + "冖" => &["mi"], + "冗" => &["rong"], + "冘" => &["yin"], + "写" => &["xie"], + "冚" => &["kan"], + "军" => &["jun"], + "农" => &["nong"], + "冝" => &["yi"], + "冞" => &["mi"], + "冟" => &["shi"], + "冠" => &["guan"], + "冡" => &["meng"], + "冢" => &["zhong"], + "冣" => &["zui"], + "冤" => &["yuan"], + "冥" => &["ming"], + "冦" => &["kou"], + "冨" => &["fu"], + "冩" => &["xie"], + "冪" => &["mi"], + "冫" => &["bing"], + "冬" => &["dong"], + "冭" => &["tai"], + "冮" => &["gang"], + "冯" => &["feng","ping"], + "冰" => &["bing"], + "冱" => &["hu"], + "冲" => &["chong"], + "决" => &["jue"], + "冴" => &["hu"], + "况" => &["kuang"], + "冶" => &["ye"], + "冷" => &["leng"], + "冸" => &["pan"], + "冹" => &["fu"], + "冺" => &["min"], + "冻" => &["dong"], + "冼" => &["xian"], + "冽" => &["lie"], + "冾" => &["xia"], + "冿" => &["jian"], + "净" => &["jing"], + "凁" => &["shu"], + "凂" => &["mei"], + "凃" => &["shang"], + "凄" => &["qi"], + "凅" => &["gu"], + "准" => &["zhun"], + "凇" => &["song"], + "凈" => &["jing"], + "凉" => &["liang"], + "凊" => &["qing","jing"], + "凋" => &["diao"], + "凌" => &["ling"], + "凍" => &["dong"], + "凎" => &["gan"], + "减" => &["jian"], + "凐" => &["yin"], + "凑" => &["cou"], + "凒" => &["ai"], + "凓" => &["li"], + "凔" => &["cang"], + "凕" => &["ming"], + "凖" => &["zhun"], + "凗" => &["cui"], + "凘" => &["si"], + "凙" => &["duo"], + "凚" => &["jin"], + "凛" => &["lin"], + "凜" => &["lin"], + "凝" => &["ning"], + "凞" => &["xi"], + "凟" => &["du"], + "几" => &["ji"], + "凡" => &["fan"], + "凢" => &["fan"], + "凣" => &["fan"], + "凤" => &["feng"], + "凥" => &["ju"], + "処" => &["chu"], + "凨" => &["feng"], + "凫" => &["fu"], + "凬" => &["feng"], + "凭" => &["ping"], + "凮" => &["feng"], + "凯" => &["kai"], + "凰" => &["huang"], + "凱" => &["kai"], + "凲" => &["gan"], + "凳" => &["deng"], + "凴" => &["ping"], + "凵" => &["qu","kan"], + "凶" => &["xiong"], + "凷" => &["kuai"], + "凸" => &["tu","gu"], + "凹" => &["ao","wa"], + "出" => &["chu"], + "击" => &["ji"], + "凼" => &["dang"], + "函" => &["han"], + "凾" => &["han"], + "凿" => &["zao","zuo"], + "刀" => &["dao"], + "刁" => &["diao"], + "刂" => &["dao"], + "刃" => &["ren"], + "刄" => &["ren"], + "刅" => &["chuang"], + "分" => &["fen"], + "切" => &["qie"], + "刈" => &["yi"], + "刉" => &["ji"], + "刊" => &["kan"], + "刋" => &["qian"], + "刌" => &["cun"], + "刍" => &["chu"], + "刎" => &["wen"], + "刏" => &["ji"], + "刐" => &["dan"], + "刑" => &["xing"], + "划" => &["hua","huai"], + "刓" => &["wan"], + "刔" => &["jue"], + "刕" => &["li"], + "刖" => &["yue"], + "列" => &["lie"], + "刘" => &["liu"], + "则" => &["ze"], + "刚" => &["gang"], + "创" => &["chuang"], + "刜" => &["fu"], + "初" => &["chu"], + "刞" => &["qu"], + "刟" => &["ju"], + "删" => &["shan"], + "刡" => &["min"], + "刢" => &["ling"], + "刣" => &["zhong"], + "判" => &["pan"], + "別" => &["bie"], + "刦" => &["jie"], + "刧" => &["jie"], + "刨" => &["pao","bao"], + "利" => &["li"], + "刪" => &["shan"], + "别" => &["bie"], + "刬" => &["chan"], + "刭" => &["jing"], + "刮" => &["gua"], + "刯" => &["gen"], + "到" => &["dao"], + "刱" => &["chuang"], + "刲" => &["kui"], + "刳" => &["ku"], + "刴" => &["duo"], + "刵" => &["er"], + "制" => &["zhi"], + "刷" => &["shua"], + "券" => &["quan","xuan"], + "刹" => &["cha","sha"], + "刺" => &["ci"], + "刻" => &["ke"], + "刼" => &["jie"], + "刽" => &["gui"], + "刾" => &["ci"], + "刿" => &["gui"], + "剀" => &["kai"], + "剁" => &["duo"], + "剂" => &["ji"], + "剃" => &["ti"], + "剄" => &["jing"], + "剅" => &["lou"], + "剆" => &["luo"], + "則" => &["ze"], + "剈" => &["yuan"], + "剉" => &["cuo"], + "削" => &["xue","xiao"], + "剋" => &["ke"], + "剌" => &["la"], + "前" => &["qian"], + "剎" => &["cha"], + "剏" => &["chuan"], + "剐" => &["gua"], + "剑" => &["jian"], + "剒" => &["cuo"], + "剓" => &["li"], + "剔" => &["ti"], + "剕" => &["fei"], + "剖" => &["pou","po"], + "剗" => &["chan"], + "剘" => &["qi"], + "剙" => &["chuang"], + "剚" => &["zi"], + "剛" => &["gang"], + "剜" => &["wan"], + "剝" => &["bo"], + "剞" => &["ji"], + "剟" => &["duo"], + "剠" => &["qing"], + "剡" => &["yan","shan"], + "剢" => &["zhuo"], + "剣" => &["jian"], + "剤" => &["ji"], + "剥" => &["bo","bao"], + "剦" => &["yan"], + "剧" => &["ju"], + "剨" => &["huo"], + "剩" => &["sheng"], + "剪" => &["jian"], + "剫" => &["duo"], + "剬" => &["duan"], + "剭" => &["wu"], + "剮" => &["gua"], + "副" => &["fu"], + "剰" => &["sheng"], + "剱" => &["jian"], + "割" => &["ge"], + "剳" => &["zha"], + "剴" => &["kai"], + "創" => &["chuang"], + "剶" => &["juan"], + "剷" => &["chan"], + "剸" => &["tuan","zhuan"], + "剹" => &["lu"], + "剺" => &["li"], + "剻" => &["fou"], + "剼" => &["shan"], + "剽" => &["piao"], + "剾" => &["kou"], + "剿" => &["jiao","chao","jia"], + "劀" => &["gua"], + "劁" => &["qiao"], + "劂" => &["jue"], + "劃" => &["hua"], + "劄" => &["zha"], + "劅" => &["zhuo"], + "劆" => &["lian"], + "劇" => &["ju"], + "劈" => &["pi"], + "劉" => &["liu"], + "劊" => &["gui"], + "劋" => &["jiao"], + "劌" => &["gui"], + "劍" => &["jian"], + "劎" => &["jian"], + "劏" => &["tang"], + "劐" => &["huo"], + "劑" => &["ji"], + "劒" => &["jian"], + "劓" => &["yi"], + "劔" => &["jian"], + "劕" => &["zhi"], + "劖" => &["chan"], + "劗" => &["cuan"], + "劘" => &["mo"], + "劙" => &["li"], + "劚" => &["zhu"], + "力" => &["li"], + "劜" => &["ya"], + "劝" => &["quan"], + "办" => &["ban"], + "功" => &["gong"], + "加" => &["jia"], + "务" => &["wu"], + "劢" => &["mai"], + "劣" => &["lie"], + "劤" => &["jing"], + "劥" => &["keng"], + "劦" => &["xie"], + "劧" => &["zhi"], + "动" => &["dong"], + "助" => &["zhu"], + "努" => &["nu","nao"], + "劫" => &["jie"], + "劬" => &["qu"], + "劭" => &["shao"], + "劮" => &["yi"], + "劯" => &["zhu"], + "劰" => &["mo"], + "励" => &["li"], + "劲" => &["jing","jin"], + "劳" => &["lao"], + "労" => &["lao"], + "劵" => &["juan"], + "劶" => &["kou"], + "劷" => &["yang"], + "劸" => &["wa"], + "効" => &["xiao"], + "劺" => &["mou"], + "劻" => &["kuang"], + "劼" => &["jie"], + "劽" => &["lie"], + "劾" => &["he"], + "势" => &["shi"], + "勀" => &["ke"], + "勁" => &["jing","jin"], + "勂" => &["hao"], + "勃" => &["bo"], + "勄" => &["min"], + "勅" => &["chi"], + "勆" => &["lang"], + "勇" => &["yong"], + "勈" => &["yong"], + "勉" => &["mian"], + "勊" => &["ke"], + "勋" => &["xun"], + "勌" => &["juan"], + "勍" => &["qing"], + "勎" => &["lu"], + "勏" => &["bu"], + "勐" => &["meng"], + "勑" => &["lai"], + "勒" => &["le","lei"], + "勓" => &["kai"], + "勔" => &["mian"], + "動" => &["dong"], + "勖" => &["xu"], + "勗" => &["xu"], + "勘" => &["kan"], + "務" => &["wu"], + "勚" => &["yi"], + "勛" => &["xun"], + "勜" => &["weng"], + "勝" => &["sheng"], + "勞" => &["lao"], + "募" => &["mu"], + "勠" => &["lu"], + "勡" => &["piao"], + "勢" => &["shi"], + "勣" => &["ji"], + "勤" => &["qin"], + "勥" => &["qiang"], + "勦" => &["jiao","chao"], + "勧" => &["quan"], + "勨" => &["xiang"], + "勩" => &["yi"], + "勪" => &["qiao"], + "勫" => &["fan"], + "勬" => &["juan"], + "勭" => &["tong"], + "勮" => &["ju"], + "勯" => &["dan"], + "勰" => &["xie"], + "勱" => &["mai"], + "勲" => &["xun"], + "勳" => &["xun"], + "勴" => &["lu:"], + "勵" => &["li"], + "勶" => &["che"], + "勷" => &["rang"], + "勸" => &["quan"], + "勹" => &["bao"], + "勺" => &["shao","shuo","biao"], + "勻" => &["yun"], + "勼" => &["jiu"], + "勽" => &["bao"], + "勾" => &["gou"], + "勿" => &["wu"], + "匀" => &["yun"], + "匃" => &["gai"], + "匄" => &["gai"], + "包" => &["bao"], + "匆" => &["cong"], + "匈" => &["xiong"], + "匉" => &["peng"], + "匊" => &["ju"], + "匋" => &["tao"], + "匌" => &["ge"], + "匍" => &["pu"], + "匎" => &["an"], + "匏" => &["pao"], + "匐" => &["fu"], + "匑" => &["gong"], + "匒" => &["da"], + "匓" => &["jiu"], + "匔" => &["qiong"], + "匕" => &["bi"], + "化" => &["hua"], + "北" => &["bei"], + "匘" => &["nao"], + "匙" => &["chi","shi"], + "匚" => &["fang","xi"], + "匛" => &["jiu"], + "匜" => &["yi"], + "匝" => &["za"], + "匞" => &["jiang"], + "匟" => &["kang"], + "匠" => &["jiang"], + "匡" => &["kuang"], + "匢" => &["hu"], + "匣" => &["xia"], + "匤" => &["qu"], + "匥" => &["fan"], + "匦" => &["gui"], + "匧" => &["qie"], + "匨" => &["cang","zang"], + "匩" => &["kuang"], + "匪" => &["fei"], + "匫" => &["hu"], + "匬" => &["yu"], + "匭" => &["gui"], + "匮" => &["kui"], + "匯" => &["hui"], + "匰" => &["dan"], + "匱" => &["kui"], + "匲" => &["lian"], + "匳" => &["lian"], + "匴" => &["suan"], + "匵" => &["du"], + "匶" => &["jiu"], + "匷" => &["qu"], + "匸" => &["xi"], + "匹" => &["pi","ya"], + "区" => &["qu","ou"], + "医" => &["yi"], + "匼" => &["an"], + "匽" => &["yan"], + "匾" => &["bian"], + "匿" => &["ni"], + "區" => &["qu","ou"], + "十" => &["shi"], + "卂" => &["xin"], + "千" => &["qian"], + "卄" => &["nian"], + "卅" => &["sa"], + "卆" => &["zu"], + "升" => &["sheng"], + "午" => &["wu"], + "卉" => &["hui"], + "半" => &["ban"], + "卋" => &["shi"], + "卌" => &["xi"], + "卍" => &["wan"], + "华" => &["hua"], + "协" => &["xie"], + "卐" => &["wan"], + "卑" => &["bei"], + "卒" => &["zu","cu"], + "卓" => &["zhuo"], + "協" => &["xie"], + "单" => &["dan","chan","shan"], + "卖" => &["mai"], + "南" => &["nan","na"], + "単" => &["dan","chan"], + "卙" => &["ji"], + "博" => &["bo"], + "卛" => &["shuai","lu:"], + "卜" => &["bu","bo"], + "卝" => &["kuang"], + "卞" => &["bian"], + "卟" => &["bu"], + "占" => &["zhan"], + "卡" => &["ka","qia"], + "卢" => &["lu"], + "卣" => &["you"], + "卤" => &["lu"], + "卥" => &["xi"], + "卦" => &["gua"], + "卧" => &["wo"], + "卨" => &["xie"], + "卩" => &["jie"], + "卪" => &["jie"], + "卫" => &["wei"], + "卬" => &["ang","yang"], + "卭" => &["qiong"], + "卮" => &["zhi"], + "卯" => &["mao"], + "印" => &["yin"], + "危" => &["wei"], + "卲" => &["shao"], + "即" => &["ji"], + "却" => &["que"], + "卵" => &["luan"], + "卶" => &["shi"], + "卷" => &["juan","quan"], + "卸" => &["xie"], + "卹" => &["xu"], + "卺" => &["jin"], + "卻" => &["que"], + "卼" => &["wu"], + "卽" => &["ji"], + "卾" => &["e"], + "卿" => &["qing"], + "厀" => &["xi"], + "厂" => &["chang","han","an"], + "厃" => &["han"], + "厄" => &["e"], + "厅" => &["ting"], + "历" => &["li"], + "厇" => &["zhe"], + "厈" => &["an","chang"], + "厉" => &["li"], + "厊" => &["ya"], + "压" => &["ya"], + "厌" => &["yan"], + "厍" => &["she"], + "厎" => &["zhi"], + "厏" => &["zha"], + "厐" => &["pang"], + "厒" => &["ke"], + "厓" => &["ya"], + "厔" => &["zhi"], + "厕" => &["ce","si"], + "厖" => &["pang"], + "厗" => &["ti"], + "厘" => &["li"], + "厙" => &["she"], + "厚" => &["hou"], + "厛" => &["ting"], + "厜" => &["zui"], + "厝" => &["cuo"], + "厞" => &["fei"], + "原" => &["yuan"], + "厠" => &["ce","si"], + "厡" => &["yuan"], + "厢" => &["xiang"], + "厣" => &["yan"], + "厤" => &["li"], + "厥" => &["jue"], + "厦" => &["sha","xia"], + "厧" => &["dian"], + "厨" => &["chu"], + "厩" => &["jiu"], + "厪" => &["qin","jin"], + "厫" => &["ao"], + "厬" => &["gui"], + "厭" => &["yan"], + "厮" => &["si"], + "厯" => &["li"], + "厰" => &["chang","an"], + "厱" => &["lan"], + "厲" => &["li"], + "厳" => &["yan"], + "厴" => &["yan"], + "厵" => &["yuan"], + "厶" => &["si"], + "厷" => &["si"], + "厸" => &["lin"], + "厹" => &["qiu"], + "厺" => &["qu"], + "去" => &["qu"], + "厽" => &["lei"], + "厾" => &["du"], + "县" => &["xian"], + "叀" => &["zhuan"], + "叁" => &["san"], + "参" => &["can","cen","shen"], + "參" => &["can","cen","shen","san"], + "叄" => &["san"], + "叅" => &["can","cen","shen"], + "叆" => &["ai"], + "叇" => &["dai"], + "又" => &["you"], + "叉" => &["cha"], + "及" => &["ji"], + "友" => &["you"], + "双" => &["shuang"], + "反" => &["fan"], + "収" => &["shou"], + "叏" => &["guai"], + "叐" => &["ba"], + "发" => &["fa"], + "叒" => &["ruo"], + "叓" => &["shi"], + "叔" => &["shu"], + "叕" => &["zhui"], + "取" => &["qu"], + "受" => &["shou"], + "变" => &["bian"], + "叙" => &["xu"], + "叚" => &["jia"], + "叛" => &["pan"], + "叜" => &["sou"], + "叝" => &["ji"], + "叞" => &["yu"], + "叟" => &["sou"], + "叠" => &["die"], + "叡" => &["rui"], + "叢" => &["cong"], + "口" => &["kou"], + "古" => &["gu"], + "句" => &["ju","gou"], + "另" => &["ling"], + "叧" => &["gua"], + "叨" => &["dao","tao"], + "叩" => &["kou"], + "只" => &["zhi"], + "叫" => &["jiao"], + "召" => &["zhao","shao"], + "叭" => &["ba"], + "叮" => &["ding"], + "可" => &["ke"], + "台" => &["tai"], + "叱" => &["chi"], + "史" => &["shi"], + "右" => &["you"], + "叴" => &["qiu"], + "叵" => &["po"], + "叶" => &["ye","xie"], + "号" => &["hao"], + "司" => &["si"], + "叹" => &["tan"], + "叺" => &["chi"], + "叻" => &["le"], + "叼" => &["diao"], + "叽" => &["ji"], + "叿" => &["hong"], + "吀" => &["mie"], + "吁" => &["yu","xu"], + "吂" => &["mang"], + "吃" => &["chi","ji"], + "各" => &["ge"], + "吅" => &["xuan"], + "吆" => &["yao"], + "吇" => &["zi"], + "合" => &["he","ge"], + "吉" => &["ji"], + "吊" => &["diao"], + "吋" => &["cun"], + "同" => &["tong"], + "名" => &["ming"], + "后" => &["hou"], + "吏" => &["li"], + "吐" => &["tu"], + "向" => &["xiang"], + "吒" => &["zha"], + "吓" => &["xia","he"], + "吔" => &["ye"], + "吕" => &["lu:"], + "吖" => &["a"], + "吗" => &["ma"], + "吘" => &["ou"], + "吙" => &["xue"], + "吚" => &["yi"], + "君" => &["jun"], + "吜" => &["chou"], + "吝" => &["lin"], + "吞" => &["tun"], + "吟" => &["yin"], + "吠" => &["fei"], + "吡" => &["bi","pi"], + "吢" => &["qin"], + "吣" => &["qin"], + "吤" => &["jie"], + "吥" => &["pou"], + "否" => &["fou","pi"], + "吧" => &["ba"], + "吨" => &["dun"], + "吩" => &["fen"], + "吪" => &["e"], + "含" => &["han"], + "听" => &["ting","yin"], + "吭" => &["keng","hang"], + "吮" => &["shun"], + "启" => &["qi"], + "吰" => &["hu"], + "吱" => &["zhi","zi"], + "吲" => &["yin"], + "吳" => &["wu"], + "吴" => &["wu"], + "吵" => &["chao"], + "吶" => &["na"], + "吷" => &["chuo"], + "吸" => &["xi"], + "吹" => &["chui"], + "吺" => &["dou"], + "吻" => &["wen"], + "吼" => &["hou"], + "吽" => &["ou","hong"], + "吾" => &["wu"], + "吿" => &["gao","gu"], + "呀" => &["ya"], + "呁" => &["jun"], + "呂" => &["lu:"], + "呃" => &["e"], + "呄" => &["ge"], + "呅" => &["mei"], + "呆" => &["dai","ai"], + "呇" => &["qi"], + "呈" => &["cheng"], + "呉" => &["wu"], + "告" => &["gao","gu"], + "呋" => &["fu"], + "呌" => &["jiao"], + "呍" => &["hong"], + "呎" => &["chi"], + "呏" => &["sheng"], + "呐" => &["na","ne"], + "呑" => &["tun"], + "呒" => &["m"], + "呓" => &["yi"], + "呔" => &["dai","tai"], + "呕" => &["ou"], + "呖" => &["li"], + "呗" => &["bei","bai"], + "员" => &["yuan","yun"], + "呙" => &["guo"], + "呛" => &["qiang"], + "呜" => &["wu"], + "呝" => &["e"], + "呞" => &["shi"], + "呟" => &["quan"], + "呠" => &["pen"], + "呡" => &["wen"], + "呢" => &["ni","ne","na"], + "呣" => &["mou"], + "呤" => &["ling"], + "呥" => &["ran"], + "呦" => &["you"], + "呧" => &["di"], + "周" => &["zhou"], + "呩" => &["shi"], + "呪" => &["zhou"], + "呫" => &["zhan"], + "呬" => &["ling"], + "呭" => &["yi"], + "呮" => &["qi"], + "呯" => &["ping"], + "呰" => &["zi"], + "呱" => &["gua","gu","wa"], + "呲" => &["ci","zi"], + "味" => &["wei"], + "呴" => &["xu"], + "呵" => &["he","ke","a"], + "呶" => &["nao"], + "呷" => &["xia"], + "呸" => &["pei"], + "呹" => &["yi"], + "呺" => &["xiao"], + "呻" => &["shen"], + "呼" => &["hu"], + "命" => &["ming"], + "呾" => &["da"], + "呿" => &["qu"], + "咀" => &["ju","zui"], + "咁" => &["gan"], + "咂" => &["za"], + "咃" => &["tuo"], + "咄" => &["duo"], + "咅" => &["pou"], + "咆" => &["pao"], + "咇" => &["bie"], + "咈" => &["fu"], + "咉" => &["yang","bi","fu"], + "咊" => &["he","huo"], + "咋" => &["za","ze","zha"], + "和" => &["he","huo","hai","hu"], + "咍" => &["hai"], + "咎" => &["jiu"], + "咏" => &["yong"], + "咐" => &["fu"], + "咑" => &["da"], + "咒" => &["zhou"], + "咓" => &["wa"], + "咔" => &["ka"], + "咕" => &["gu"], + "咖" => &["ka","ga"], + "咗" => &["zuo"], + "咘" => &["bu"], + "咙" => &["long"], + "咚" => &["dong"], + "咛" => &["ning"], + "咜" => &["zha"], + "咝" => &["si"], + "咞" => &["xian"], + "咟" => &["huo"], + "咠" => &["qi"], + "咡" => &["er"], + "咢" => &["e"], + "咣" => &["guang"], + "咤" => &["zha"], + "咥" => &["xi","die"], + "咦" => &["yi"], + "咧" => &["lie"], + "咨" => &["zi"], + "咩" => &["mie"], + "咪" => &["mi"], + "咫" => &["zhi"], + "咬" => &["yao"], + "咭" => &["ji"], + "咮" => &["zhou"], + "咯" => &["ge","ka","lo","luo"], + "咰" => &["shuai"], + "咱" => &["zan","za"], + "咲" => &["xiao"], + "咳" => &["ke","hai","ka","kai"], + "咴" => &["hui"], + "咵" => &["kua"], + "咶" => &["huai"], + "咷" => &["tao"], + "咸" => &["xian"], + "咹" => &["e"], + "咺" => &["xuan"], + "咻" => &["xiu"], + "咼" => &["guo","kuai"], + "咽" => &["yan","ye"], + "咾" => &["lao"], + "咿" => &["yi"], + "哀" => &["ai"], + "品" => &["pin"], + "哂" => &["shen"], + "哃" => &["tong"], + "哄" => &["hong"], + "哅" => &["xiong","hong"], + "哆" => &["duo"], + "哇" => &["wa"], + "哈" => &["ha","ka"], + "哉" => &["zai"], + "哊" => &["you"], + "哋" => &["di"], + "哌" => &["pai"], + "响" => &["xiang"], + "哎" => &["ai"], + "哏" => &["gen"], + "哐" => &["kuang"], + "哑" => &["ya"], + "哒" => &["da"], + "哓" => &["xiao"], + "哔" => &["bi"], + "哕" => &["hui","yue"], + "哗" => &["hua","ye"], + "哙" => &["kuai"], + "哚" => &["duo"], + "哜" => &["ji"], + "哝" => &["nong"], + "哞" => &["mou"], + "哟" => &["yo"], + "哠" => &["hao"], + "員" => &["yuan","yun"], + "哢" => &["long"], + "哣" => &["pou"], + "哤" => &["mang"], + "哥" => &["ge"], + "哦" => &["e","o","wo"], + "哧" => &["chi"], + "哨" => &["shao"], + "哩" => &["li"], + "哪" => &["na","nei","ne","nai"], + "哫" => &["zu"], + "哬" => &["he"], + "哭" => &["ku"], + "哮" => &["xiao"], + "哯" => &["xian"], + "哰" => &["lao"], + "哱" => &["bei"], + "哲" => &["zhe"], + "哳" => &["zha"], + "哴" => &["liang"], + "哵" => &["ba"], + "哶" => &["mi"], + "哷" => &["le"], + "哸" => &["sui"], + "哹" => &["fou"], + "哺" => &["bu"], + "哻" => &["han"], + "哼" => &["heng","hng"], + "哽" => &["geng"], + "哾" => &["shuo"], + "哿" => &["ge"], + "唀" => &["you"], + "唁" => &["yan"], + "唂" => &["gu"], + "唃" => &["gu"], + "唄" => &["bai","bei"], + "唅" => &["han"], + "唆" => &["suo"], + "唇" => &["chun"], + "唈" => &["yi"], + "唉" => &["ai"], + "唊" => &["jia"], + "唋" => &["tu"], + "唌" => &["xian"], + "唍" => &["guan"], + "唎" => &["li"], + "唏" => &["xi"], + "唐" => &["tang"], + "唑" => &["zuo"], + "唒" => &["miu"], + "唓" => &["che"], + "唔" => &["wu","n","ng"], + "唕" => &["zao"], + "唖" => &["ya"], + "唗" => &["dou"], + "唘" => &["qi"], + "唙" => &["di"], + "唚" => &["qin"], + "唛" => &["ma"], + "唝" => &["gong"], + "唞" => &["dou"], + "唠" => &["lao"], + "唡" => &["liang"], + "唢" => &["suo"], + "唣" => &["zao"], + "唤" => &["huan"], + "唦" => &["gou"], + "唧" => &["ji"], + "唨" => &["zuo"], + "唩" => &["wo"], + "唪" => &["feng"], + "唫" => &["yin"], + "唬" => &["hu","xia"], + "唭" => &["qi"], + "售" => &["shou"], + "唯" => &["wei"], + "唰" => &["shua"], + "唱" => &["chang"], + "唲" => &["er"], + "唳" => &["li"], + "唴" => &["qiang"], + "唵" => &["an"], + "唶" => &["jie"], + "唷" => &["yo"], + "唸" => &["nian"], + "唹" => &["yu"], + "唺" => &["tian"], + "唻" => &["lai"], + "唼" => &["sha"], + "唽" => &["xi"], + "唾" => &["tuo"], + "唿" => &["hu"], + "啀" => &["ai"], + "啁" => &["zhou","zhao"], + "啂" => &["nou"], + "啃" => &["ken"], + "啄" => &["zhuo"], + "啅" => &["zhuo"], + "商" => &["shang"], + "啇" => &["di"], + "啈" => &["heng"], + "啉" => &["lin"], + "啊" => &["a"], + "啋" => &["xiao"], + "啌" => &["xiang"], + "啍" => &["tun"], + "啎" => &["wu"], + "問" => &["wen"], + "啐" => &["cui"], + "啑" => &["jie"], + "啒" => &["hu"], + "啓" => &["qi"], + "啔" => &["qi"], + "啕" => &["tao"], + "啖" => &["dan"], + "啗" => &["dan"], + "啘" => &["wan"], + "啙" => &["zi"], + "啚" => &["bi"], + "啛" => &["cui"], + "啜" => &["chuo","chuai"], + "啝" => &["he"], + "啞" => &["ya"], + "啟" => &["qi"], + "啠" => &["zhe"], + "啡" => &["fei"], + "啢" => &["liang"], + "啣" => &["xian"], + "啤" => &["pi"], + "啥" => &["sha"], + "啦" => &["la"], + "啧" => &["ze"], + "啨" => &["qing"], + "啩" => &["gua"], + "啪" => &["pa"], + "啫" => &["zhe"], + "啬" => &["se"], + "啭" => &["zhuan"], + "啮" => &["nie"], + "啯" => &["guo"], + "啰" => &["luo"], + "啱" => &["yan"], + "啲" => &["di"], + "啳" => &["quan"], + "啴" => &["tan","chan"], + "啵" => &["bo"], + "啶" => &["ding"], + "啷" => &["lang"], + "啸" => &["xiao"], + "啺" => &["tang"], + "啻" => &["chi"], + "啼" => &["ti"], + "啽" => &["an"], + "啾" => &["jiu"], + "啿" => &["dan"], + "喀" => &["ka","ke"], + "喁" => &["yong"], + "喂" => &["wei"], + "喃" => &["nan"], + "善" => &["shan"], + "喅" => &["yu"], + "喆" => &["zhe"], + "喇" => &["la"], + "喈" => &["jie"], + "喉" => &["hou"], + "喊" => &["han"], + "喋" => &["die","zha"], + "喌" => &["zhou"], + "喍" => &["chai"], + "喎" => &["kuai"], + "喏" => &["re","nuo"], + "喐" => &["yu"], + "喑" => &["yin"], + "喒" => &["zan"], + "喓" => &["yao"], + "喔" => &["wo","o"], + "喕" => &["mian"], + "喖" => &["hu"], + "喗" => &["yun"], + "喘" => &["chuan"], + "喙" => &["hui"], + "喚" => &["huan"], + "喛" => &["huan"], + "喜" => &["xi"], + "喝" => &["he"], + "喞" => &["ji"], + "喟" => &["kui"], + "喠" => &["zhong"], + "喡" => &["wei"], + "喢" => &["sha"], + "喣" => &["xu"], + "喤" => &["huang"], + "喥" => &["du"], + "喦" => &["nie"], + "喧" => &["xuan"], + "喨" => &["liang"], + "喩" => &["yu"], + "喪" => &["sang"], + "喫" => &["chi"], + "喬" => &["qiao"], + "喭" => &["yan"], + "單" => &["dan","chan","shan"], + "喯" => &["pen"], + "喰" => &["shi","si"], + "喱" => &["li"], + "喲" => &["yo"], + "喳" => &["zha","cha"], + "喴" => &["wei"], + "喵" => &["miao"], + "営" => &["ying"], + "喷" => &["pen"], + "喹" => &["kui"], + "喺" => &["xi"], + "喻" => &["yu"], + "喼" => &["jie"], + "喽" => &["lou"], + "喾" => &["ku"], + "喿" => &["cao"], + "嗀" => &["huo"], + "嗁" => &["ti"], + "嗂" => &["yao"], + "嗃" => &["he"], + "嗄" => &["a","sha"], + "嗅" => &["xiu"], + "嗆" => &["qiang"], + "嗇" => &["se"], + "嗈" => &["yong"], + "嗉" => &["su"], + "嗊" => &["hong"], + "嗋" => &["xie"], + "嗌" => &["ai","yi"], + "嗍" => &["suo"], + "嗎" => &["ma"], + "嗏" => &["cha"], + "嗐" => &["hai"], + "嗑" => &["ke"], + "嗒" => &["da","ta"], + "嗓" => &["sang"], + "嗔" => &["chen"], + "嗕" => &["ru","nou"], + "嗖" => &["sou"], + "嗗" => &["gong"], + "嗘" => &["ji"], + "嗙" => &["pang"], + "嗚" => &["wu"], + "嗛" => &["qian"], + "嗜" => &["shi"], + "嗝" => &["ge"], + "嗞" => &["zi"], + "嗟" => &["jie","jue"], + "嗠" => &["luo"], + "嗡" => &["weng"], + "嗢" => &["wa"], + "嗣" => &["si"], + "嗤" => &["chi"], + "嗥" => &["hao"], + "嗦" => &["suo"], + "嗧" => &["jia","lun"], + "嗨" => &["hai","hei"], + "嗩" => &["suo"], + "嗪" => &["qin"], + "嗫" => &["nie"], + "嗬" => &["he"], + "嗮" => &["sai"], + "嗯" => &["ng","n"], + "嗰" => &["ge"], + "嗱" => &["na"], + "嗲" => &["dia"], + "嗳" => &["ai"], + "嗵" => &["tong"], + "嗶" => &["bi"], + "嗷" => &["ao"], + "嗸" => &["ao"], + "嗹" => &["lian"], + "嗺" => &["cui"], + "嗻" => &["zhe"], + "嗼" => &["mo"], + "嗽" => &["sou"], + "嗾" => &["sou","zu"], + "嗿" => &["tan"], + "嘀" => &["di"], + "嘁" => &["qi"], + "嘂" => &["jiao"], + "嘃" => &["chong"], + "嘄" => &["jiao"], + "嘅" => &["kai"], + "嘆" => &["tan"], + "嘇" => &["san"], + "嘈" => &["cao"], + "嘉" => &["jia"], + "嘋" => &["xiao"], + "嘌" => &["piao"], + "嘍" => &["lou"], + "嘎" => &["ga"], + "嘏" => &["gu","jia"], + "嘐" => &["xiao"], + "嘑" => &["hu"], + "嘒" => &["hui"], + "嘓" => &["guo"], + "嘔" => &["ou"], + "嘕" => &["xian"], + "嘖" => &["ze"], + "嘗" => &["chang"], + "嘘" => &["xu","shi"], + "嘙" => &["po"], + "嘚" => &["de","dei"], + "嘛" => &["ma"], + "嘜" => &["ma","mo"], + "嘝" => &["hu"], + "嘞" => &["lei"], + "嘟" => &["du"], + "嘠" => &["ga"], + "嘡" => &["tang"], + "嘢" => &["ye"], + "嘣" => &["beng"], + "嘤" => &["ying"], + "嘦" => &["jiao"], + "嘧" => &["mi"], + "嘨" => &["xiao"], + "嘩" => &["hua","ye"], + "嘪" => &["mai"], + "嘫" => &["ran"], + "嘬" => &["zuo","chuai","zhuai"], + "嘭" => &["peng"], + "嘮" => &["lao"], + "嘯" => &["xiao"], + "嘰" => &["ji"], + "嘱" => &["zhu"], + "嘲" => &["chao","zhao"], + "嘳" => &["kui"], + "嘴" => &["zui"], + "嘵" => &["xiao"], + "嘶" => &["si"], + "嘷" => &["hao"], + "嘸" => &["fu","m"], + "嘹" => &["liao"], + "嘺" => &["qiao"], + "嘻" => &["xi"], + "嘼" => &["xu"], + "嘽" => &["chan"], + "嘾" => &["dan"], + "嘿" => &["hei","mo","hai"], + "噀" => &["xun"], + "噁" => &["wu"], + "噂" => &["zun"], + "噃" => &["pan"], + "噄" => &["chi"], + "噅" => &["kui"], + "噆" => &["can"], + "噇" => &["zan"], + "噈" => &["cu"], + "噉" => &["dan"], + "噊" => &["yu"], + "噋" => &["tun"], + "噌" => &["cheng","ceng"], + "噍" => &["jiao"], + "噎" => &["ye"], + "噏" => &["xi"], + "噐" => &["qi"], + "噑" => &["hao"], + "噒" => &["lian"], + "噓" => &["xu","shi"], + "噔" => &["deng"], + "噕" => &["hui"], + "噖" => &["yin"], + "噗" => &["pu"], + "噘" => &["jue"], + "噙" => &["qin"], + "噚" => &["xun"], + "噛" => &["nie"], + "噜" => &["lu"], + "噝" => &["si"], + "噞" => &["yan"], + "噟" => &["ying"], + "噠" => &["da"], + "噡" => &["zhan"], + "噢" => &["o"], + "噣" => &["zhou"], + "噤" => &["jin"], + "噥" => &["nong"], + "噦" => &["hui","yue"], + "噧" => &["hui"], + "器" => &["qi"], + "噩" => &["e"], + "噪" => &["zao"], + "噫" => &["yi"], + "噬" => &["shi"], + "噭" => &["jiao"], + "噮" => &["yuan"], + "噯" => &["ai"], + "噰" => &["yong"], + "噱" => &["xue","jue"], + "噲" => &["kuai"], + "噳" => &["yu"], + "噴" => &["pen"], + "噵" => &["dao"], + "噶" => &["ga"], + "噷" => &["xin"], + "噸" => &["dun"], + "噹" => &["dang"], + "噻" => &["sai"], + "噼" => &["pi"], + "噽" => &["pi"], + "噾" => &["yin"], + "噿" => &["zui"], + "嚀" => &["ning"], + "嚁" => &["di"], + "嚂" => &["han"], + "嚃" => &["ta"], + "嚄" => &["huo","o"], + "嚅" => &["ru"], + "嚆" => &["hao"], + "嚇" => &["xia","he"], + "嚈" => &["yan"], + "嚉" => &["duo"], + "嚊" => &["pi"], + "嚋" => &["chou"], + "嚌" => &["ji"], + "嚍" => &["jin"], + "嚎" => &["hao"], + "嚏" => &["ti"], + "嚐" => &["chang"], + "嚓" => &["ca","cha"], + "嚔" => &["ti"], + "嚕" => &["lu"], + "嚖" => &["hui"], + "嚗" => &["bao"], + "嚘" => &["you"], + "嚙" => &["nie"], + "嚚" => &["yin"], + "嚛" => &["hu"], + "嚜" => &["mo"], + "嚝" => &["huang"], + "嚞" => &["zhe"], + "嚟" => &["li"], + "嚠" => &["liu"], + "嚢" => &["nang"], + "嚣" => &["xiao","ao"], + "嚤" => &["mo"], + "嚥" => &["yan"], + "嚦" => &["li"], + "嚧" => &["lu"], + "嚨" => &["long"], + "嚩" => &["mo"], + "嚪" => &["dan"], + "嚫" => &["chen"], + "嚬" => &["pin"], + "嚭" => &["pi"], + "嚮" => &["xiang"], + "嚯" => &["huo"], + "嚰" => &["mo"], + "嚱" => &["xi"], + "嚲" => &["duo"], + "嚳" => &["ku"], + "嚴" => &["yan"], + "嚵" => &["chan"], + "嚶" => &["ying"], + "嚷" => &["rang"], + "嚸" => &["dian"], + "嚹" => &["la"], + "嚺" => &["ta"], + "嚻" => &["xiao"], + "嚼" => &["jiao","jue"], + "嚽" => &["chuo"], + "嚾" => &["huan"], + "嚿" => &["huo"], + "囀" => &["zhuan"], + "囁" => &["nie"], + "囂" => &["xiao","ao"], + "囃" => &["ca"], + "囄" => &["li"], + "囅" => &["chan"], + "囆" => &["chai"], + "囇" => &["li"], + "囈" => &["yi"], + "囉" => &["luo"], + "囊" => &["nang"], + "囋" => &["zan"], + "囌" => &["su"], + "囍" => &["xi"], + "囏" => &["jian"], + "囐" => &["za"], + "囑" => &["zhu"], + "囒" => &["lan"], + "囓" => &["nie"], + "囔" => &["nang"], + "囗" => &["wei"], + "囘" => &["hui"], + "囙" => &["yin"], + "囚" => &["qiu"], + "四" => &["si"], + "囜" => &["nin"], + "囝" => &["jian","nan"], + "回" => &["hui"], + "囟" => &["xin"], + "因" => &["yin"], + "囡" => &["nan"], + "团" => &["tuan"], + "団" => &["tuan"], + "囤" => &["dun","tun"], + "囥" => &["kang"], + "囦" => &["yuan"], + "囧" => &["jiong"], + "囨" => &["pian"], + "囩" => &["yun"], + "囪" => &["cong","chuang"], + "囫" => &["hu"], + "囬" => &["hui"], + "园" => &["yuan"], + "囮" => &["e"], + "囯" => &["guo"], + "困" => &["kun"], + "囱" => &["cong"], + "囲" => &["wei"], + "図" => &["tu"], + "围" => &["wei"], + "囵" => &["lun"], + "囶" => &["guo"], + "囷" => &["jun"], + "囸" => &["ri"], + "囹" => &["ling"], + "固" => &["gu"], + "囻" => &["guo"], + "囼" => &["tai"], + "国" => &["guo"], + "图" => &["tu"], + "囿" => &["you"], + "圀" => &["guo"], + "圁" => &["yin"], + "圂" => &["hun"], + "圃" => &["pu"], + "圄" => &["yu"], + "圅" => &["han"], + "圆" => &["yuan"], + "圇" => &["lun"], + "圈" => &["quan","juan"], + "圉" => &["yu"], + "圊" => &["qing"], + "國" => &["guo"], + "圌" => &["chui"], + "圍" => &["wei"], + "圎" => &["yuan"], + "圏" => &["quan","juan"], + "圐" => &["ku"], + "圑" => &["pu"], + "園" => &["yuan"], + "圓" => &["yuan"], + "圔" => &["e"], + "圕" => &["tu","shu","guan"], + "圖" => &["tu"], + "圗" => &["tu"], + "團" => &["tuan"], + "圙" => &["lu:e"], + "圚" => &["hui"], + "圛" => &["yi"], + "圜" => &["yuan","huan"], + "圝" => &["luan"], + "圞" => &["luan"], + "土" => &["tu"], + "圠" => &["ya"], + "圡" => &["tu"], + "圢" => &["ting"], + "圣" => &["sheng"], + "圤" => &["yan"], + "圥" => &["lu"], + "圧" => &["ya"], + "在" => &["zai"], + "圩" => &["wei","xu"], + "圪" => &["ge"], + "圫" => &["yu"], + "圬" => &["wu"], + "圭" => &["gui"], + "圮" => &["pi"], + "圯" => &["yi"], + "地" => &["di","de"], + "圱" => &["qian"], + "圲" => &["qian"], + "圳" => &["zhen"], + "圴" => &["zhuo","shao"], + "圵" => &["dang"], + "圶" => &["qia"], + "圹" => &["kuang"], + "场" => &["chang"], + "圻" => &["qi","yin"], + "圼" => &["nie"], + "圽" => &["mo"], + "圾" => &["ji"], + "圿" => &["jia"], + "址" => &["zhi"], + "坁" => &["zhi"], + "坂" => &["ban"], + "坃" => &["xun"], + "坄" => &["tou"], + "坅" => &["qin"], + "坆" => &["fen"], + "均" => &["jun","yun"], + "坈" => &["keng"], + "坉" => &["dun"], + "坊" => &["fang"], + "坋" => &["fen"], + "坌" => &["ben"], + "坍" => &["tan"], + "坎" => &["kan"], + "坏" => &["huai","pi","pei"], + "坐" => &["zuo"], + "坑" => &["keng"], + "坒" => &["bi"], + "坓" => &["xing"], + "坔" => &["di"], + "坕" => &["jing"], + "坖" => &["ji"], + "块" => &["kuai"], + "坘" => &["di"], + "坙" => &["jing"], + "坚" => &["jian"], + "坛" => &["tan"], + "坜" => &["li"], + "坝" => &["ba"], + "坞" => &["wu"], + "坟" => &["fen"], + "坠" => &["zhui"], + "坡" => &["po"], + "坢" => &["pan"], + "坣" => &["tang"], + "坤" => &["kun"], + "坥" => &["qu"], + "坦" => &["tan"], + "坧" => &["zhi"], + "坨" => &["tuo"], + "坩" => &["gan"], + "坪" => &["ping"], + "坫" => &["dian"], + "坬" => &["wa"], + "坭" => &["ni"], + "坮" => &["tai"], + "坯" => &["pi"], + "坰" => &["jiong"], + "坱" => &["yang"], + "坲" => &["fo"], + "坳" => &["ao"], + "坴" => &["liu"], + "坵" => &["qiu"], + "坶" => &["mu"], + "坷" => &["ke"], + "坸" => &["gou"], + "坹" => &["xue"], + "坺" => &["ba"], + "坻" => &["chi","di"], + "坼" => &["che"], + "坽" => &["ling"], + "坾" => &["zhu"], + "坿" => &["fu"], + "垀" => &["hu"], + "垁" => &["zhi"], + "垂" => &["chui"], + "垃" => &["la"], + "垄" => &["long"], + "垅" => &["long"], + "垆" => &["lu"], + "垇" => &["ao"], + "垉" => &["pao"], + "型" => &["xing"], + "垌" => &["tong","dong"], + "垍" => &["ji"], + "垎" => &["ke"], + "垏" => &["lu"], + "垐" => &["ci"], + "垑" => &["chi"], + "垒" => &["lei"], + "垓" => &["gai"], + "垔" => &["yin"], + "垕" => &["hou"], + "垖" => &["dui"], + "垗" => &["zhao"], + "垘" => &["fu"], + "垙" => &["guang"], + "垚" => &["yao"], + "垛" => &["duo"], + "垜" => &["duo"], + "垝" => &["gui"], + "垞" => &["cha"], + "垟" => &["yang"], + "垠" => &["yin"], + "垡" => &["fa"], + "垢" => &["gou"], + "垣" => &["yuan"], + "垤" => &["die"], + "垥" => &["xie"], + "垦" => &["ken"], + "垧" => &["shang"], + "垨" => &["shou"], + "垩" => &["e"], + "垫" => &["dian"], + "垬" => &["hong"], + "垭" => &["ya"], + "垮" => &["kua"], + "垯" => &["da"], + "垱" => &["dang"], + "垲" => &["kai"], + "垴" => &["nao"], + "垵" => &["an"], + "垶" => &["xing"], + "垷" => &["xian"], + "垸" => &["huan","yuan"], + "垹" => &["bang"], + "垺" => &["pei"], + "垻" => &["ba"], + "垼" => &["yi"], + "垽" => &["yin"], + "垾" => &["han"], + "垿" => &["xu"], + "埀" => &["chui"], + "埁" => &["cen"], + "埂" => &["geng"], + "埃" => &["ai"], + "埄" => &["peng"], + "埅" => &["fang"], + "埆" => &["que"], + "埇" => &["yong"], + "埈" => &["jun"], + "埉" => &["jia"], + "埊" => &["di"], + "埋" => &["mai","man"], + "埌" => &["lang"], + "埍" => &["xuan"], + "城" => &["cheng"], + "埏" => &["shan"], + "埐" => &["jin"], + "埑" => &["zhe"], + "埒" => &["lie","le"], + "埓" => &["lie"], + "埔" => &["pu","bu"], + "埕" => &["cheng"], + "埗" => &["bu"], + "埘" => &["shi"], + "埙" => &["xun"], + "埚" => &["guo"], + "埛" => &["jiong"], + "埜" => &["ye"], + "埝" => &["nian"], + "埞" => &["di"], + "域" => &["yu"], + "埠" => &["bu"], + "埡" => &["wu","ya"], + "埢" => &["juan"], + "埣" => &["sui"], + "埤" => &["pi","bei","bi"], + "埥" => &["cheng"], + "埦" => &["wan"], + "埧" => &["ju"], + "埨" => &["lun"], + "埩" => &["zheng"], + "埪" => &["kong"], + "埫" => &["zhong"], + "埬" => &["dong"], + "埭" => &["dai"], + "埮" => &["tan"], + "埯" => &["an"], + "埰" => &["cai"], + "埱" => &["shu"], + "埲" => &["beng"], + "埳" => &["kan"], + "埴" => &["zhi"], + "埵" => &["duo"], + "埶" => &["yi"], + "執" => &["zhi"], + "埸" => &["yi"], + "培" => &["pei"], + "基" => &["ji"], + "埻" => &["zhun"], + "埼" => &["qi"], + "埽" => &["sao"], + "埾" => &["ju"], + "埿" => &["ni"], + "堀" => &["ku","jue"], + "堁" => &["ke"], + "堂" => &["tang"], + "堃" => &["kun"], + "堄" => &["ni"], + "堅" => &["jian"], + "堆" => &["dui","zui"], + "堇" => &["jin"], + "堈" => &["gang"], + "堉" => &["yu"], + "堊" => &["e","wu"], + "堋" => &["peng","beng"], + "堌" => &["gu"], + "堍" => &["tu"], + "堎" => &["leng","ling"], + "堐" => &["ya"], + "堑" => &["qian"], + "堓" => &["an"], + "堔" => &["chen"], + "堕" => &["duo","hui"], + "堖" => &["nao"], + "堗" => &["tu"], + "堘" => &["cheng"], + "堙" => &["yin"], + "堚" => &["hun"], + "堛" => &["bi"], + "堜" => &["lian"], + "堝" => &["guo"], + "堞" => &["die"], + "堟" => &["zhuan"], + "堠" => &["hou"], + "堡" => &["bao","bu","pu"], + "堢" => &["bao"], + "堣" => &["yu"], + "堤" => &["di","ti"], + "堥" => &["mao"], + "堦" => &["jie"], + "堧" => &["ruan"], + "堨" => &["e","ai"], + "堩" => &["geng"], + "堪" => &["kan"], + "堫" => &["zong"], + "堬" => &["yu"], + "堭" => &["huang"], + "堮" => &["e"], + "堯" => &["yao"], + "堰" => &["yan"], + "報" => &["bao"], + "堲" => &["ji"], + "堳" => &["mei"], + "場" => &["chang"], + "堵" => &["du"], + "堶" => &["tuo"], + "堷" => &["an"], + "堸" => &["feng"], + "堹" => &["zhong"], + "堺" => &["jie"], + "堻" => &["zhen"], + "堼" => &["heng"], + "堽" => &["gang"], + "堾" => &["chuan"], + "堿" => &["jian"], + "塁" => &["lei"], + "塂" => &["gang"], + "塃" => &["huang"], + "塄" => &["leng"], + "塅" => &["duan"], + "塆" => &["wan"], + "塇" => &["xuan"], + "塈" => &["ji"], + "塉" => &["ji"], + "塊" => &["kuai"], + "塋" => &["ying"], + "塌" => &["ta"], + "塍" => &["cheng"], + "塎" => &["yong"], + "塏" => &["kai"], + "塐" => &["su"], + "塑" => &["su"], + "塒" => &["shi"], + "塓" => &["mi"], + "塔" => &["ta","da"], + "塕" => &["weng"], + "塖" => &["cheng"], + "塗" => &["tu"], + "塘" => &["tang"], + "塙" => &["qiao"], + "塚" => &["zhong"], + "塛" => &["li"], + "塜" => &["peng"], + "塝" => &["bang"], + "塞" => &["sai","se"], + "塟" => &["zang"], + "塠" => &["dui"], + "塡" => &["tian"], + "塢" => &["wu"], + "塣" => &["cheng"], + "塤" => &["xun","xuan"], + "塥" => &["ge"], + "塦" => &["zhen"], + "塧" => &["ai"], + "塨" => &["gong"], + "塩" => &["yan"], + "塪" => &["kan"], + "填" => &["tian"], + "塬" => &["yuan"], + "塭" => &["wen"], + "塮" => &["xie"], + "塯" => &["liu"], + "塱" => &["lang"], + "塲" => &["chang"], + "塳" => &["peng"], + "塴" => &["beng"], + "塵" => &["chen"], + "塶" => &["lu"], + "塷" => &["lu"], + "塸" => &["ou"], + "塹" => &["qian"], + "塺" => &["mei"], + "塻" => &["mo"], + "塼" => &["zhuan"], + "塽" => &["shuang"], + "塾" => &["shu"], + "塿" => &["lou"], + "墀" => &["chi"], + "墁" => &["man"], + "墂" => &["biao"], + "境" => &["jing"], + "墄" => &["ce"], + "墅" => &["shu"], + "墆" => &["di"], + "墇" => &["zhang"], + "墈" => &["kan"], + "墉" => &["yong"], + "墊" => &["dian"], + "墋" => &["chen"], + "墌" => &["zhi"], + "墍" => &["ji"], + "墎" => &["guo"], + "墏" => &["qiang"], + "墐" => &["jin"], + "墑" => &["di"], + "墒" => &["shang"], + "墓" => &["mu"], + "墔" => &["cui"], + "墕" => &["yan"], + "墖" => &["ta","da"], + "増" => &["zeng"], + "墘" => &["qi"], + "墙" => &["qiang"], + "墚" => &["liang"], + "墜" => &["zhui"], + "墝" => &["qiao"], + "增" => &["zeng"], + "墟" => &["xu"], + "墠" => &["shan"], + "墡" => &["shan"], + "墢" => &["ba"], + "墣" => &["pu"], + "墤" => &["kuai"], + "墥" => &["dong"], + "墦" => &["fan"], + "墧" => &["que"], + "墨" => &["mo"], + "墩" => &["dun"], + "墪" => &["dun"], + "墫" => &["zun"], + "墬" => &["zui"], + "墭" => &["sheng"], + "墮" => &["duo","hui"], + "墯" => &["duo"], + "墰" => &["tan"], + "墱" => &["deng","yan"], + "墲" => &["mu"], + "墳" => &["fen"], + "墴" => &["huang"], + "墵" => &["tan"], + "墶" => &["da"], + "墷" => &["ye"], + "墸" => &["chu"], + "墺" => &["ao"], + "墻" => &["qiang"], + "墼" => &["ji"], + "墽" => &["qiao"], + "墾" => &["ken"], + "墿" => &["yi"], + "壀" => &["pi"], + "壁" => &["bi"], + "壂" => &["dian"], + "壃" => &["jiang"], + "壄" => &["ye"], + "壅" => &["yong"], + "壆" => &["xue"], + "壇" => &["tan"], + "壈" => &["lan"], + "壉" => &["ju"], + "壊" => &["huai","pi"], + "壋" => &["dang"], + "壌" => &["rang"], + "壍" => &["qian"], + "壎" => &["xuan"], + "壏" => &["lan"], + "壐" => &["mi"], + "壑" => &["he","huo"], + "壒" => &["kai"], + "壓" => &["ya"], + "壔" => &["dao"], + "壕" => &["hao"], + "壖" => &["ruan"], + "壘" => &["lei"], + "壙" => &["kuang"], + "壚" => &["lu"], + "壛" => &["yan"], + "壜" => &["tan"], + "壝" => &["wei"], + "壞" => &["huai","pi"], + "壟" => &["long"], + "壠" => &["long"], + "壡" => &["rui"], + "壢" => &["li"], + "壣" => &["lin"], + "壤" => &["rang"], + "壥" => &["chan"], + "壦" => &["xun"], + "壧" => &["yan"], + "壨" => &["lei"], + "壩" => &["ba"], + "士" => &["shi"], + "壬" => &["ren"], + "壮" => &["zhuang"], + "壯" => &["zhuang"], + "声" => &["sheng"], + "壱" => &["yi"], + "売" => &["mai"], + "壳" => &["ke","qiao"], + "壴" => &["zhu"], + "壵" => &["zhuang"], + "壶" => &["hu"], + "壷" => &["hu"], + "壸" => &["kun"], + "壹" => &["yi"], + "壺" => &["hu"], + "壻" => &["xu"], + "壼" => &["kun"], + "壽" => &["shou"], + "壾" => &["mang"], + "壿" => &["zun"], + "夀" => &["shou"], + "夁" => &["yi"], + "夂" => &["zhi"], + "夃" => &["gu"], + "处" => &["chu"], + "夅" => &["xiang"], + "夆" => &["feng"], + "备" => &["bei"], + "変" => &["bian"], + "夊" => &["sui"], + "夋" => &["qun"], + "夌" => &["ling"], + "复" => &["fu"], + "夎" => &["zuo"], + "夏" => &["xia","jia"], + "夐" => &["xiong"], + "夒" => &["nao"], + "夓" => &["xia"], + "夔" => &["kui"], + "夕" => &["xi"], + "外" => &["wai"], + "夗" => &["yuan"], + "夘" => &["mao"], + "夙" => &["su"], + "多" => &["duo"], + "夛" => &["duo"], + "夜" => &["ye"], + "夝" => &["qing"], + "够" => &["gou"], + "夠" => &["gou"], + "夡" => &["qi"], + "夢" => &["meng"], + "夣" => &["meng"], + "夤" => &["yin"], + "夥" => &["huo"], + "夦" => &["chen"], + "大" => &["da","dai"], + "夨" => &["ze"], + "天" => &["tian"], + "太" => &["tai"], + "夫" => &["fu"], + "夬" => &["guai"], + "夭" => &["yao"], + "央" => &["yang"], + "夯" => &["hang","ben"], + "夰" => &["gao"], + "失" => &["shi"], + "夲" => &["ben","tao"], + "夳" => &["tai"], + "头" => &["tou"], + "夵" => &["yan"], + "夶" => &["bi"], + "夷" => &["yi"], + "夸" => &["kua"], + "夹" => &["jia","ga"], + "夺" => &["duo"], + "夼" => &["kuang"], + "夽" => &["yun"], + "夾" => &["jia"], + "夿" => &["ba"], + "奀" => &["en","mang"], + "奁" => &["lian"], + "奂" => &["huan"], + "奃" => &["di"], + "奄" => &["yan"], + "奅" => &["pao"], + "奆" => &["juan"], + "奇" => &["qi","ji"], + "奈" => &["nai"], + "奉" => &["feng"], + "奊" => &["xie"], + "奋" => &["fen"], + "奌" => &["dian"], + "奎" => &["kui"], + "奏" => &["zou"], + "奐" => &["huan"], + "契" => &["qi","xie","qie"], + "奒" => &["kai"], + "奓" => &["she"], + "奔" => &["ben"], + "奕" => &["yi"], + "奖" => &["jiang"], + "套" => &["tao"], + "奘" => &["zhuang","zang"], + "奙" => &["ben"], + "奚" => &["xi"], + "奛" => &["huang"], + "奜" => &["fei"], + "奝" => &["diao"], + "奞" => &["sui"], + "奟" => &["beng"], + "奠" => &["dian"], + "奡" => &["ao"], + "奢" => &["she"], + "奣" => &["weng"], + "奤" => &["pan"], + "奥" => &["ao"], + "奦" => &["wu"], + "奧" => &["ao"], + "奨" => &["jiang"], + "奩" => &["lian"], + "奪" => &["duo"], + "奫" => &["yun"], + "奬" => &["jiang"], + "奭" => &["shi"], + "奮" => &["fen"], + "奯" => &["huo"], + "奰" => &["bei"], + "奱" => &["lian"], + "奲" => &["che"], + "女" => &["nu:","ru"], + "奴" => &["nu"], + "奵" => &["ding"], + "奶" => &["nai"], + "奷" => &["qian"], + "奸" => &["jian"], + "她" => &["ta"], + "奺" => &["jiu"], + "奻" => &["nan"], + "奼" => &["cha"], + "好" => &["hao"], + "奾" => &["xian"], + "奿" => &["fan"], + "妀" => &["ji"], + "妁" => &["shuo"], + "如" => &["ru"], + "妃" => &["fei"], + "妄" => &["wang"], + "妅" => &["hong"], + "妆" => &["zhuang"], + "妇" => &["fu"], + "妈" => &["ma"], + "妉" => &["dan"], + "妊" => &["ren"], + "妋" => &["fu"], + "妌" => &["jing"], + "妍" => &["yan"], + "妎" => &["xie"], + "妏" => &["wen"], + "妐" => &["zhong"], + "妑" => &["pa"], + "妒" => &["du"], + "妓" => &["ji"], + "妔" => &["keng"], + "妕" => &["zhong"], + "妖" => &["yao"], + "妗" => &["jin"], + "妘" => &["yun"], + "妙" => &["miao"], + "妚" => &["pei"], + "妛" => &["chi"], + "妜" => &["yue"], + "妝" => &["zhuang"], + "妞" => &["niu"], + "妟" => &["yan"], + "妠" => &["na"], + "妡" => &["xin"], + "妢" => &["fen"], + "妣" => &["bi"], + "妤" => &["yu"], + "妥" => &["tuo"], + "妦" => &["feng"], + "妧" => &["yuan"], + "妨" => &["fang"], + "妩" => &["wu"], + "妪" => &["yu"], + "妫" => &["gui"], + "妬" => &["du"], + "妭" => &["ba"], + "妮" => &["ni"], + "妯" => &["zhou"], + "妰" => &["zhou"], + "妱" => &["zhao"], + "妲" => &["da"], + "妳" => &["nai","ni"], + "妴" => &["yuan"], + "妵" => &["tou"], + "妶" => &["xuan"], + "妷" => &["zhi"], + "妸" => &["e"], + "妹" => &["mei"], + "妺" => &["mo"], + "妻" => &["qi"], + "妼" => &["bi"], + "妽" => &["shen"], + "妾" => &["qie"], + "妿" => &["e"], + "姀" => &["he"], + "姁" => &["xu"], + "姂" => &["fa"], + "姃" => &["zheng"], + "姄" => &["ni"], + "姅" => &["ban"], + "姆" => &["mu"], + "姇" => &["fu"], + "姈" => &["ling"], + "姉" => &["zi"], + "姊" => &["zi"], + "始" => &["shi"], + "姌" => &["ran"], + "姍" => &["shan"], + "姎" => &["yang"], + "姏" => &["qian"], + "姐" => &["jie"], + "姑" => &["gu"], + "姒" => &["si"], + "姓" => &["xing"], + "委" => &["wei"], + "姕" => &["zi"], + "姖" => &["ju"], + "姗" => &["shan"], + "姘" => &["pin"], + "姙" => &["ren"], + "姚" => &["yao"], + "姛" => &["tong"], + "姜" => &["jiang"], + "姝" => &["shu"], + "姞" => &["ji"], + "姟" => &["gai"], + "姠" => &["shang"], + "姡" => &["kuo"], + "姢" => &["juan"], + "姣" => &["jiao"], + "姤" => &["gou"], + "姥" => &["lao","mu"], + "姦" => &["jian"], + "姧" => &["jian"], + "姨" => &["yi"], + "姩" => &["nian"], + "姪" => &["zhi"], + "姫" => &["ji"], + "姬" => &["ji"], + "姭" => &["xian"], + "姮" => &["heng"], + "姯" => &["guang"], + "姰" => &["jun"], + "姱" => &["kua"], + "姲" => &["yan"], + "姳" => &["ming"], + "姴" => &["lie"], + "姵" => &["pei"], + "姶" => &["yan"], + "姷" => &["you"], + "姸" => &["yan"], + "姹" => &["cha"], + "姺" => &["xian"], + "姻" => &["yin"], + "姼" => &["chi"], + "姽" => &["gui"], + "姾" => &["quan"], + "姿" => &["zi"], + "娀" => &["song"], + "威" => &["wei"], + "娂" => &["hong"], + "娃" => &["wa"], + "娄" => &["lou"], + "娅" => &["ya"], + "娆" => &["rao"], + "娇" => &["jiao"], + "娈" => &["luan"], + "娉" => &["ping"], + "娊" => &["xian"], + "娋" => &["shao"], + "娌" => &["li"], + "娍" => &["cheng"], + "娎" => &["xie"], + "娏" => &["mang"], + "娑" => &["suo"], + "娒" => &["mu"], + "娓" => &["wei"], + "娔" => &["ke"], + "娕" => &["lai"], + "娖" => &["chuo"], + "娗" => &["ding"], + "娘" => &["niang"], + "娙" => &["keng"], + "娚" => &["nan"], + "娛" => &["yu"], + "娜" => &["na","nuo"], + "娝" => &["pei"], + "娞" => &["sui"], + "娟" => &["juan"], + "娠" => &["shen","chen","zhen"], + "娡" => &["zhi"], + "娢" => &["han"], + "娣" => &["di"], + "娤" => &["zhuang"], + "娥" => &["e"], + "娦" => &["pin"], + "娧" => &["tui"], + "娨" => &["xian"], + "娩" => &["mian","wan"], + "娪" => &["wu"], + "娫" => &["yan"], + "娬" => &["wu"], + "娭" => &["xi"], + "娮" => &["yan"], + "娯" => &["yu"], + "娰" => &["si"], + "娱" => &["yu"], + "娲" => &["wa"], + "娳" => &["li"], + "娴" => &["xian"], + "娵" => &["ju"], + "娶" => &["qu"], + "娷" => &["chui"], + "娸" => &["qi"], + "娹" => &["xian"], + "娺" => &["zhui"], + "娻" => &["dong"], + "娼" => &["chang"], + "娽" => &["lu"], + "娾" => &["ai"], + "娿" => &["e"], + "婀" => &["e"], + "婁" => &["lou","lu:"], + "婂" => &["mian"], + "婃" => &["cong"], + "婄" => &["pou"], + "婅" => &["ju"], + "婆" => &["po"], + "婇" => &["cai"], + "婈" => &["ling"], + "婉" => &["wan"], + "婊" => &["biao"], + "婋" => &["xiao"], + "婌" => &["shu"], + "婍" => &["qi"], + "婎" => &["hui"], + "婏" => &["fu"], + "婐" => &["wo"], + "婑" => &["rui"], + "婒" => &["tan"], + "婓" => &["fei"], + "婕" => &["jie"], + "婖" => &["tian"], + "婗" => &["ni"], + "婘" => &["quan"], + "婙" => &["jing"], + "婚" => &["hun"], + "婛" => &["jing"], + "婜" => &["qian"], + "婝" => &["dian"], + "婞" => &["xing"], + "婟" => &["hu"], + "婠" => &["wan"], + "婡" => &["lai"], + "婢" => &["bi"], + "婣" => &["yin"], + "婤" => &["chou","zhou"], + "婥" => &["chuo"], + "婦" => &["fu"], + "婧" => &["jing"], + "婨" => &["lun"], + "婩" => &["yan","an"], + "婪" => &["lan"], + "婫" => &["kun"], + "婬" => &["yin"], + "婭" => &["ya"], + "婯" => &["li"], + "婰" => &["dian"], + "婱" => &["xian"], + "婳" => &["hua"], + "婴" => &["ying"], + "婵" => &["chan"], + "婶" => &["shen"], + "婷" => &["ting"], + "婸" => &["yang"], + "婹" => &["yao"], + "婺" => &["wu"], + "婻" => &["nan"], + "婼" => &["chuo"], + "婽" => &["jia"], + "婾" => &["tou"], + "婿" => &["xu"], + "媀" => &["yu"], + "媁" => &["wei"], + "媂" => &["ti"], + "媃" => &["rou"], + "媄" => &["mei"], + "媅" => &["dan"], + "媆" => &["ruan"], + "媇" => &["qin"], + "媉" => &["wu"], + "媊" => &["qian"], + "媋" => &["chun"], + "媌" => &["mao"], + "媍" => &["fu"], + "媎" => &["jie"], + "媏" => &["duan"], + "媐" => &["xi"], + "媑" => &["zhong"], + "媒" => &["mei"], + "媓" => &["huang"], + "媔" => &["mian"], + "媕" => &["an"], + "媖" => &["ying"], + "媗" => &["xuan"], + "媙" => &["wei"], + "媚" => &["mei"], + "媛" => &["yuan"], + "媜" => &["zhen"], + "媝" => &["qiu"], + "媞" => &["ti","shi"], + "媟" => &["xie"], + "媠" => &["tuo"], + "媡" => &["lian"], + "媢" => &["mao"], + "媣" => &["ran"], + "媤" => &["si"], + "媥" => &["pian"], + "媦" => &["wei"], + "媧" => &["wa"], + "媨" => &["jiu"], + "媩" => &["hu"], + "媪" => &["ao"], + "媬" => &["bao"], + "媭" => &["xu"], + "媮" => &["tou"], + "媯" => &["gui"], + "媰" => &["zou"], + "媱" => &["yao"], + "媲" => &["pi"], + "媳" => &["xi"], + "媴" => &["yuan"], + "媵" => &["ying"], + "媶" => &["rong"], + "媷" => &["ru"], + "媸" => &["chi"], + "媹" => &["liu"], + "媺" => &["mei"], + "媻" => &["pan"], + "媼" => &["ao"], + "媽" => &["ma"], + "媾" => &["gou"], + "媿" => &["kui"], + "嫀" => &["qin"], + "嫁" => &["jia"], + "嫂" => &["sao"], + "嫃" => &["zhen"], + "嫄" => &["yuan"], + "嫅" => &["cha"], + "嫆" => &["yong"], + "嫇" => &["ming"], + "嫈" => &["ying"], + "嫉" => &["ji"], + "嫊" => &["su"], + "嫋" => &["niao"], + "嫌" => &["xian"], + "嫍" => &["tao","yao"], + "嫎" => &["pang"], + "嫏" => &["lang"], + "嫐" => &["niao"], + "嫑" => &["bao"], + "嫒" => &["ai"], + "嫓" => &["pi"], + "嫔" => &["pin"], + "嫕" => &["yi"], + "嫖" => &["piao"], + "嫗" => &["yu"], + "嫘" => &["lei"], + "嫙" => &["xuan"], + "嫚" => &["man"], + "嫛" => &["yi"], + "嫜" => &["zhang"], + "嫝" => &["kang"], + "嫞" => &["yong"], + "嫟" => &["ni"], + "嫠" => &["li"], + "嫡" => &["di"], + "嫢" => &["gui"], + "嫣" => &["yan"], + "嫤" => &["jin"], + "嫥" => &["zhuan"], + "嫦" => &["chang"], + "嫧" => &["ce"], + "嫨" => &["han","ran"], + "嫩" => &["nen"], + "嫪" => &["lao"], + "嫫" => &["mo"], + "嫬" => &["zhe"], + "嫭" => &["hu"], + "嫮" => &["hu"], + "嫯" => &["ao"], + "嫰" => &["nen"], + "嫱" => &["qiang"], + "嫳" => &["bi"], + "嫴" => &["gu"], + "嫵" => &["wu"], + "嫶" => &["qiao"], + "嫷" => &["tuo"], + "嫸" => &["zhan"], + "嫹" => &["mao"], + "嫺" => &["xian"], + "嫻" => &["xian"], + "嫼" => &["mo"], + "嫽" => &["liao"], + "嫾" => &["lian"], + "嫿" => &["hua"], + "嬀" => &["gui"], + "嬁" => &["deng"], + "嬂" => &["zhi"], + "嬃" => &["xu"], + "嬅" => &["hua"], + "嬆" => &["xi"], + "嬇" => &["hui"], + "嬈" => &["rao"], + "嬉" => &["xi"], + "嬊" => &["yan"], + "嬋" => &["chan"], + "嬌" => &["jiao"], + "嬍" => &["mei"], + "嬎" => &["fan"], + "嬏" => &["fan"], + "嬐" => &["xian"], + "嬑" => &["yi"], + "嬒" => &["wei"], + "嬓" => &["chan"], + "嬔" => &["fan"], + "嬕" => &["shi"], + "嬖" => &["bi"], + "嬗" => &["shan"], + "嬘" => &["sui"], + "嬙" => &["qiang"], + "嬚" => &["lian"], + "嬛" => &["huan","qiong","xuan"], + "嬝" => &["niao"], + "嬞" => &["dong"], + "嬟" => &["yi"], + "嬠" => &["can"], + "嬡" => &["ai"], + "嬢" => &["niang"], + "嬣" => &["ning"], + "嬤" => &["ma"], + "嬥" => &["tiao"], + "嬦" => &["chou"], + "嬧" => &["jin"], + "嬨" => &["ci"], + "嬩" => &["yu"], + "嬪" => &["pin"], + "嬬" => &["xu"], + "嬭" => &["nai"], + "嬮" => &["yan"], + "嬯" => &["tai"], + "嬰" => &["ying"], + "嬱" => &["can"], + "嬲" => &["niao"], + "嬴" => &["ying"], + "嬵" => &["mian"], + "嬷" => &["ma"], + "嬸" => &["shen"], + "嬹" => &["xing"], + "嬺" => &["ni"], + "嬻" => &["du"], + "嬼" => &["liu"], + "嬽" => &["yuan"], + "嬾" => &["lan"], + "嬿" => &["yan"], + "孀" => &["shuang"], + "孁" => &["ling"], + "孂" => &["jiao"], + "孃" => &["niang"], + "孄" => &["lan"], + "孅" => &["xian"], + "孆" => &["ying"], + "孇" => &["shuang"], + "孈" => &["shuai"], + "孉" => &["quan"], + "孊" => &["mi"], + "孋" => &["li"], + "孌" => &["luan"], + "孍" => &["yan"], + "孎" => &["zhu"], + "孏" => &["lan"], + "子" => &["zi"], + "孑" => &["jie"], + "孒" => &["jue"], + "孓" => &["jue"], + "孔" => &["kong"], + "孕" => &["yun"], + "孖" => &["zi"], + "字" => &["zi"], + "存" => &["cun"], + "孙" => &["sun"], + "孚" => &["fu"], + "孛" => &["bei","bo"], + "孜" => &["zi"], + "孝" => &["xiao"], + "孞" => &["xin"], + "孟" => &["meng"], + "孠" => &["si"], + "孡" => &["tai"], + "孢" => &["bao"], + "季" => &["ji"], + "孤" => &["gu"], + "孥" => &["nu"], + "学" => &["xue"], + "孨" => &["chan"], + "孩" => &["hai"], + "孪" => &["luan"], + "孫" => &["sun"], + "孬" => &["nao"], + "孭" => &["mie"], + "孮" => &["cong"], + "孯" => &["jian"], + "孰" => &["shu"], + "孱" => &["chan","can"], + "孲" => &["ya"], + "孳" => &["zi"], + "孴" => &["ni"], + "孵" => &["fu"], + "孶" => &["zi"], + "孷" => &["li"], + "學" => &["xue","xiao"], + "孹" => &["bo"], + "孺" => &["ru"], + "孻" => &["nai"], + "孼" => &["nie"], + "孽" => &["nie"], + "孾" => &["ying"], + "孿" => &["luan"], + "宀" => &["mian"], + "宁" => &["ning"], + "宂" => &["rong"], + "它" => &["ta"], + "宄" => &["gui"], + "宅" => &["zhai","zhe"], + "宆" => &["qiong"], + "宇" => &["yu"], + "守" => &["shou"], + "安" => &["an"], + "宊" => &["tu"], + "宋" => &["song"], + "完" => &["wan"], + "宍" => &["rou"], + "宎" => &["yao"], + "宏" => &["hong"], + "宐" => &["yi"], + "宑" => &["jing"], + "宒" => &["zhun"], + "宓" => &["mi"], + "宔" => &["guai"], + "宕" => &["dang"], + "宖" => &["hong"], + "宗" => &["zong"], + "官" => &["guan"], + "宙" => &["zhou"], + "定" => &["ding"], + "宛" => &["wan","yuan"], + "宜" => &["yi"], + "宝" => &["bao"], + "实" => &["shi"], + "実" => &["shi"], + "宠" => &["chong"], + "审" => &["shen"], + "客" => &["ke"], + "宣" => &["xuan"], + "室" => &["shi"], + "宥" => &["you"], + "宦" => &["huan"], + "宧" => &["yi"], + "宨" => &["tiao"], + "宩" => &["shi"], + "宪" => &["xian"], + "宫" => &["gong"], + "宬" => &["cheng"], + "宭" => &["qun"], + "宮" => &["gong"], + "宯" => &["xiao"], + "宰" => &["zai"], + "宱" => &["zha"], + "宲" => &["bao"], + "害" => &["hai"], + "宴" => &["yan"], + "宵" => &["xiao"], + "家" => &["jia","gu","jie"], + "宷" => &["shen"], + "宸" => &["chen"], + "容" => &["rong"], + "宺" => &["huang"], + "宻" => &["mi"], + "宼" => &["kou"], + "宽" => &["kuan"], + "宾" => &["bin"], + "宿" => &["su","xiu"], + "寀" => &["cai","shen"], + "寁" => &["zan"], + "寂" => &["ji"], + "寃" => &["yuan"], + "寄" => &["ji"], + "寅" => &["yin"], + "密" => &["mi"], + "寇" => &["kou"], + "寈" => &["qing"], + "寉" => &["he"], + "寊" => &["zhen"], + "寋" => &["jian"], + "富" => &["fu"], + "寍" => &["ning"], + "寎" => &["bing"], + "寏" => &["huan"], + "寐" => &["mei"], + "寑" => &["qin"], + "寒" => &["han"], + "寓" => &["yu"], + "寔" => &["shi"], + "寕" => &["ning"], + "寖" => &["jin"], + "寗" => &["ning"], + "寘" => &["zhi"], + "寙" => &["yu"], + "寚" => &["bao"], + "寛" => &["kuan"], + "寜" => &["ning"], + "寝" => &["qin"], + "寞" => &["mo"], + "察" => &["cha"], + "寠" => &["ju"], + "寡" => &["gua"], + "寢" => &["qin"], + "寣" => &["hu"], + "寤" => &["wu"], + "寥" => &["liao"], + "實" => &["shi"], + "寧" => &["ning"], + "寨" => &["zhai"], + "審" => &["shen"], + "寪" => &["wei"], + "寫" => &["xie"], + "寬" => &["kuan"], + "寭" => &["hui"], + "寮" => &["liao"], + "寯" => &["jun"], + "寰" => &["huan"], + "寱" => &["yi"], + "寲" => &["yi"], + "寳" => &["bao"], + "寴" => &["qin"], + "寵" => &["chong"], + "寶" => &["bao"], + "寷" => &["feng"], + "寸" => &["cun"], + "对" => &["dui"], + "寺" => &["si"], + "寻" => &["xun","xin"], + "导" => &["dao"], + "寽" => &["lu:","luo"], + "対" => &["dui"], + "寿" => &["shou"], + "尀" => &["po"], + "封" => &["feng"], + "専" => &["zhuan"], + "尃" => &["fu"], + "射" => &["she","shi","ye"], + "尅" => &["ke"], + "将" => &["jiang","qiang"], + "將" => &["jiang","qiang"], + "專" => &["zhuan"], + "尉" => &["wei","yu"], + "尊" => &["zun"], + "尋" => &["xun","xin"], + "尌" => &["shu"], + "對" => &["dui"], + "導" => &["dao"], + "小" => &["xiao"], + "尐" => &["ji"], + "少" => &["shao"], + "尒" => &["er"], + "尓" => &["er"], + "尔" => &["er"], + "尕" => &["ga"], + "尖" => &["jian"], + "尗" => &["shu"], + "尘" => &["chen"], + "尙" => &["shang"], + "尚" => &["shang"], + "尛" => &["yuan"], + "尜" => &["ga"], + "尝" => &["chang"], + "尞" => &["liao"], + "尟" => &["xian"], + "尠" => &["xian"], + "尢" => &["wang","you"], + "尣" => &["wang","you"], + "尤" => &["you"], + "尥" => &["liao"], + "尦" => &["liao"], + "尧" => &["yao"], + "尨" => &["mang","pang"], + "尩" => &["wang"], + "尪" => &["wang"], + "尫" => &["wang"], + "尬" => &["ga"], + "尭" => &["yao"], + "尮" => &["duo"], + "尯" => &["kui"], + "尰" => &["zhong"], + "就" => &["jiu"], + "尲" => &["gan"], + "尳" => &["gu"], + "尴" => &["gan"], + "尵" => &["gan"], + "尶" => &["gan"], + "尷" => &["gan"], + "尸" => &["shi"], + "尹" => &["yin"], + "尺" => &["chi","che"], + "尻" => &["kao"], + "尼" => &["ni"], + "尽" => &["jin"], + "尾" => &["wei","yi"], + "尿" => &["niao","sui","ni"], + "局" => &["ju"], + "屁" => &["pi"], + "层" => &["ceng"], + "屃" => &["xi"], + "屄" => &["bi"], + "居" => &["ju","ji"], + "屆" => &["jie"], + "屇" => &["tian"], + "屈" => &["qu"], + "屉" => &["ti"], + "届" => &["jie"], + "屋" => &["wu"], + "屌" => &["diao"], + "屍" => &["shi"], + "屎" => &["shi"], + "屏" => &["ping","bing"], + "屐" => &["ji"], + "屑" => &["xie"], + "屒" => &["chen"], + "屓" => &["xi"], + "屔" => &["ni"], + "展" => &["zhan"], + "屖" => &["xi"], + "屘" => &["man"], + "屙" => &["e"], + "屚" => &["lou"], + "屛" => &["ping"], + "屜" => &["ti"], + "屝" => &["fei"], + "属" => &["shu","zhu"], + "屟" => &["xie"], + "屠" => &["tu"], + "屡" => &["lu:"], + "屢" => &["lu:"], + "屣" => &["xi"], + "層" => &["ceng"], + "履" => &["lu:"], + "屦" => &["ju"], + "屧" => &["xie"], + "屨" => &["ju"], + "屩" => &["jue"], + "屪" => &["liao"], + "屫" => &["jue"], + "屬" => &["shu","zhu"], + "屭" => &["xi"], + "屮" => &["che"], + "屯" => &["tun","zhun"], + "屰" => &["ni"], + "山" => &["shan"], + "屲" => &["wa"], + "屳" => &["xian"], + "屴" => &["li"], + "屵" => &["e"], + "屸" => &["long"], + "屹" => &["yi","ge"], + "屺" => &["qi"], + "屻" => &["ren"], + "屼" => &["wu"], + "屽" => &["han"], + "屾" => &["shen"], + "屿" => &["yu"], + "岀" => &["chu"], + "岁" => &["sui"], + "岂" => &["qi"], + "岄" => &["yue"], + "岅" => &["ban"], + "岆" => &["yao"], + "岇" => &["ang"], + "岈" => &["ya"], + "岉" => &["wu"], + "岊" => &["jie"], + "岋" => &["e"], + "岌" => &["ji"], + "岍" => &["qian"], + "岎" => &["fen"], + "岏" => &["wan"], + "岐" => &["qi"], + "岑" => &["cen"], + "岒" => &["qian"], + "岓" => &["qi"], + "岔" => &["cha"], + "岕" => &["jie"], + "岖" => &["qu"], + "岗" => &["gang"], + "岘" => &["xian"], + "岙" => &["ao"], + "岚" => &["lan"], + "岛" => &["dao"], + "岜" => &["ba"], + "岝" => &["zhai"], + "岞" => &["zuo"], + "岟" => &["yang"], + "岠" => &["ju"], + "岡" => &["gang"], + "岢" => &["ke"], + "岣" => &["gou"], + "岤" => &["xue"], + "岥" => &["bo"], + "岦" => &["li"], + "岧" => &["tiao"], + "岨" => &["qu"], + "岩" => &["yan"], + "岪" => &["fu"], + "岫" => &["xiu"], + "岬" => &["jia"], + "岭" => &["ling"], + "岮" => &["tuo"], + "岯" => &["pei"], + "岰" => &["you"], + "岱" => &["dai"], + "岲" => &["kuang"], + "岳" => &["yue"], + "岴" => &["qu"], + "岵" => &["hu"], + "岶" => &["po"], + "岷" => &["min"], + "岸" => &["an"], + "岹" => &["tiao"], + "岺" => &["ling"], + "岻" => &["chi"], + "岽" => &["dong"], + "岿" => &["kui"], + "峀" => &["xiu"], + "峁" => &["mao"], + "峂" => &["tong"], + "峃" => &["xue"], + "峄" => &["yi"], + "峆" => &["he"], + "峇" => &["ke"], + "峈" => &["luo"], + "峉" => &["e"], + "峊" => &["fu"], + "峋" => &["xun"], + "峌" => &["die"], + "峍" => &["lu"], + "峎" => &["lang"], + "峏" => &["er"], + "峐" => &["gai"], + "峑" => &["quan"], + "峒" => &["tong","dong"], + "峓" => &["yi"], + "峔" => &["mu"], + "峕" => &["shi"], + "峖" => &["an"], + "峗" => &["wei"], + "峘" => &["hu"], + "峙" => &["zhi","shi"], + "峚" => &["mi"], + "峛" => &["li"], + "峜" => &["ji"], + "峝" => &["tong"], + "峞" => &["kui"], + "峟" => &["you"], + "峡" => &["xia"], + "峢" => &["li"], + "峣" => &["yao"], + "峤" => &["jiao","qiao"], + "峥" => &["zheng"], + "峦" => &["luan"], + "峧" => &["jiao"], + "峨" => &["e"], + "峩" => &["e"], + "峪" => &["yu"], + "峫" => &["ye"], + "峬" => &["bu"], + "峭" => &["qiao"], + "峮" => &["qun"], + "峯" => &["feng"], + "峰" => &["feng"], + "峱" => &["nao"], + "峲" => &["li"], + "峳" => &["you"], + "峴" => &["xian"], + "峵" => &["hong"], + "島" => &["dao"], + "峷" => &["shen"], + "峸" => &["cheng"], + "峹" => &["tu"], + "峺" => &["geng"], + "峻" => &["jun"], + "峼" => &["hao"], + "峽" => &["xia"], + "峾" => &["yin"], + "峿" => &["wu","yu"], + "崀" => &["lang"], + "崁" => &["kan"], + "崂" => &["lao"], + "崃" => &["lai"], + "崄" => &["xian"], + "崅" => &["que"], + "崆" => &["kong"], + "崇" => &["chong"], + "崈" => &["chong"], + "崉" => &["ta"], + "崋" => &["hua"], + "崌" => &["ju"], + "崍" => &["lai"], + "崎" => &["qi"], + "崏" => &["min"], + "崐" => &["kun"], + "崑" => &["kun"], + "崒" => &["zu"], + "崓" => &["gu"], + "崔" => &["cui"], + "崕" => &["ya"], + "崖" => &["ya","ai"], + "崗" => &["gang"], + "崘" => &["lun"], + "崙" => &["lun"], + "崚" => &["leng"], + "崛" => &["jue"], + "崜" => &["duo"], + "崝" => &["cheng"], + "崞" => &["guo"], + "崟" => &["yin"], + "崠" => &["dong"], + "崡" => &["han"], + "崢" => &["zheng"], + "崣" => &["wei"], + "崤" => &["yao","xiao"], + "崥" => &["pi"], + "崦" => &["yan"], + "崧" => &["song"], + "崨" => &["jie"], + "崩" => &["beng"], + "崪" => &["zu"], + "崫" => &["jue"], + "崬" => &["dong"], + "崭" => &["zhan"], + "崮" => &["gu"], + "崯" => &["yin"], + "崰" => &["zi"], + "崱" => &["ze"], + "崲" => &["huang"], + "崳" => &["yu"], + "崴" => &["wei","wai"], + "崵" => &["yang"], + "崶" => &["feng"], + "崷" => &["qiu"], + "崸" => &["dun"], + "崹" => &["ti"], + "崺" => &["yi"], + "崻" => &["zhi"], + "崼" => &["shi"], + "崽" => &["zai"], + "崾" => &["yao"], + "崿" => &["e"], + "嵀" => &["zhu"], + "嵁" => &["kan"], + "嵂" => &["lu:"], + "嵃" => &["yan"], + "嵄" => &["mei"], + "嵅" => &["gan"], + "嵆" => &["ji"], + "嵇" => &["ji"], + "嵈" => &["huan"], + "嵉" => &["ting"], + "嵊" => &["sheng"], + "嵋" => &["mei"], + "嵌" => &["qian","kan"], + "嵍" => &["wu"], + "嵎" => &["yu"], + "嵏" => &["zong"], + "嵐" => &["lan"], + "嵑" => &["jie","he"], + "嵒" => &["yan"], + "嵓" => &["yan"], + "嵔" => &["wei"], + "嵕" => &["zong"], + "嵖" => &["cha"], + "嵗" => &["sui"], + "嵘" => &["rong"], + "嵙" => &["ke"], + "嵚" => &["qin"], + "嵛" => &["yu"], + "嵜" => &["qi"], + "嵝" => &["lou"], + "嵞" => &["tu"], + "嵟" => &["dui"], + "嵠" => &["xi"], + "嵡" => &["weng"], + "嵢" => &["cang"], + "嵣" => &["dang"], + "嵤" => &["rong"], + "嵥" => &["jie"], + "嵦" => &["ai"], + "嵧" => &["liu"], + "嵨" => &["wu"], + "嵩" => &["song"], + "嵪" => &["qiao"], + "嵫" => &["zi"], + "嵬" => &["wei"], + "嵭" => &["beng"], + "嵮" => &["dian"], + "嵯" => &["cuo"], + "嵰" => &["qian"], + "嵱" => &["yong"], + "嵲" => &["nie"], + "嵳" => &["cuo"], + "嵴" => &["ji"], + "嵷" => &["song"], + "嵸" => &["zong"], + "嵹" => &["jiang"], + "嵺" => &["liao"], + "嵼" => &["chan"], + "嵽" => &["di"], + "嵾" => &["cen"], + "嵿" => &["ding"], + "嶀" => &["tu","die"], + "嶁" => &["lou"], + "嶂" => &["zhang"], + "嶃" => &["zhan"], + "嶄" => &["zhan"], + "嶅" => &["ao"], + "嶆" => &["cao"], + "嶇" => &["qu"], + "嶈" => &["qiang"], + "嶉" => &["zui"], + "嶊" => &["zui"], + "嶋" => &["dao"], + "嶌" => &["dao"], + "嶍" => &["xi"], + "嶎" => &["yu"], + "嶏" => &["bo"], + "嶐" => &["long"], + "嶑" => &["xiang"], + "嶒" => &["ceng"], + "嶓" => &["bo"], + "嶔" => &["qin"], + "嶕" => &["jiao"], + "嶖" => &["yan"], + "嶗" => &["lao"], + "嶘" => &["zhan"], + "嶙" => &["lin"], + "嶚" => &["liao"], + "嶛" => &["liao"], + "嶜" => &["jin"], + "嶝" => &["deng"], + "嶞" => &["duo"], + "嶟" => &["zun"], + "嶠" => &["jiao","qiao"], + "嶡" => &["gui"], + "嶢" => &["yao"], + "嶣" => &["qiao"], + "嶤" => &["yao"], + "嶥" => &["jue"], + "嶦" => &["zhan"], + "嶧" => &["yi"], + "嶨" => &["xue"], + "嶩" => &["nao"], + "嶪" => &["ye"], + "嶫" => &["ye"], + "嶬" => &["yi"], + "嶭" => &["e"], + "嶮" => &["xian"], + "嶯" => &["ji"], + "嶰" => &["xie"], + "嶱" => &["ke"], + "嶲" => &["sui"], + "嶳" => &["di"], + "嶴" => &["ao"], + "嶵" => &["zui"], + "嶷" => &["yi"], + "嶸" => &["rong"], + "嶹" => &["dao"], + "嶺" => &["ling"], + "嶻" => &["za"], + "嶼" => &["yu"], + "嶽" => &["yue"], + "嶾" => &["yin"], + "巀" => &["jie"], + "巁" => &["li"], + "巂" => &["sui","xi"], + "巃" => &["long"], + "巄" => &["long"], + "巅" => &["dian"], + "巆" => &["ying"], + "巇" => &["xi"], + "巈" => &["ju"], + "巉" => &["chan"], + "巊" => &["ying"], + "巋" => &["kui"], + "巌" => &["yan"], + "巍" => &["wei"], + "巎" => &["nao"], + "巏" => &["quan"], + "巐" => &["chao"], + "巑" => &["cuan"], + "巒" => &["luan"], + "巓" => &["dian"], + "巔" => &["dian"], + "巕" => &["nie"], + "巖" => &["yan"], + "巗" => &["yan"], + "巘" => &["yan"], + "巙" => &["nao"], + "巚" => &["yan"], + "巛" => &["chuan"], + "巜" => &["gui"], + "川" => &["chuan"], + "州" => &["zhou"], + "巟" => &["huang"], + "巠" => &["jing"], + "巡" => &["xun"], + "巢" => &["chao"], + "巣" => &["chao"], + "巤" => &["lie"], + "工" => &["gong"], + "左" => &["zuo"], + "巧" => &["qiao"], + "巨" => &["ju"], + "巩" => &["gong"], + "巫" => &["wu"], + "差" => &["cha","chai","ci"], + "巯" => &["qiu"], + "巰" => &["qiu"], + "己" => &["ji"], + "已" => &["yi"], + "巳" => &["si"], + "巴" => &["ba"], + "巵" => &["zhi"], + "巶" => &["zhao"], + "巷" => &["xiang","hang"], + "巸" => &["yi"], + "巹" => &["jin"], + "巺" => &["xun"], + "巻" => &["juan"], + "巽" => &["xun"], + "巾" => &["jin"], + "巿" => &["fu"], + "帀" => &["za"], + "币" => &["bi"], + "市" => &["shi"], + "布" => &["bu"], + "帄" => &["ding"], + "帅" => &["shuai"], + "帆" => &["fan"], + "帇" => &["nie"], + "师" => &["shi"], + "帉" => &["fen"], + "帊" => &["pa"], + "帋" => &["zhi"], + "希" => &["xi"], + "帍" => &["hu"], + "帎" => &["dan"], + "帏" => &["wei"], + "帐" => &["zhang"], + "帑" => &["tang"], + "帒" => &["dai"], + "帓" => &["ma"], + "帔" => &["pei"], + "帕" => &["pa"], + "帖" => &["tie"], + "帗" => &["fu"], + "帘" => &["lian"], + "帙" => &["zhi"], + "帚" => &["zhou"], + "帛" => &["bo"], + "帜" => &["zhi"], + "帝" => &["di"], + "帞" => &["mo"], + "帟" => &["yi"], + "帠" => &["yi"], + "帡" => &["ping"], + "帢" => &["qia"], + "帣" => &["juan"], + "帤" => &["ru"], + "帥" => &["shuai","shuo"], + "带" => &["dai"], + "帧" => &["zhen"], + "帨" => &["shui"], + "帩" => &["qiao"], + "帪" => &["zhen"], + "師" => &["shi"], + "帬" => &["qun"], + "席" => &["xi"], + "帮" => &["bang"], + "帯" => &["dai"], + "帰" => &["gui"], + "帱" => &["chou","dao"], + "帲" => &["ping"], + "帳" => &["zhang"], + "帴" => &["sha"], + "帵" => &["wan"], + "帶" => &["dai"], + "帷" => &["wei"], + "常" => &["chang"], + "帹" => &["sha"], + "帺" => &["qi"], + "帻" => &["ze"], + "帼" => &["guo"], + "帽" => &["mao"], + "帾" => &["du"], + "帿" => &["hou"], + "幀" => &["zhen","zheng"], + "幁" => &["xu"], + "幂" => &["mi"], + "幃" => &["wei"], + "幄" => &["wo"], + "幅" => &["fu"], + "幆" => &["yi"], + "幇" => &["bang"], + "幈" => &["ping"], + "幊" => &["gong"], + "幋" => &["pan"], + "幌" => &["huang"], + "幍" => &["dao","tao"], + "幎" => &["mi"], + "幏" => &["jia"], + "幐" => &["teng"], + "幑" => &["hui"], + "幒" => &["zhong"], + "幓" => &["sen"], + "幔" => &["man"], + "幕" => &["mu"], + "幖" => &["biao"], + "幗" => &["guo"], + "幘" => &["ze"], + "幙" => &["mu"], + "幚" => &["bang"], + "幛" => &["zhang"], + "幜" => &["jiong"], + "幝" => &["chan"], + "幞" => &["fu"], + "幟" => &["zhi"], + "幠" => &["hu"], + "幡" => &["fan"], + "幢" => &["zhuang","chuang"], + "幣" => &["bi"], + "幤" => &["bi"], + "幦" => &["mi"], + "幧" => &["qiao"], + "幨" => &["dan"], + "幩" => &["fen"], + "幪" => &["meng"], + "幫" => &["bang"], + "幬" => &["chou","dao"], + "幭" => &["mie"], + "幮" => &["chu"], + "幯" => &["jie"], + "幰" => &["xian"], + "幱" => &["lan"], + "干" => &["gan"], + "平" => &["ping"], + "年" => &["nian"], + "幵" => &["jian"], + "并" => &["bing"], + "幷" => &["bing"], + "幸" => &["xing"], + "幹" => &["gan"], + "幺" => &["yao"], + "幻" => &["huan"], + "幼" => &["you"], + "幽" => &["you"], + "幾" => &["ji"], + "广" => &["guang","an"], + "庀" => &["pi"], + "庁" => &["ting"], + "庂" => &["ze"], + "広" => &["guang","an"], + "庄" => &["zhuang"], + "庅" => &["mo"], + "庆" => &["qing"], + "庇" => &["bi"], + "庈" => &["qin"], + "庉" => &["dun"], + "床" => &["chuang"], + "庋" => &["gui"], + "庌" => &["ya"], + "庍" => &["bai"], + "庎" => &["jie"], + "序" => &["xu"], + "庐" => &["lu"], + "庑" => &["wu"], + "库" => &["ku"], + "应" => &["ying"], + "底" => &["di","de"], + "庖" => &["pao"], + "店" => &["dian"], + "庘" => &["ya"], + "庙" => &["miao"], + "庚" => &["geng"], + "庛" => &["ci"], + "府" => &["fu"], + "庝" => &["tong"], + "庞" => &["pang"], + "废" => &["fei"], + "庠" => &["xiang"], + "庡" => &["yi"], + "庢" => &["zhi"], + "庣" => &["tiao"], + "庤" => &["zhi"], + "庥" => &["xiu"], + "度" => &["du","duo"], + "座" => &["zuo"], + "庨" => &["xiao"], + "庩" => &["tu"], + "庪" => &["gui"], + "庫" => &["ku"], + "庬" => &["pang","mang"], + "庭" => &["ting"], + "庮" => &["you"], + "庯" => &["bu"], + "庰" => &["bing"], + "庱" => &["cheng"], + "庲" => &["lai"], + "庳" => &["bi","bei"], + "庴" => &["ji"], + "庵" => &["an"], + "庶" => &["shu"], + "康" => &["kang"], + "庸" => &["yong"], + "庹" => &["tuo"], + "庺" => &["song"], + "庻" => &["shu"], + "庼" => &["qing"], + "庽" => &["yu"], + "庾" => &["yu"], + "庿" => &["miao"], + "廀" => &["sou"], + "廁" => &["ce","ci"], + "廂" => &["xiang"], + "廃" => &["fei"], + "廄" => &["jiu"], + "廅" => &["he"], + "廆" => &["hui"], + "廇" => &["liu"], + "廈" => &["sha","xia"], + "廉" => &["lian"], + "廊" => &["lang"], + "廋" => &["sou"], + "廌" => &["jian"], + "廍" => &["pou"], + "廎" => &["qing"], + "廏" => &["jiu"], + "廐" => &["jiu"], + "廑" => &["qin","jin"], + "廒" => &["ao"], + "廓" => &["kuo"], + "廔" => &["lou"], + "廕" => &["yin"], + "廖" => &["liao"], + "廗" => &["dai"], + "廘" => &["lu"], + "廙" => &["yi"], + "廚" => &["chu"], + "廛" => &["chan"], + "廜" => &["tu"], + "廝" => &["si"], + "廞" => &["xin"], + "廟" => &["miao"], + "廠" => &["chang","an"], + "廡" => &["wu"], + "廢" => &["fei"], + "廣" => &["guang","an"], + "廥" => &["guai"], + "廦" => &["bi"], + "廧" => &["qiang"], + "廨" => &["xie"], + "廩" => &["lin"], + "廪" => &["lin"], + "廫" => &["liao"], + "廬" => &["lu"], + "廮" => &["ying"], + "廯" => &["xian"], + "廰" => &["ting"], + "廱" => &["yong"], + "廲" => &["li"], + "廳" => &["ting"], + "廴" => &["yin"], + "廵" => &["xun"], + "延" => &["yan"], + "廷" => &["ting"], + "廸" => &["di"], + "廹" => &["po"], + "建" => &["jian"], + "廻" => &["hui"], + "廼" => &["nai"], + "廽" => &["hui"], + "廾" => &["gong"], + "廿" => &["nian"], + "开" => &["kai"], + "弁" => &["bian"], + "异" => &["yi"], + "弃" => &["qi"], + "弄" => &["nong","long"], + "弅" => &["fen"], + "弆" => &["ju"], + "弇" => &["yan"], + "弈" => &["yi"], + "弉" => &["zang","zhuang"], + "弊" => &["bi"], + "弋" => &["yi"], + "弌" => &["yi"], + "弍" => &["er"], + "弎" => &["san"], + "式" => &["shi"], + "弐" => &["er"], + "弑" => &["shi"], + "弒" => &["shi"], + "弓" => &["gong"], + "弔" => &["diao"], + "引" => &["yin"], + "弖" => &["hu"], + "弗" => &["fu"], + "弘" => &["hong"], + "弙" => &["wu"], + "弚" => &["tui"], + "弛" => &["chi"], + "弜" => &["qiang"], + "弝" => &["ba"], + "弞" => &["shen"], + "弟" => &["di"], + "张" => &["zhang"], + "弡" => &["jue"], + "弢" => &["tao"], + "弣" => &["fu"], + "弤" => &["di"], + "弥" => &["mi"], + "弦" => &["xian"], + "弧" => &["hu"], + "弨" => &["chao"], + "弩" => &["nu"], + "弪" => &["jing"], + "弫" => &["zhen"], + "弬" => &["yi"], + "弭" => &["mi"], + "弮" => &["quan"], + "弯" => &["wan"], + "弰" => &["shao"], + "弱" => &["ruo"], + "弲" => &["xuan"], + "弳" => &["jing"], + "弴" => &["diao"], + "張" => &["zhang"], + "弶" => &["jiang"], + "強" => &["qiang","jiang"], + "弸" => &["beng"], + "弹" => &["dan","tan"], + "强" => &["qiang","jiang"], + "弻" => &["bi"], + "弼" => &["bi"], + "弽" => &["she"], + "弾" => &["dan","tan"], + "弿" => &["jian"], + "彀" => &["gou"], + "彂" => &["fa"], + "彃" => &["bi"], + "彄" => &["kou"], + "彆" => &["bie"], + "彇" => &["xiao"], + "彈" => &["dan","tan"], + "彉" => &["kuang"], + "彊" => &["qiang","jiang"], + "彋" => &["hong"], + "彌" => &["mi"], + "彍" => &["kuo"], + "彎" => &["wan"], + "彏" => &["jue"], + "彐" => &["ji"], + "彑" => &["ji"], + "归" => &["gui"], + "当" => &["dang"], + "彔" => &["lu"], + "录" => &["lu"], + "彖" => &["tuan"], + "彗" => &["hui"], + "彘" => &["zhi"], + "彙" => &["hui"], + "彚" => &["hui"], + "彛" => &["yi"], + "彜" => &["yi"], + "彝" => &["yi"], + "彞" => &["yi"], + "彟" => &["huo"], + "彠" => &["huo"], + "彡" => &["shan"], + "形" => &["xing"], + "彣" => &["zhang"], + "彤" => &["tong"], + "彥" => &["yan"], + "彦" => &["yan"], + "彧" => &["yu"], + "彨" => &["chi"], + "彩" => &["cai"], + "彪" => &["biao"], + "彫" => &["diao"], + "彬" => &["bin"], + "彭" => &["peng"], + "彮" => &["yong"], + "彯" => &["piao"], + "彰" => &["zhang"], + "影" => &["ying"], + "彲" => &["chi"], + "彳" => &["chi"], + "彴" => &["zhuo"], + "彵" => &["tuo"], + "彶" => &["ji"], + "彷" => &["pang","fang"], + "彸" => &["zhong"], + "役" => &["yi"], + "彺" => &["wang"], + "彻" => &["che"], + "彼" => &["bi"], + "彽" => &["di"], + "彾" => &["ling"], + "彿" => &["fu"], + "往" => &["wang"], + "征" => &["zheng"], + "徂" => &["cu"], + "徃" => &["wang"], + "径" => &["jing"], + "待" => &["dai"], + "徆" => &["xi"], + "徇" => &["xun"], + "很" => &["hen"], + "徉" => &["yang"], + "徊" => &["huai","hui"], + "律" => &["lu:"], + "後" => &["hou"], + "徍" => &["wang"], + "徎" => &["cheng"], + "徏" => &["zhi"], + "徐" => &["xu"], + "徑" => &["jing"], + "徒" => &["tu"], + "従" => &["cong"], + "徕" => &["lai"], + "徖" => &["cong"], + "得" => &["de","dei"], + "徘" => &["pai"], + "徙" => &["xi"], + "徛" => &["qi"], + "徜" => &["chang"], + "徝" => &["zhi"], + "從" => &["cong","zong"], + "徟" => &["zhou"], + "徠" => &["lai"], + "御" => &["yu"], + "徢" => &["xie"], + "徣" => &["jie"], + "徤" => &["jian"], + "徥" => &["chi","shi"], + "徦" => &["jia"], + "徧" => &["bian"], + "徨" => &["huang"], + "復" => &["fu"], + "循" => &["xun"], + "徫" => &["wei"], + "徬" => &["pang"], + "徭" => &["yao"], + "微" => &["wei"], + "徯" => &["xi"], + "徰" => &["zheng"], + "徱" => &["piao"], + "徲" => &["chi"], + "徳" => &["de"], + "徴" => &["zheng"], + "徵" => &["zhi","zheng"], + "徶" => &["bie"], + "德" => &["de"], + "徸" => &["chong"], + "徹" => &["che"], + "徺" => &["jiao"], + "徻" => &["wei"], + "徼" => &["jiao","jia"], + "徽" => &["hui"], + "徾" => &["mei"], + "徿" => &["long"], + "忀" => &["xiang"], + "忁" => &["bao"], + "忂" => &["qu"], + "心" => &["xin"], + "忄" => &["xin"], + "必" => &["bi"], + "忆" => &["yi"], + "忇" => &["le"], + "忈" => &["ren"], + "忉" => &["dao"], + "忊" => &["ding"], + "忋" => &["gai"], + "忌" => &["ji"], + "忍" => &["ren"], + "忎" => &["ren"], + "忏" => &["chan"], + "忐" => &["tan"], + "忑" => &["te"], + "忒" => &["te","tui","tei"], + "忓" => &["gan"], + "忔" => &["qi"], + "忕" => &["dai"], + "忖" => &["cun"], + "志" => &["zhi"], + "忘" => &["wang"], + "忙" => &["mang"], + "忚" => &["xi"], + "忛" => &["fan"], + "応" => &["ying"], + "忝" => &["tian"], + "忞" => &["min"], + "忟" => &["min"], + "忠" => &["zhong"], + "忡" => &["chong"], + "忢" => &["wu"], + "忣" => &["ji"], + "忤" => &["wu"], + "忥" => &["xi"], + "忦" => &["ye"], + "忧" => &["you"], + "忨" => &["wan"], + "忩" => &["zong"], + "忪" => &["zhong","song"], + "快" => &["kuai"], + "忬" => &["yu"], + "忭" => &["bian"], + "忮" => &["zhi"], + "忯" => &["chi"], + "忰" => &["cui"], + "忱" => &["chen"], + "忲" => &["tai"], + "忳" => &["tun"], + "忴" => &["qian"], + "念" => &["nian"], + "忶" => &["hun"], + "忷" => &["xiong"], + "忸" => &["niu","nu:"], + "忹" => &["wang"], + "忺" => &["xian"], + "忻" => &["xin"], + "忼" => &["kang"], + "忽" => &["hu"], + "忾" => &["kai"], + "忿" => &["fen"], + "怀" => &["huai"], + "态" => &["tai"], + "怂" => &["song"], + "怃" => &["wu"], + "怄" => &["ou"], + "怅" => &["chang"], + "怆" => &["chuang"], + "怇" => &["ju"], + "怈" => &["yi"], + "怉" => &["bao"], + "怊" => &["chao"], + "怋" => &["min"], + "怌" => &["pi"], + "怍" => &["zuo"], + "怎" => &["zen","ze"], + "怏" => &["yang"], + "怐" => &["kou"], + "怑" => &["ban"], + "怒" => &["nu"], + "怓" => &["nao"], + "怔" => &["zheng"], + "怕" => &["pa"], + "怖" => &["bu"], + "怗" => &["tie"], + "怘" => &["hu"], + "怙" => &["hu"], + "怚" => &["ju"], + "怛" => &["da"], + "怜" => &["lian","ling"], + "思" => &["si","sai"], + "怞" => &["zhou"], + "怟" => &["di"], + "怠" => &["dai"], + "怡" => &["yi"], + "怢" => &["tu"], + "怣" => &["you"], + "怤" => &["fu"], + "急" => &["ji"], + "怦" => &["peng"], + "性" => &["xing"], + "怨" => &["yuan"], + "怩" => &["ni"], + "怪" => &["guai"], + "怫" => &["fu","fei"], + "怬" => &["xi"], + "怭" => &["bi"], + "怮" => &["you"], + "怯" => &["qie","que"], + "怰" => &["xuan"], + "怱" => &["zong"], + "怲" => &["bing"], + "怳" => &["huang"], + "怴" => &["xu"], + "怵" => &["chu"], + "怶" => &["pi"], + "怷" => &["xi"], + "怸" => &["xi"], + "怹" => &["tan"], + "总" => &["zong"], + "怼" => &["dui"], + "怿" => &["yi"], + "恀" => &["chi"], + "恁" => &["nen","ren","nin"], + "恂" => &["xun"], + "恃" => &["shi"], + "恄" => &["xi"], + "恅" => &["lao"], + "恆" => &["heng"], + "恇" => &["kuang"], + "恈" => &["mou","mu"], + "恉" => &["zhi"], + "恊" => &["xie"], + "恋" => &["lian"], + "恌" => &["tiao"], + "恍" => &["huang"], + "恎" => &["die"], + "恏" => &["hao"], + "恐" => &["kong"], + "恑" => &["gui"], + "恒" => &["heng"], + "恓" => &["xi"], + "恔" => &["xiao"], + "恕" => &["shu"], + "恖" => &["sai","si"], + "恗" => &["hu"], + "恘" => &["qiu"], + "恙" => &["yang"], + "恚" => &["hui"], + "恛" => &["hui"], + "恜" => &["chi"], + "恝" => &["jia"], + "恞" => &["yi"], + "恟" => &["xiong"], + "恠" => &["guai"], + "恡" => &["lin"], + "恢" => &["hui"], + "恣" => &["zi"], + "恤" => &["xu"], + "恥" => &["chi"], + "恦" => &["xiang"], + "恧" => &["nu:"], + "恨" => &["hen"], + "恩" => &["en"], + "恪" => &["ke","que"], + "恫" => &["dong","tong"], + "恬" => &["tian"], + "恭" => &["gong"], + "恮" => &["quan"], + "息" => &["xi"], + "恰" => &["qia"], + "恱" => &["yue"], + "恲" => &["peng"], + "恳" => &["ken"], + "恴" => &["de"], + "恵" => &["hui"], + "恶" => &["e","wu"], + "恸" => &["tong"], + "恹" => &["yan"], + "恺" => &["kai"], + "恻" => &["ce"], + "恼" => &["nao"], + "恽" => &["yun"], + "恾" => &["mang"], + "恿" => &["yong"], + "悀" => &["yong"], + "悁" => &["juan"], + "悂" => &["mang"], + "悃" => &["kun"], + "悄" => &["qiao"], + "悅" => &["yue"], + "悆" => &["yu"], + "悇" => &["yu"], + "悈" => &["jie"], + "悉" => &["xi"], + "悊" => &["zhe","qi"], + "悋" => &["lin"], + "悌" => &["ti"], + "悍" => &["han"], + "悎" => &["hao"], + "悏" => &["qie"], + "悐" => &["ti"], + "悑" => &["bu"], + "悒" => &["yi"], + "悓" => &["qian"], + "悔" => &["hui"], + "悕" => &["xi"], + "悖" => &["bei"], + "悗" => &["man"], + "悘" => &["yi"], + "悙" => &["heng"], + "悚" => &["song"], + "悛" => &["quan"], + "悜" => &["cheng"], + "悝" => &["kui","li"], + "悞" => &["wu"], + "悟" => &["wu"], + "悠" => &["you"], + "悡" => &["li"], + "悢" => &["liang"], + "患" => &["huan"], + "悤" => &["cong"], + "悥" => &["yi"], + "悦" => &["yue"], + "悧" => &["li"], + "您" => &["nin"], + "悩" => &["nao"], + "悪" => &["e","wu"], + "悫" => &["que"], + "悬" => &["xuan"], + "悭" => &["qian"], + "悮" => &["wu"], + "悯" => &["min"], + "悰" => &["cong"], + "悱" => &["fei"], + "悲" => &["bei"], + "悳" => &["de"], + "悴" => &["cui"], + "悵" => &["chang"], + "悶" => &["men"], + "悷" => &["li"], + "悸" => &["ji"], + "悹" => &["guan"], + "悺" => &["guan"], + "悻" => &["xing"], + "悼" => &["dao"], + "悽" => &["qi"], + "悾" => &["kong"], + "悿" => &["tian"], + "惀" => &["lun"], + "惁" => &["xi"], + "惂" => &["kan"], + "惃" => &["kun"], + "惄" => &["ni"], + "情" => &["qing"], + "惆" => &["chou"], + "惇" => &["dun"], + "惈" => &["guo"], + "惉" => &["chan"], + "惊" => &["jing"], + "惋" => &["wan"], + "惌" => &["yuan"], + "惍" => &["jin"], + "惎" => &["ji"], + "惏" => &["lin"], + "惐" => &["yu"], + "惑" => &["huo"], + "惒" => &["he"], + "惓" => &["quan"], + "惔" => &["yan"], + "惕" => &["ti"], + "惖" => &["ti"], + "惗" => &["nie"], + "惘" => &["wang"], + "惙" => &["chuo"], + "惚" => &["hu"], + "惛" => &["hun"], + "惜" => &["xi"], + "惝" => &["chang"], + "惞" => &["xin"], + "惟" => &["wei"], + "惠" => &["hui"], + "惡" => &["e","wu"], + "惢" => &["rui"], + "惣" => &["zong"], + "惤" => &["jian"], + "惥" => &["yong"], + "惦" => &["dian"], + "惧" => &["ju"], + "惨" => &["can"], + "惩" => &["cheng"], + "惪" => &["de"], + "惫" => &["bei"], + "惬" => &["qie"], + "惭" => &["can"], + "惮" => &["dan"], + "惯" => &["guan"], + "惰" => &["duo"], + "惱" => &["nao"], + "惲" => &["yun"], + "想" => &["xiang"], + "惴" => &["zhui"], + "惵" => &["die"], + "惶" => &["huang"], + "惷" => &["chun"], + "惸" => &["qiong"], + "惹" => &["re"], + "惺" => &["xing"], + "惻" => &["ce"], + "惼" => &["bian"], + "惽" => &["hun"], + "惾" => &["zong"], + "惿" => &["ti"], + "愀" => &["qiao"], + "愁" => &["chou"], + "愂" => &["bei"], + "愃" => &["xuan"], + "愄" => &["wei"], + "愅" => &["ge"], + "愆" => &["qian"], + "愇" => &["wei"], + "愈" => &["yu"], + "愉" => &["yu"], + "愊" => &["bi"], + "愋" => &["xuan"], + "愌" => &["huan"], + "愍" => &["min"], + "愎" => &["bi"], + "意" => &["yi"], + "愐" => &["mian"], + "愑" => &["yong"], + "愒" => &["kai","qi"], + "愓" => &["dang"], + "愔" => &["yin"], + "愕" => &["e"], + "愖" => &["chen"], + "愗" => &["mou"], + "愘" => &["qia"], + "愙" => &["ke"], + "愚" => &["yu"], + "愛" => &["ai"], + "愜" => &["qie"], + "愝" => &["yan"], + "愞" => &["nuo"], + "感" => &["gan"], + "愠" => &["yun"], + "愡" => &["zong"], + "愢" => &["sai"], + "愣" => &["leng"], + "愤" => &["fen"], + "愦" => &["kui"], + "愧" => &["kui"], + "愨" => &["que"], + "愩" => &["gong"], + "愪" => &["yun"], + "愫" => &["su"], + "愬" => &["su"], + "愭" => &["qi"], + "愮" => &["yao"], + "愯" => &["song"], + "愰" => &["huang"], + "愲" => &["gu"], + "愳" => &["ju"], + "愴" => &["chuang"], + "愵" => &["ta"], + "愶" => &["xie"], + "愷" => &["kai"], + "愸" => &["zheng"], + "愹" => &["yong"], + "愺" => &["cao"], + "愻" => &["sun"], + "愼" => &["shen"], + "愽" => &["bo"], + "愾" => &["kai"], + "愿" => &["yuan"], + "慀" => &["xie"], + "慁" => &["hun"], + "慂" => &["yong"], + "慃" => &["yang"], + "慄" => &["li"], + "慅" => &["sao"], + "慆" => &["tao"], + "慇" => &["yin"], + "慈" => &["ci"], + "慉" => &["xu"], + "慊" => &["qian","qie"], + "態" => &["tai"], + "慌" => &["huang"], + "慍" => &["yun"], + "慎" => &["shen"], + "慏" => &["ming"], + "慑" => &["she"], + "慒" => &["cong"], + "慓" => &["piao"], + "慔" => &["mo"], + "慕" => &["mu"], + "慖" => &["guo"], + "慗" => &["chi"], + "慘" => &["can"], + "慙" => &["can"], + "慚" => &["can"], + "慛" => &["cui"], + "慜" => &["min"], + "慝" => &["ni","te"], + "慞" => &["zhang"], + "慟" => &["tong"], + "慠" => &["ao"], + "慡" => &["shuang"], + "慢" => &["man"], + "慣" => &["guan"], + "慤" => &["que"], + "慥" => &["zao"], + "慦" => &["jiu"], + "慧" => &["hui"], + "慨" => &["kai"], + "慩" => &["lian"], + "慪" => &["ou"], + "慫" => &["song"], + "慬" => &["jin"], + "慭" => &["yin"], + "慮" => &["lu:"], + "慯" => &["shang"], + "慰" => &["wei"], + "慱" => &["tuan"], + "慲" => &["man"], + "慳" => &["qian"], + "慴" => &["zhe"], + "慵" => &["yong"], + "慶" => &["qing"], + "慷" => &["kang"], + "慸" => &["di"], + "慹" => &["zhi"], + "慺" => &["lu:"], + "慻" => &["juan"], + "慼" => &["qi"], + "慽" => &["qi"], + "慾" => &["yu"], + "慿" => &["ping"], + "憀" => &["liao"], + "憁" => &["zong"], + "憂" => &["you"], + "憃" => &["chuang"], + "憄" => &["zhi"], + "憅" => &["tong"], + "憆" => &["cheng"], + "憇" => &["qi"], + "憈" => &["qu"], + "憉" => &["peng"], + "憊" => &["bei"], + "憋" => &["bie"], + "憌" => &["chun"], + "憍" => &["jiao"], + "憎" => &["zeng"], + "憏" => &["chi"], + "憐" => &["lian"], + "憑" => &["ping"], + "憒" => &["kui"], + "憓" => &["hui"], + "憔" => &["qiao"], + "憕" => &["cheng"], + "憖" => &["yin"], + "憗" => &["yin"], + "憘" => &["xi"], + "憙" => &["xi"], + "憚" => &["dan"], + "憛" => &["tan"], + "憜" => &["duo"], + "憝" => &["dui"], + "憞" => &["dui"], + "憟" => &["su"], + "憠" => &["jue"], + "憡" => &["ce"], + "憢" => &["xiao"], + "憣" => &["fan"], + "憤" => &["fen"], + "憥" => &["lao"], + "憦" => &["lao"], + "憧" => &["chong"], + "憨" => &["han"], + "憩" => &["qi"], + "憪" => &["xian"], + "憫" => &["min"], + "憬" => &["jing"], + "憭" => &["liao"], + "憮" => &["wu"], + "憯" => &["can"], + "憰" => &["jue"], + "憱" => &["chou"], + "憲" => &["xian"], + "憳" => &["tan"], + "憴" => &["sheng"], + "憵" => &["pi"], + "憶" => &["yi"], + "憷" => &["chu"], + "憸" => &["xian"], + "憹" => &["nao"], + "憺" => &["dan"], + "憻" => &["tan"], + "憼" => &["jing"], + "憽" => &["song"], + "憾" => &["han"], + "憿" => &["jiao"], + "懀" => &["wei"], + "懁" => &["huan"], + "懂" => &["dong"], + "懃" => &["qin"], + "懄" => &["qin"], + "懅" => &["qu"], + "懆" => &["cao"], + "懇" => &["ken"], + "懈" => &["xie"], + "應" => &["ying"], + "懊" => &["ao"], + "懋" => &["mao"], + "懌" => &["yi"], + "懍" => &["lin"], + "懎" => &["se"], + "懏" => &["jun"], + "懐" => &["huai"], + "懑" => &["men"], + "懒" => &["lan"], + "懓" => &["ai"], + "懔" => &["lin"], + "懕" => &["yan"], + "懖" => &["gua"], + "懗" => &["xia"], + "懘" => &["chi"], + "懙" => &["yu"], + "懚" => &["yin"], + "懛" => &["dai"], + "懜" => &["meng"], + "懝" => &["ai"], + "懞" => &["meng"], + "懟" => &["dui"], + "懠" => &["qi"], + "懡" => &["mo"], + "懢" => &["lan"], + "懣" => &["men"], + "懤" => &["chou"], + "懥" => &["zhi"], + "懦" => &["nuo"], + "懧" => &["nuo"], + "懨" => &["yan"], + "懩" => &["yang"], + "懪" => &["bo"], + "懫" => &["zhi"], + "懬" => &["xing"], + "懭" => &["kuang"], + "懮" => &["you"], + "懯" => &["fu"], + "懰" => &["liu"], + "懱" => &["mie"], + "懲" => &["cheng"], + "懴" => &["chan"], + "懵" => &["meng"], + "懶" => &["lan"], + "懷" => &["huai"], + "懸" => &["xuan"], + "懹" => &["rang"], + "懺" => &["chan"], + "懻" => &["ji"], + "懼" => &["ju"], + "懽" => &["huan"], + "懾" => &["she","zhe"], + "懿" => &["yi"], + "戀" => &["lian"], + "戁" => &["nan"], + "戂" => &["mi"], + "戃" => &["tang"], + "戄" => &["jue"], + "戅" => &["gang"], + "戆" => &["gang","zhuang"], + "戇" => &["zhuang"], + "戈" => &["ge"], + "戉" => &["yue"], + "戊" => &["wu"], + "戋" => &["jian"], + "戌" => &["xu","qu"], + "戍" => &["shu"], + "戎" => &["rong"], + "戏" => &["xi","hu"], + "成" => &["cheng"], + "我" => &["wo"], + "戒" => &["jie"], + "戓" => &["ge"], + "戔" => &["jian"], + "戕" => &["qiang"], + "或" => &["huo"], + "戗" => &["qiang"], + "战" => &["zhan"], + "戙" => &["dong"], + "戚" => &["qi"], + "戛" => &["jia"], + "戜" => &["die"], + "戝" => &["cai"], + "戞" => &["jia"], + "戟" => &["ji"], + "戠" => &["shi","chi"], + "戡" => &["kan"], + "戢" => &["ji"], + "戣" => &["kui"], + "戤" => &["gai"], + "戥" => &["deng"], + "戦" => &["zhan"], + "戧" => &["chuang","qiang"], + "戨" => &["ge"], + "戩" => &["jian"], + "截" => &["jie"], + "戫" => &["yu"], + "戬" => &["jian"], + "戭" => &["yan"], + "戮" => &["lu"], + "戯" => &["xi","hu"], + "戰" => &["zhan"], + "戱" => &["xi"], + "戲" => &["xi","hu"], + "戳" => &["chuo"], + "戴" => &["dai"], + "戵" => &["qu"], + "戶" => &["hu"], + "户" => &["hu"], + "戸" => &["hu"], + "戹" => &["e"], + "戺" => &["shi"], + "戻" => &["li"], + "戼" => &["mao"], + "戽" => &["hu"], + "戾" => &["li"], + "房" => &["fang"], + "所" => &["suo"], + "扁" => &["bian","pian"], + "扂" => &["dian"], + "扃" => &["jiong"], + "扄" => &["shang"], + "扅" => &["yi"], + "扆" => &["yi"], + "扇" => &["shan"], + "扈" => &["hu"], + "扉" => &["fei"], + "扊" => &["yan"], + "手" => &["shou"], + "扌" => &["shou"], + "才" => &["cai"], + "扎" => &["zha","za"], + "扏" => &["qiu"], + "扐" => &["le"], + "扑" => &["pu"], + "扒" => &["ba","pa"], + "打" => &["da"], + "扔" => &["reng"], + "払" => &["fu","bi"], + "扗" => &["zai"], + "托" => &["tuo"], + "扙" => &["zhang"], + "扚" => &["diao"], + "扛" => &["kang","gang"], + "扜" => &["yu"], + "扝" => &["ku"], + "扞" => &["han"], + "扟" => &["shen"], + "扠" => &["cha"], + "扡" => &["chi"], + "扢" => &["gu"], + "扣" => &["kou"], + "扤" => &["wu"], + "扥" => &["tuo"], + "扦" => &["qian"], + "执" => &["zhi"], + "扨" => &["cha"], + "扩" => &["kuo"], + "扪" => &["men"], + "扫" => &["sao"], + "扬" => &["yang"], + "扭" => &["niu"], + "扮" => &["ban"], + "扯" => &["che"], + "扰" => &["rao"], + "扱" => &["xi"], + "扲" => &["qian"], + "扳" => &["ban","pan"], + "扴" => &["jia"], + "扵" => &["yu"], + "扶" => &["fu"], + "扷" => &["ao"], + "扸" => &["xi"], + "批" => &["pi"], + "扺" => &["zhi"], + "扻" => &["zi"], + "扼" => &["e"], + "扽" => &["dun"], + "找" => &["zhao"], + "承" => &["cheng"], + "技" => &["ji"], + "抁" => &["yan"], + "抂" => &["kuang"], + "抃" => &["bian"], + "抄" => &["chao"], + "抅" => &["ju"], + "抆" => &["wen"], + "抇" => &["hu"], + "抈" => &["yue"], + "抉" => &["jue"], + "把" => &["ba"], + "抋" => &["qin"], + "抌" => &["zhen"], + "抍" => &["zheng"], + "抎" => &["yun"], + "抏" => &["wan"], + "抐" => &["na"], + "抑" => &["yi"], + "抒" => &["shu"], + "抓" => &["zhua"], + "抔" => &["pou"], + "投" => &["tou"], + "抖" => &["dou"], + "抗" => &["kang"], + "折" => &["zhe","she"], + "抙" => &["pou"], + "抚" => &["fu"], + "抛" => &["pao"], + "抜" => &["ba"], + "抝" => &["ao"], + "択" => &["ze","zhai"], + "抟" => &["tuan"], + "抠" => &["kou"], + "抡" => &["lun"], + "抢" => &["qiang"], + "护" => &["hu"], + "报" => &["bao"], + "抦" => &["bing"], + "抧" => &["zhi"], + "抨" => &["peng"], + "抩" => &["tan"], + "抪" => &["pu"], + "披" => &["pi"], + "抬" => &["tai"], + "抭" => &["yao"], + "抮" => &["zhen"], + "抯" => &["zha"], + "抰" => &["yang"], + "抱" => &["bao"], + "抲" => &["he"], + "抳" => &["ni"], + "抴" => &["yi"], + "抵" => &["di"], + "抶" => &["chi"], + "抷" => &["pi"], + "抸" => &["za"], + "抹" => &["mo","ma"], + "抺" => &["mo"], + "抻" => &["chen","shen"], + "押" => &["ya"], + "抽" => &["chou"], + "抾" => &["qu"], + "抿" => &["min"], + "拀" => &["chu"], + "拁" => &["jia"], + "拂" => &["fu","bi"], + "拃" => &["zha"], + "拄" => &["zhu"], + "担" => &["dan"], + "拆" => &["chai","ca"], + "拇" => &["mu"], + "拈" => &["nian"], + "拉" => &["la"], + "拊" => &["fu"], + "拋" => &["pao"], + "拌" => &["ban"], + "拍" => &["pai"], + "拎" => &["lin"], + "拏" => &["na"], + "拐" => &["guai"], + "拑" => &["qian"], + "拒" => &["ju"], + "拓" => &["tuo","ta"], + "拔" => &["ba"], + "拕" => &["tuo"], + "拖" => &["tuo"], + "拗" => &["ao","niu","yao"], + "拘" => &["ju"], + "拙" => &["zhuo"], + "拚" => &["pan","pin"], + "招" => &["zhao"], + "拜" => &["bai"], + "拝" => &["bai"], + "拞" => &["di"], + "拟" => &["ni"], + "拠" => &["ju"], + "拡" => &["kuo"], + "拢" => &["long"], + "拣" => &["jian"], + "拤" => &["qia"], + "拥" => &["yong"], + "拦" => &["lan"], + "拧" => &["ning"], + "拨" => &["bo"], + "择" => &["ze","zhai"], + "拪" => &["qian"], + "拫" => &["hen"], + "括" => &["kuo","gua"], + "拭" => &["shi"], + "拮" => &["jie"], + "拯" => &["zheng"], + "拰" => &["nin"], + "拱" => &["gong"], + "拲" => &["gong"], + "拳" => &["quan"], + "拴" => &["shuan"], + "拵" => &["tun"], + "拶" => &["zan","za"], + "拷" => &["kao"], + "拸" => &["chi"], + "拹" => &["xie"], + "拺" => &["ce"], + "拻" => &["hui"], + "拼" => &["pin"], + "拽" => &["zhuai","ye"], + "拾" => &["shi","she"], + "拿" => &["na"], + "挀" => &["bo"], + "持" => &["chi"], + "挂" => &["gua"], + "挃" => &["zhi"], + "挄" => &["kuo"], + "挅" => &["duo"], + "挆" => &["duo"], + "指" => &["zhi"], + "挈" => &["qie"], + "按" => &["an"], + "挊" => &["nong","long"], + "挋" => &["zhen"], + "挌" => &["ge"], + "挍" => &["jiao"], + "挎" => &["kua"], + "挏" => &["dong"], + "挐" => &["ru","na"], + "挑" => &["tiao"], + "挒" => &["lie"], + "挓" => &["zha"], + "挔" => &["lu:"], + "挕" => &["die"], + "挖" => &["wa"], + "挗" => &["jue"], + "挙" => &["ju"], + "挚" => &["zhi"], + "挛" => &["luan"], + "挜" => &["ya"], + "挝" => &["wo","zhua"], + "挞" => &["ta"], + "挟" => &["xie","jia"], + "挠" => &["nao"], + "挡" => &["dang"], + "挢" => &["jiao","jia"], + "挣" => &["zheng"], + "挤" => &["ji"], + "挥" => &["hui"], + "挦" => &["xian"], + "挨" => &["ai"], + "挩" => &["tuo"], + "挪" => &["nuo"], + "挫" => &["cuo"], + "挬" => &["bo"], + "挭" => &["geng"], + "挮" => &["ti"], + "振" => &["zhen"], + "挰" => &["cheng"], + "挱" => &["suo"], + "挲" => &["suo","sa","sha"], + "挳" => &["keng"], + "挴" => &["mei"], + "挵" => &["long","nong"], + "挶" => &["ju"], + "挷" => &["peng"], + "挸" => &["jian"], + "挹" => &["yi"], + "挺" => &["ting"], + "挻" => &["shan"], + "挼" => &["nuo"], + "挽" => &["wan"], + "挾" => &["xie","jia","xia"], + "挿" => &["cha"], + "捀" => &["feng"], + "捁" => &["jiao","jia"], + "捂" => &["wu"], + "捃" => &["jun"], + "捄" => &["jiu"], + "捅" => &["tong"], + "捆" => &["kun"], + "捇" => &["huo"], + "捈" => &["tu"], + "捉" => &["zhuo"], + "捊" => &["pou"], + "捋" => &["lu:","luo"], + "捌" => &["ba"], + "捍" => &["han"], + "捎" => &["shao"], + "捏" => &["nie"], + "捐" => &["juan"], + "捑" => &["she"], + "捒" => &["shu"], + "捓" => &["ye"], + "捔" => &["jue"], + "捕" => &["bu"], + "捖" => &["huan"], + "捗" => &["bu"], + "捘" => &["jun"], + "捙" => &["yi"], + "捚" => &["zhai"], + "捛" => &["lu:"], + "捜" => &["sou"], + "捝" => &["tuo"], + "捞" => &["lao"], + "损" => &["sun"], + "捠" => &["bang"], + "捡" => &["jian"], + "换" => &["huan"], + "捣" => &["dao"], + "捥" => &["wan"], + "捦" => &["qin"], + "捧" => &["peng"], + "捨" => &["she"], + "捩" => &["lie"], + "捪" => &["min"], + "捫" => &["men"], + "捬" => &["fu"], + "捭" => &["bai"], + "据" => &["ju"], + "捯" => &["dao"], + "捰" => &["wo"], + "捱" => &["ai"], + "捲" => &["juan"], + "捳" => &["yue"], + "捴" => &["zong"], + "捵" => &["chen"], + "捶" => &["chui"], + "捷" => &["jie"], + "捸" => &["tu"], + "捹" => &["ben"], + "捺" => &["na"], + "捻" => &["nian"], + "捼" => &["nuo"], + "捽" => &["zu"], + "捾" => &["wo"], + "捿" => &["xi","qi"], + "掀" => &["xian"], + "掁" => &["cheng"], + "掂" => &["dian"], + "掃" => &["sao"], + "掄" => &["lun"], + "掅" => &["qing"], + "掆" => &["gang"], + "掇" => &["duo"], + "授" => &["shou"], + "掉" => &["diao"], + "掊" => &["pou"], + "掋" => &["di"], + "掌" => &["zhang"], + "掍" => &["gun"], + "掎" => &["ji"], + "掏" => &["tao"], + "掐" => &["qia"], + "掑" => &["qi"], + "排" => &["pai"], + "掓" => &["shu"], + "掔" => &["qian"], + "掕" => &["ling"], + "掖" => &["ye","yi"], + "掗" => &["ya"], + "掘" => &["jue"], + "掙" => &["zheng"], + "掚" => &["liang"], + "掛" => &["gua"], + "掜" => &["yi"], + "掝" => &["huo"], + "掞" => &["shan"], + "掟" => &["ding"], + "掠" => &["lu:e"], + "採" => &["cai"], + "探" => &["tan"], + "掣" => &["che"], + "掤" => &["bing"], + "接" => &["jie"], + "掦" => &["ti"], + "控" => &["kong"], + "推" => &["tui"], + "掩" => &["yan"], + "措" => &["cuo"], + "掫" => &["zou"], + "掬" => &["ju"], + "掭" => &["tian"], + "掮" => &["qian"], + "掯" => &["ken"], + "掰" => &["bai","bo"], + "掱" => &["shou","pa"], + "掲" => &["jie"], + "掳" => &["lu"], + "掴" => &["guai","guo"], + "掷" => &["zhi"], + "掸" => &["dan","shan"], + "掺" => &["chan","shan","can"], + "掻" => &["sao"], + "掼" => &["guan"], + "掽" => &["peng"], + "掾" => &["yuan"], + "掿" => &["nuo"], + "揀" => &["jian"], + "揁" => &["zheng"], + "揂" => &["jiu"], + "揃" => &["jian"], + "揄" => &["yu"], + "揅" => &["yan"], + "揆" => &["kui"], + "揇" => &["nan"], + "揈" => &["hong"], + "揉" => &["rou"], + "揊" => &["pi"], + "揋" => &["wei"], + "揌" => &["sai"], + "揍" => &["zou"], + "揎" => &["xuan"], + "描" => &["miao"], + "提" => &["ti","di","shi"], + "揑" => &["nie"], + "插" => &["cha"], + "揓" => &["shi"], + "揔" => &["zong"], + "揕" => &["zhen"], + "揖" => &["yi"], + "揗" => &["shun"], + "揘" => &["heng"], + "揙" => &["bian"], + "揚" => &["yang"], + "換" => &["huan"], + "揜" => &["yan"], + "揝" => &["zan"], + "揞" => &["an"], + "揟" => &["xu","ju"], + "揠" => &["ya"], + "握" => &["wo"], + "揢" => &["ke"], + "揣" => &["chuai"], + "揤" => &["ji","jie"], + "揥" => &["ti"], + "揦" => &["la"], + "揧" => &["la"], + "揨" => &["cheng"], + "揩" => &["kai"], + "揪" => &["jiu"], + "揫" => &["jiu"], + "揬" => &["tu"], + "揭" => &["jie"], + "揮" => &["hui"], + "揯" => &["geng"], + "揰" => &["chong"], + "揱" => &["shuo"], + "揲" => &["she","die"], + "揳" => &["xie"], + "援" => &["yuan"], + "揵" => &["qian"], + "揶" => &["ye"], + "揷" => &["cha"], + "揸" => &["zha"], + "揹" => &["bei"], + "揺" => &["yao"], + "揽" => &["lan"], + "揾" => &["wen"], + "揿" => &["qin"], + "搀" => &["chan"], + "搁" => &["ge"], + "搂" => &["lou"], + "搃" => &["zong"], + "搄" => &["geng"], + "搅" => &["jiao","jia"], + "搆" => &["gou"], + "搇" => &["qin"], + "搈" => &["yong"], + "搉" => &["que"], + "搊" => &["chou"], + "搋" => &["chuai"], + "搌" => &["zhan"], + "損" => &["sun"], + "搎" => &["sun"], + "搏" => &["bo"], + "搐" => &["chu"], + "搑" => &["rong"], + "搒" => &["bang","peng"], + "搓" => &["cuo"], + "搔" => &["sao"], + "搕" => &["ke"], + "搖" => &["yao"], + "搗" => &["dao"], + "搘" => &["zhi"], + "搙" => &["nu"], + "搚" => &["xie"], + "搛" => &["jian"], + "搜" => &["sou"], + "搝" => &["qiu"], + "搞" => &["gao"], + "搟" => &["xian"], + "搠" => &["shuo"], + "搡" => &["sang"], + "搢" => &["jin"], + "搣" => &["mie"], + "搤" => &["e"], + "搥" => &["chui"], + "搦" => &["nuo"], + "搧" => &["shan"], + "搨" => &["ta"], + "搩" => &["jie"], + "搪" => &["tang"], + "搫" => &["pan"], + "搬" => &["ban"], + "搭" => &["da"], + "搮" => &["li"], + "搯" => &["tao"], + "搰" => &["hu"], + "搱" => &["zhi"], + "搲" => &["wa"], + "搳" => &["xia"], + "搴" => &["qian"], + "搵" => &["wen"], + "搶" => &["qiang","chuang"], + "搷" => &["chen"], + "搸" => &["zhen"], + "搹" => &["e"], + "携" => &["xie"], + "搻" => &["nuo"], + "搼" => &["quan"], + "搽" => &["cha"], + "搾" => &["zha"], + "搿" => &["ge"], + "摀" => &["wu"], + "摁" => &["en"], + "摂" => &["she"], + "摃" => &["gong"], + "摄" => &["she"], + "摅" => &["shu"], + "摆" => &["bai"], + "摇" => &["yao"], + "摈" => &["bin"], + "摉" => &["sou"], + "摊" => &["tan"], + "摋" => &["sha"], + "摌" => &["chan"], + "摍" => &["suo"], + "摎" => &["liao"], + "摏" => &["chong"], + "摐" => &["chuang"], + "摑" => &["guo","guai"], + "摒" => &["bing"], + "摓" => &["feng"], + "摔" => &["shuai"], + "摕" => &["di"], + "摖" => &["qi"], + "摘" => &["zhai","zhe"], + "摙" => &["lian"], + "摚" => &["cheng"], + "摛" => &["chi"], + "摜" => &["guan"], + "摝" => &["lu"], + "摞" => &["luo"], + "摟" => &["lou"], + "摠" => &["zong"], + "摡" => &["gai","xi"], + "摢" => &["hu"], + "摣" => &["zha"], + "摤" => &["chuang"], + "摥" => &["tang"], + "摦" => &["hua"], + "摧" => &["cui"], + "摨" => &["nai"], + "摩" => &["mo","ma"], + "摪" => &["jiang"], + "摫" => &["gui"], + "摬" => &["ying"], + "摭" => &["zhi"], + "摮" => &["ao"], + "摯" => &["zhi"], + "摰" => &["chi"], + "摱" => &["man"], + "摲" => &["shan"], + "摳" => &["kou"], + "摴" => &["shu"], + "摵" => &["suo"], + "摶" => &["tuan"], + "摷" => &["zhao"], + "摸" => &["mo"], + "摹" => &["mo"], + "摺" => &["zhe"], + "摻" => &["chan","shan"], + "摼" => &["keng"], + "摽" => &["biao"], + "摾" => &["jiang"], + "摿" => &["yin"], + "撀" => &["gou"], + "撁" => &["qian"], + "撂" => &["liao"], + "撃" => &["ji"], + "撄" => &["ying"], + "撅" => &["jue"], + "撆" => &["pie"], + "撇" => &["pie"], + "撈" => &["lao"], + "撉" => &["dun"], + "撊" => &["xian"], + "撋" => &["ruan"], + "撌" => &["kui"], + "撍" => &["zan"], + "撎" => &["yi"], + "撏" => &["xian"], + "撐" => &["cheng"], + "撑" => &["cheng"], + "撒" => &["sa"], + "撓" => &["nao"], + "撔" => &["heng"], + "撕" => &["si"], + "撖" => &["han"], + "撗" => &["huang"], + "撘" => &["da"], + "撙" => &["zun"], + "撚" => &["nian"], + "撛" => &["lin"], + "撜" => &["zheng"], + "撝" => &["hui"], + "撞" => &["zhuang","chuang"], + "撟" => &["jiao","jia"], + "撠" => &["ji"], + "撡" => &["cao"], + "撢" => &["dan"], + "撣" => &["dan","shan"], + "撤" => &["che"], + "撥" => &["bo"], + "撦" => &["che"], + "撧" => &["jue"], + "撨" => &["xiao"], + "撩" => &["liao"], + "撪" => &["ben"], + "撫" => &["fu"], + "撬" => &["qiao"], + "播" => &["bo"], + "撮" => &["cuo","zuo"], + "撯" => &["zhuo"], + "撰" => &["zhuan"], + "撱" => &["tuo"], + "撲" => &["pu"], + "撳" => &["qin"], + "撴" => &["dun"], + "撵" => &["nian"], + "撷" => &["xie"], + "撸" => &["lu"], + "撹" => &["jiao","jia"], + "撺" => &["cuan"], + "撻" => &["ta"], + "撼" => &["han"], + "撽" => &["qiao"], + "撾" => &["zhua","wo"], + "撿" => &["jian"], + "擀" => &["gan"], + "擁" => &["yong"], + "擂" => &["lei"], + "擃" => &["kuo"], + "擄" => &["lu"], + "擅" => &["shan"], + "擆" => &["zhuo"], + "擇" => &["ze","zhai"], + "擈" => &["pu"], + "擉" => &["chuo"], + "擊" => &["ji"], + "擋" => &["dang"], + "擌" => &["se"], + "操" => &["cao"], + "擎" => &["qing"], + "擏" => &["jing"], + "擐" => &["huan"], + "擑" => &["jie"], + "擒" => &["qin"], + "擓" => &["kuai"], + "擔" => &["dan"], + "擕" => &["xie"], + "擖" => &["ge"], + "擗" => &["pi"], + "擘" => &["bo","bai"], + "擙" => &["ao"], + "據" => &["ju"], + "擛" => &["ye"], + "擞" => &["sou"], + "擟" => &["mi"], + "擠" => &["ji"], + "擡" => &["tai"], + "擢" => &["zhuo"], + "擣" => &["dao"], + "擤" => &["xing"], + "擥" => &["lan"], + "擦" => &["ca"], + "擧" => &["ju"], + "擨" => &["ye"], + "擩" => &["ru"], + "擪" => &["ye"], + "擫" => &["ye"], + "擬" => &["ni"], + "擭" => &["huo"], + "擮" => &["ji"], + "擯" => &["bin"], + "擰" => &["ning"], + "擱" => &["ge"], + "擲" => &["zhi"], + "擳" => &["jie"], + "擴" => &["kuo"], + "擵" => &["mo","ma"], + "擶" => &["jian"], + "擷" => &["xie"], + "擸" => &["lie"], + "擹" => &["tan"], + "擺" => &["bai"], + "擻" => &["sou"], + "擼" => &["lu"], + "擽" => &["lu:e"], + "擾" => &["rao"], + "擿" => &["zhi"], + "攀" => &["pan"], + "攁" => &["yang"], + "攂" => &["lei"], + "攃" => &["sa"], + "攄" => &["shu"], + "攅" => &["zan","cuan"], + "攆" => &["nian"], + "攇" => &["xian"], + "攈" => &["jun"], + "攉" => &["huo"], + "攊" => &["lu:e"], + "攋" => &["la"], + "攌" => &["han"], + "攍" => &["ying"], + "攎" => &["lu"], + "攏" => &["long"], + "攐" => &["qian"], + "攑" => &["qian"], + "攒" => &["zan","cuan"], + "攓" => &["qian"], + "攔" => &["lan"], + "攕" => &["san"], + "攖" => &["ying"], + "攗" => &["mei"], + "攘" => &["rang"], + "攙" => &["chan"], + "攛" => &["cuan"], + "攜" => &["xie","xi"], + "攝" => &["she"], + "攞" => &["luo"], + "攟" => &["jun"], + "攠" => &["mi"], + "攡" => &["li"], + "攢" => &["zan","cuan"], + "攣" => &["luan"], + "攤" => &["tan"], + "攥" => &["zuan"], + "攦" => &["li"], + "攧" => &["dian"], + "攨" => &["wa"], + "攩" => &["dang"], + "攪" => &["jiao","gao","jia"], + "攫" => &["jue"], + "攬" => &["lan"], + "攭" => &["li"], + "攮" => &["nang"], + "支" => &["zhi"], + "攰" => &["gui"], + "攱" => &["gui"], + "攲" => &["qi"], + "攳" => &["xin"], + "攴" => &["po"], + "攵" => &["po"], + "收" => &["shou"], + "攷" => &["kao"], + "攸" => &["you"], + "改" => &["gai"], + "攺" => &["gai"], + "攻" => &["gong"], + "攼" => &["gan"], + "攽" => &["ban"], + "放" => &["fang"], + "政" => &["zheng"], + "敀" => &["bo"], + "敁" => &["dian"], + "敂" => &["kou"], + "敃" => &["min"], + "敄" => &["wu"], + "故" => &["gu"], + "敆" => &["ge"], + "敇" => &["ce"], + "效" => &["xiao"], + "敉" => &["mi"], + "敊" => &["chu"], + "敋" => &["ge"], + "敌" => &["di"], + "敍" => &["xu"], + "敎" => &["jiao"], + "敏" => &["min"], + "敐" => &["chen"], + "救" => &["jiu"], + "敒" => &["shen"], + "敓" => &["duo"], + "敔" => &["yu"], + "敕" => &["chi"], + "敖" => &["ao"], + "敗" => &["bai"], + "敘" => &["xu"], + "教" => &["jiao"], + "敚" => &["duo"], + "敛" => &["lian"], + "敜" => &["nie"], + "敝" => &["bi"], + "敞" => &["chang","tang"], + "敟" => &["dian"], + "敠" => &["duo"], + "敡" => &["yi"], + "敢" => &["gan"], + "散" => &["san"], + "敤" => &["ke"], + "敥" => &["yan"], + "敦" => &["dun","dui"], + "敧" => &["qi"], + "敨" => &["dou"], + "敩" => &["xiao"], + "敪" => &["duo"], + "敫" => &["jiao","jia"], + "敬" => &["jing"], + "敭" => &["yang"], + "敮" => &["xia"], + "敯" => &["hun","min"], + "数" => &["shu","shuo"], + "敱" => &["ai"], + "敲" => &["qiao"], + "敳" => &["ai"], + "整" => &["zheng"], + "敵" => &["di"], + "敶" => &["zhen"], + "敷" => &["fu"], + "數" => &["shu","shuo"], + "敹" => &["liao"], + "敺" => &["qu"], + "敻" => &["xiong"], + "敼" => &["xi"], + "敽" => &["jiao"], + "敿" => &["qiao"], + "斀" => &["zhuo"], + "斁" => &["yi","du"], + "斂" => &["lian"], + "斃" => &["bi"], + "斄" => &["li"], + "斅" => &["xue"], + "斆" => &["xiao"], + "文" => &["wen"], + "斈" => &["xue"], + "斉" => &["qi","ji"], + "斊" => &["qi","ji"], + "斋" => &["zhai"], + "斌" => &["bin"], + "斍" => &["jue"], + "斎" => &["zhai"], + "斏" => &["lang"], + "斐" => &["fei"], + "斑" => &["ban"], + "斒" => &["ban"], + "斓" => &["lan"], + "斔" => &["yu"], + "斕" => &["lan"], + "斖" => &["wei"], + "斗" => &["dou"], + "斘" => &["sheng"], + "料" => &["liao"], + "斚" => &["jia"], + "斛" => &["hu"], + "斜" => &["xie","xia"], + "斝" => &["jia"], + "斞" => &["yu"], + "斟" => &["zhen"], + "斠" => &["jiao"], + "斡" => &["wo"], + "斢" => &["tiao","tou"], + "斣" => &["dou"], + "斤" => &["jin"], + "斥" => &["chi"], + "斦" => &["yin"], + "斧" => &["fu"], + "斨" => &["qiang"], + "斩" => &["zhan"], + "斪" => &["qu","ju"], + "斫" => &["zhuo"], + "斬" => &["zhan"], + "断" => &["duan"], + "斮" => &["zhuo"], + "斯" => &["si"], + "新" => &["xin"], + "斱" => &["zhuo"], + "斲" => &["zhuo"], + "斳" => &["qin"], + "斴" => &["lin"], + "斵" => &["zhuo"], + "斶" => &["chu"], + "斷" => &["duan"], + "斸" => &["zhu"], + "方" => &["fang"], + "斺" => &["xie"], + "斻" => &["hang"], + "於" => &["wu","yu"], + "施" => &["shi"], + "斾" => &["pei"], + "斿" => &["you"], + "旁" => &["pang","bang"], + "旂" => &["qi"], + "旃" => &["zhan"], + "旄" => &["mao"], + "旅" => &["lu:"], + "旆" => &["pei"], + "旇" => &["pi"], + "旈" => &["liu"], + "旉" => &["fu"], + "旊" => &["fang"], + "旋" => &["xuan"], + "旌" => &["jing"], + "旍" => &["jing"], + "旎" => &["ni"], + "族" => &["zu"], + "旐" => &["zhao"], + "旑" => &["yi"], + "旒" => &["liu"], + "旓" => &["shao"], + "旔" => &["jian"], + "旖" => &["yi"], + "旗" => &["qi"], + "旘" => &["zhi"], + "旙" => &["fan"], + "旚" => &["piao"], + "旛" => &["fan"], + "旜" => &["zhan"], + "旝" => &["guai"], + "旞" => &["sui"], + "旟" => &["yu"], + "无" => &["wu","mo"], + "旡" => &["ji"], + "既" => &["ji"], + "旣" => &["ji"], + "旤" => &["huo"], + "日" => &["ri"], + "旦" => &["dan"], + "旧" => &["jiu"], + "旨" => &["zhi"], + "早" => &["zao"], + "旪" => &["xie"], + "旫" => &["tiao"], + "旬" => &["xun"], + "旭" => &["xu"], + "旮" => &["ga"], + "旯" => &["la"], + "旰" => &["gan"], + "旱" => &["han"], + "旲" => &["tai"], + "旳" => &["di"], + "旴" => &["xu"], + "旵" => &["chan"], + "时" => &["shi"], + "旷" => &["kuang"], + "旸" => &["yang"], + "旹" => &["shi"], + "旺" => &["wang"], + "旻" => &["min"], + "旼" => &["min"], + "旽" => &["tun"], + "旾" => &["chun"], + "旿" => &["wu"], + "昀" => &["yun"], + "昁" => &["bei"], + "昂" => &["ang"], + "昃" => &["ze"], + "昄" => &["ban"], + "昅" => &["jie"], + "昆" => &["kun"], + "昇" => &["sheng"], + "昈" => &["hu"], + "昉" => &["fang"], + "昊" => &["hao"], + "昋" => &["gui"], + "昌" => &["chang"], + "昍" => &["xuan"], + "明" => &["ming"], + "昏" => &["hun"], + "昐" => &["fen"], + "昑" => &["qin"], + "昒" => &["hu"], + "易" => &["yi"], + "昔" => &["xi"], + "昕" => &["xin"], + "昖" => &["yan"], + "昗" => &["ze"], + "昘" => &["fang"], + "昙" => &["tan"], + "昚" => &["shen"], + "昛" => &["ju"], + "昜" => &["yang"], + "昝" => &["zan"], + "昞" => &["bing"], + "星" => &["xing"], + "映" => &["ying"], + "昡" => &["xuan"], + "昢" => &["pei"], + "昣" => &["zhen"], + "昤" => &["ling"], + "春" => &["chun"], + "昦" => &["hao"], + "昧" => &["mei"], + "昨" => &["zuo"], + "昩" => &["mo"], + "昪" => &["bian"], + "昫" => &["xu"], + "昬" => &["hun"], + "昭" => &["zhao"], + "昮" => &["zong"], + "是" => &["shi"], + "昰" => &["shi"], + "昱" => &["yu"], + "昲" => &["fei","fu"], + "昳" => &["die"], + "昴" => &["mao"], + "昵" => &["ni"], + "昶" => &["chang"], + "昷" => &["wen"], + "昸" => &["dong"], + "昹" => &["ai"], + "昺" => &["bing"], + "昻" => &["ang"], + "昼" => &["zhou"], + "昽" => &["long"], + "显" => &["xian"], + "昿" => &["kuang"], + "晀" => &["tiao"], + "晁" => &["chao","zhao"], + "時" => &["shi"], + "晃" => &["huang"], + "晄" => &["huang"], + "晅" => &["xuan"], + "晆" => &["kui"], + "晇" => &["xu","kua"], + "晈" => &["jiao"], + "晉" => &["jin"], + "晊" => &["zhi"], + "晋" => &["jin"], + "晌" => &["shang"], + "晍" => &["tong"], + "晎" => &["hong"], + "晏" => &["yan"], + "晐" => &["gai"], + "晑" => &["xiang"], + "晒" => &["shai"], + "晓" => &["xiao"], + "晔" => &["ye"], + "晕" => &["yun"], + "晖" => &["hui"], + "晗" => &["han"], + "晘" => &["han"], + "晙" => &["jun"], + "晚" => &["wan"], + "晛" => &["xian"], + "晜" => &["kun"], + "晝" => &["zhou"], + "晞" => &["xi"], + "晟" => &["sheng","cheng"], + "晠" => &["sheng"], + "晡" => &["bu"], + "晢" => &["zhe"], + "晣" => &["zhe"], + "晤" => &["wu"], + "晥" => &["han"], + "晦" => &["hui"], + "晧" => &["hao"], + "晨" => &["chen"], + "晩" => &["wan"], + "晪" => &["tian"], + "晫" => &["zhuo"], + "晬" => &["zui"], + "晭" => &["zhou"], + "普" => &["pu"], + "景" => &["jing","ying"], + "晰" => &["xi"], + "晱" => &["shan"], + "晲" => &["yi"], + "晳" => &["xi"], + "晴" => &["qing"], + "晵" => &["qi"], + "晶" => &["jing"], + "晷" => &["gui"], + "晸" => &["zhen"], + "晹" => &["yi"], + "智" => &["zhi"], + "晻" => &["an"], + "晼" => &["wan"], + "晽" => &["lin"], + "晾" => &["liang"], + "晿" => &["chang"], + "暀" => &["wang"], + "暁" => &["xiao"], + "暂" => &["zan"], + "暄" => &["xuan"], + "暅" => &["geng"], + "暆" => &["yi"], + "暇" => &["xia"], + "暈" => &["yun"], + "暉" => &["hui"], + "暊" => &["fu"], + "暋" => &["min"], + "暌" => &["kui"], + "暍" => &["he"], + "暎" => &["ying"], + "暏" => &["du"], + "暐" => &["wei"], + "暑" => &["shu"], + "暒" => &["qing"], + "暓" => &["mao"], + "暔" => &["nan"], + "暕" => &["jian"], + "暖" => &["nuan"], + "暗" => &["an"], + "暘" => &["yang"], + "暙" => &["chun"], + "暚" => &["yao"], + "暛" => &["suo"], + "暜" => &["pu"], + "暝" => &["ming"], + "暞" => &["jiao"], + "暟" => &["kai"], + "暠" => &["gao"], + "暡" => &["weng"], + "暢" => &["chang"], + "暣" => &["qi"], + "暤" => &["hao"], + "暥" => &["yan"], + "暦" => &["li"], + "暧" => &["ai"], + "暨" => &["ji"], + "暩" => &["gui"], + "暪" => &["men"], + "暫" => &["zan","zhan"], + "暬" => &["xie"], + "暭" => &["hao"], + "暮" => &["mu"], + "暯" => &["mo"], + "暰" => &["cong"], + "暱" => &["ni"], + "暲" => &["zhang"], + "暳" => &["hui"], + "暴" => &["bao","pu"], + "暵" => &["han"], + "暶" => &["xuan"], + "暷" => &["chuan"], + "暸" => &["liao"], + "暹" => &["xian"], + "暺" => &["dan"], + "暻" => &["jing"], + "暼" => &["pie"], + "暽" => &["lin"], + "暾" => &["tun"], + "暿" => &["xi"], + "曀" => &["yi"], + "曁" => &["ji"], + "曂" => &["kuang"], + "曃" => &["dai"], + "曄" => &["ye"], + "曅" => &["ye"], + "曆" => &["li"], + "曇" => &["tan"], + "曈" => &["tong"], + "曉" => &["xiao"], + "曊" => &["fei"], + "曋" => &["qin"], + "曌" => &["zhao"], + "曍" => &["hao"], + "曎" => &["yi"], + "曏" => &["xiang"], + "曐" => &["xing"], + "曑" => &["sen"], + "曒" => &["jiao"], + "曓" => &["bao"], + "曔" => &["jing"], + "曖" => &["ai"], + "曗" => &["ye"], + "曘" => &["ru"], + "曙" => &["shu"], + "曚" => &["meng"], + "曛" => &["xun"], + "曜" => &["yao","yue"], + "曝" => &["bao","pu"], + "曞" => &["li"], + "曟" => &["chen"], + "曠" => &["kuang"], + "曡" => &["die"], + "曣" => &["yan"], + "曤" => &["huo"], + "曥" => &["lu"], + "曦" => &["xi"], + "曧" => &["rong"], + "曨" => &["long"], + "曩" => &["nang"], + "曪" => &["luo"], + "曫" => &["luan"], + "曬" => &["shai"], + "曭" => &["tang"], + "曮" => &["yan"], + "曯" => &["chu"], + "曰" => &["yue"], + "曱" => &["yue"], + "曲" => &["qu"], + "曳" => &["ye","zhuai","yi"], + "更" => &["geng"], + "曵" => &["zhuai"], + "曶" => &["hu"], + "曷" => &["he"], + "書" => &["shu"], + "曹" => &["cao"], + "曺" => &["cao"], + "曻" => &["sheng"], + "曼" => &["man"], + "曽" => &["ceng","zeng"], + "曾" => &["ceng","zeng"], + "替" => &["ti"], + "最" => &["zui"], + "朁" => &["can"], + "朂" => &["xu"], + "會" => &["hui","kuai"], + "朄" => &["yin"], + "朅" => &["qie"], + "朆" => &["fen"], + "朇" => &["pi","bi"], + "月" => &["yue"], + "有" => &["you"], + "朊" => &["ruan"], + "朋" => &["peng"], + "朌" => &["ban"], + "服" => &["fu"], + "朎" => &["ling"], + "朏" => &["fei"], + "朐" => &["qu"], + "朒" => &["nu:"], + "朓" => &["tiao"], + "朔" => &["shuo"], + "朕" => &["zhen"], + "朖" => &["lang"], + "朗" => &["lang"], + "朘" => &["juan","zui"], + "朙" => &["ming"], + "朚" => &["huang"], + "望" => &["wang"], + "朜" => &["tun"], + "朝" => &["chao","zhao"], + "朞" => &["ji","qi"], + "期" => &["qi","ji"], + "朠" => &["ying"], + "朡" => &["zong"], + "朢" => &["wang"], + "朣" => &["tong"], + "朤" => &["lang"], + "朦" => &["meng"], + "朧" => &["long"], + "木" => &["mu"], + "朩" => &["deng"], + "未" => &["wei"], + "末" => &["mo"], + "本" => &["ben"], + "札" => &["zha"], + "朮" => &["zhu"], + "术" => &["shu","zhu"], + "朱" => &["zhu"], + "朲" => &["ren"], + "朳" => &["ba"], + "朴" => &["po","piao","pu"], + "朵" => &["duo"], + "朶" => &["duo"], + "朷" => &["dao"], + "朸" => &["li"], + "朹" => &["qiu"], + "机" => &["ji"], + "朻" => &["jiu"], + "朼" => &["bi"], + "朽" => &["xiu"], + "朾" => &["ting"], + "朿" => &["ci"], + "杀" => &["sha"], + "杂" => &["za"], + "权" => &["quan"], + "杄" => &["qian"], + "杅" => &["yu"], + "杆" => &["gan"], + "杇" => &["wu"], + "杈" => &["cha"], + "杉" => &["shan","sha"], + "杊" => &["xun"], + "杋" => &["fan"], + "杌" => &["wu"], + "杍" => &["zi"], + "李" => &["li"], + "杏" => &["xing"], + "材" => &["cai"], + "村" => &["cun"], + "杒" => &["ren"], + "杓" => &["shao","biao"], + "杔" => &["zhe"], + "杕" => &["di"], + "杖" => &["zhang"], + "杗" => &["mang"], + "杘" => &["chi"], + "杙" => &["yi"], + "杚" => &["gu"], + "杛" => &["gong"], + "杜" => &["du"], + "杝" => &["yi"], + "杞" => &["qi"], + "束" => &["shu"], + "杠" => &["gang"], + "条" => &["tiao"], + "来" => &["lai"], + "杦" => &["shan"], + "杧" => &["mang"], + "杨" => &["yang"], + "杩" => &["ma"], + "杪" => &["miao"], + "杫" => &["si"], + "杬" => &["yuan"], + "杭" => &["hang"], + "杮" => &["fei"], + "杯" => &["bei"], + "杰" => &["jie"], + "東" => &["dong"], + "杲" => &["gao"], + "杳" => &["yao","miao"], + "杴" => &["xian"], + "杵" => &["chu"], + "杶" => &["chun"], + "杷" => &["pa","ba"], + "杸" => &["shu"], + "杹" => &["hua"], + "杺" => &["xin"], + "杻" => &["chou","niu"], + "杼" => &["zhu"], + "杽" => &["chou"], + "松" => &["song"], + "板" => &["ban"], + "枀" => &["song"], + "极" => &["ji"], + "枂" => &["yue"], + "枃" => &["yun"], + "构" => &["gou"], + "枅" => &["ji"], + "枆" => &["mao"], + "枇" => &["pi"], + "枈" => &["bi"], + "枉" => &["wang"], + "枊" => &["ang"], + "枋" => &["fang"], + "枌" => &["fen"], + "枍" => &["yi"], + "枎" => &["fu"], + "枏" => &["nan"], + "析" => &["xi"], + "枑" => &["hu"], + "枒" => &["ya"], + "枓" => &["dou"], + "枔" => &["xun"], + "枕" => &["zhen"], + "枖" => &["yao"], + "林" => &["lin"], + "枘" => &["rui"], + "枙" => &["e"], + "枚" => &["mei"], + "枛" => &["zhao"], + "果" => &["guo"], + "枝" => &["zhi","qi"], + "枞" => &["zong","cong"], + "枟" => &["yun"], + "枡" => &["dou"], + "枢" => &["shu"], + "枣" => &["zao"], + "枥" => &["li"], + "枦" => &["lu"], + "枧" => &["jian"], + "枨" => &["cheng"], + "枩" => &["song"], + "枪" => &["qiang"], + "枫" => &["feng"], + "枬" => &["nan"], + "枭" => &["xiao"], + "枮" => &["xian"], + "枯" => &["ku"], + "枰" => &["ping"], + "枱" => &["tai"], + "枲" => &["xi"], + "枳" => &["zhi"], + "枴" => &["guai"], + "枵" => &["xiao"], + "架" => &["jia"], + "枷" => &["jia"], + "枸" => &["gou","ju"], + "枹" => &["bao","fu"], + "枺" => &["mo"], + "枻" => &["yi"], + "枼" => &["ye"], + "枽" => &["sang"], + "枾" => &["shi"], + "枿" => &["nie"], + "柀" => &["bi"], + "柁" => &["tuo","duo"], + "柂" => &["yi"], + "柃" => &["ling"], + "柄" => &["bing"], + "柅" => &["ni"], + "柆" => &["la"], + "柇" => &["he"], + "柈" => &["ban"], + "柉" => &["fan","bian"], + "柊" => &["zhong"], + "柋" => &["dai"], + "柌" => &["ci"], + "柍" => &["yang"], + "柎" => &["fu"], + "柏" => &["bai","bo"], + "某" => &["mou"], + "柑" => &["gan"], + "柒" => &["qi"], + "染" => &["ran"], + "柔" => &["rou"], + "柕" => &["mao"], + "柖" => &["zhao"], + "柗" => &["song"], + "柘" => &["zhe"], + "柙" => &["xia"], + "柚" => &["you"], + "柛" => &["shen"], + "柜" => &["ju","gui"], + "柝" => &["tuo"], + "柞" => &["zuo","zha"], + "柟" => &["nan"], + "柠" => &["ning"], + "柡" => &["yong"], + "柢" => &["di"], + "柣" => &["zhi"], + "柤" => &["zha"], + "查" => &["cha","zha"], + "柦" => &["dan"], + "柧" => &["gu"], + "柩" => &["jiu"], + "柪" => &["ao"], + "柫" => &["fu","bi"], + "柬" => &["jian"], + "柭" => &["bo"], + "柮" => &["duo"], + "柯" => &["ke"], + "柰" => &["nai"], + "柱" => &["zhu"], + "柲" => &["bi"], + "柳" => &["liu"], + "柴" => &["chai"], + "柵" => &["zha"], + "柶" => &["si"], + "柷" => &["zhu"], + "柸" => &["pei"], + "柹" => &["shi"], + "柺" => &["guai"], + "査" => &["cha","zha"], + "柼" => &["yao"], + "柽" => &["cheng"], + "柾" => &["jiu"], + "柿" => &["shi"], + "栀" => &["zhi"], + "栁" => &["liu"], + "栂" => &["mei"], + "栄" => &["rong"], + "栅" => &["zha","shan"], + "标" => &["biao"], + "栈" => &["zhan"], + "栉" => &["zhi"], + "栊" => &["long"], + "栋" => &["dong"], + "栌" => &["lu"], + "栎" => &["li","yue"], + "栏" => &["lan"], + "栐" => &["yong"], + "树" => &["shu"], + "栒" => &["xun"], + "栓" => &["shuan"], + "栔" => &["qi"], + "栕" => &["zhen"], + "栖" => &["qi","xi"], + "栗" => &["li"], + "栘" => &["chi","yi"], + "栙" => &["xiang"], + "栚" => &["zhen"], + "栛" => &["li"], + "栜" => &["su"], + "栝" => &["gua","kuo"], + "栞" => &["kan"], + "栟" => &["bing","ben"], + "栠" => &["ren"], + "校" => &["xiao","jiao"], + "栢" => &["bo","bai"], + "栣" => &["ren"], + "栤" => &["bing"], + "栥" => &["zi"], + "栦" => &["chou"], + "栧" => &["yi"], + "栨" => &["ci"], + "栩" => &["xu"], + "株" => &["zhu"], + "栫" => &["jian"], + "栬" => &["zui"], + "栭" => &["er"], + "栮" => &["er"], + "栯" => &["yu"], + "栰" => &["fa"], + "栱" => &["gong"], + "栲" => &["kao"], + "栳" => &["lao"], + "栴" => &["zhan"], + "栵" => &["li"], + "样" => &["yang"], + "核" => &["he","hu"], + "根" => &["gen"], + "栺" => &["zhi"], + "栻" => &["chi"], + "格" => &["ge"], + "栽" => &["zai"], + "栾" => &["luan"], + "栿" => &["fa"], + "桀" => &["jie"], + "桁" => &["heng","hang"], + "桂" => &["gui"], + "桃" => &["tao"], + "桄" => &["guang"], + "桅" => &["wei"], + "框" => &["kuang"], + "桇" => &["ru"], + "案" => &["an"], + "桉" => &["an"], + "桊" => &["juan"], + "桋" => &["yi"], + "桌" => &["zhuo"], + "桍" => &["ku"], + "桎" => &["zhi"], + "桏" => &["qiong"], + "桐" => &["tong"], + "桑" => &["sang"], + "桒" => &["sang"], + "桓" => &["huan"], + "桔" => &["jie","ju"], + "桕" => &["jiu"], + "桖" => &["xue"], + "桗" => &["duo"], + "桘" => &["zhui"], + "桙" => &["yu"], + "桚" => &["zan"], + "桜" => &["ying"], + "桟" => &["zhan"], + "桠" => &["ya"], + "桡" => &["rao"], + "桢" => &["zhen"], + "档" => &["dang"], + "桤" => &["qi"], + "桥" => &["qiao"], + "桦" => &["hua"], + "桧" => &["gui","hui"], + "桨" => &["jiang"], + "桩" => &["zhuang"], + "桪" => &["xun"], + "桫" => &["suo"], + "桬" => &["suo"], + "桭" => &["zhen"], + "桮" => &["bei"], + "桯" => &["ting"], + "桰" => &["kuo"], + "桱" => &["jing"], + "桲" => &["bo"], + "桳" => &["ben"], + "桴" => &["fu"], + "桵" => &["rui"], + "桶" => &["tong"], + "桷" => &["jue"], + "桸" => &["xi"], + "桹" => &["lang"], + "桺" => &["liu"], + "桻" => &["feng"], + "桼" => &["qi"], + "桽" => &["wen"], + "桾" => &["jun"], + "桿" => &["gan"], + "梀" => &["cu"], + "梁" => &["liang"], + "梂" => &["qiu"], + "梃" => &["ting"], + "梄" => &["you"], + "梅" => &["mei"], + "梆" => &["bang"], + "梇" => &["long"], + "梈" => &["peng"], + "梉" => &["zhuang"], + "梊" => &["di"], + "梋" => &["xuan"], + "梌" => &["tu"], + "梍" => &["zao"], + "梎" => &["ao"], + "梏" => &["gu"], + "梐" => &["bi"], + "梑" => &["di"], + "梒" => &["han"], + "梓" => &["zi"], + "梔" => &["zhi"], + "梕" => &["ren"], + "梖" => &["bei"], + "梗" => &["geng"], + "梘" => &["jian"], + "梙" => &["huan"], + "梚" => &["wan"], + "梛" => &["nuo"], + "梜" => &["jia"], + "條" => &["tiao"], + "梞" => &["ji"], + "梟" => &["xiao"], + "梠" => &["lu:"], + "梡" => &["kuan"], + "梢" => &["shao","sao"], + "梣" => &["cen"], + "梤" => &["fen"], + "梥" => &["song"], + "梦" => &["meng"], + "梧" => &["wu"], + "梨" => &["li"], + "梩" => &["li"], + "梪" => &["dou"], + "梫" => &["cen"], + "梬" => &["ying"], + "梭" => &["suo"], + "梮" => &["ju"], + "梯" => &["ti"], + "械" => &["xie"], + "梱" => &["kun"], + "梲" => &["zhuo"], + "梳" => &["shu"], + "梴" => &["chan"], + "梵" => &["fan"], + "梶" => &["wei"], + "梷" => &["jing"], + "梸" => &["li"], + "梹" => &["bing","bin"], + "梼" => &["tao"], + "梽" => &["zhi"], + "梾" => &["lai"], + "梿" => &["lian"], + "检" => &["jian"], + "棁" => &["zhuo"], + "棂" => &["ling"], + "棃" => &["li"], + "棄" => &["qi"], + "棅" => &["bing"], + "棆" => &["lun"], + "棇" => &["cong"], + "棈" => &["qian"], + "棉" => &["mian"], + "棊" => &["qi"], + "棋" => &["qi"], + "棌" => &["cai"], + "棍" => &["gun"], + "棎" => &["chan"], + "棏" => &["de"], + "棐" => &["fei"], + "棑" => &["pai"], + "棒" => &["bang"], + "棓" => &["pou","bang"], + "棔" => &["hun"], + "棕" => &["zong"], + "棖" => &["cheng"], + "棗" => &["zao"], + "棘" => &["ji"], + "棙" => &["li"], + "棚" => &["peng"], + "棛" => &["yu"], + "棜" => &["yu"], + "棝" => &["gu"], + "棞" => &["hun"], + "棟" => &["dong"], + "棠" => &["tang"], + "棡" => &["gang"], + "棢" => &["wang"], + "棣" => &["di"], + "棤" => &["xi"], + "棥" => &["fan"], + "棦" => &["cheng"], + "棧" => &["zhan"], + "棨" => &["qi"], + "棩" => &["yuan"], + "棪" => &["yan"], + "棫" => &["yu"], + "棬" => &["quan"], + "棭" => &["yi"], + "森" => &["sen"], + "棯" => &["ren"], + "棰" => &["chui"], + "棱" => &["leng","ling"], + "棲" => &["qi","xi"], + "棳" => &["zhuo"], + "棴" => &["fu"], + "棵" => &["ke"], + "棶" => &["lai"], + "棷" => &["zou"], + "棸" => &["zou"], + "棹" => &["zhao","zhuo"], + "棺" => &["guan"], + "棻" => &["fen"], + "棼" => &["fen"], + "棽" => &["chen"], + "棾" => &["qiong"], + "棿" => &["nie"], + "椀" => &["wan"], + "椁" => &["guo"], + "椂" => &["lu"], + "椃" => &["hao"], + "椄" => &["jie"], + "椅" => &["yi"], + "椆" => &["chou"], + "椇" => &["ju"], + "椈" => &["ju"], + "椉" => &["cheng","sheng"], + "椊" => &["zuo"], + "椋" => &["liang"], + "椌" => &["qiang"], + "植" => &["zhi"], + "椎" => &["zhui","chui"], + "椏" => &["ya"], + "椐" => &["ju"], + "椑" => &["bei","pi"], + "椒" => &["jiao"], + "椓" => &["zhuo"], + "椔" => &["zi"], + "椕" => &["bin"], + "椖" => &["peng"], + "椗" => &["ding"], + "椘" => &["chu"], + "椙" => &["shan"], + "検" => &["jian"], + "椝" => &["gui"], + "椞" => &["xi"], + "椟" => &["du"], + "椠" => &["qian"], + "椢" => &["kui"], + "椤" => &["luo"], + "椥" => &["zhi"], + "椪" => &["peng"], + "椫" => &["shan"], + "椭" => &["tuo"], + "椮" => &["sen"], + "椯" => &["duo"], + "椰" => &["ye"], + "椱" => &["fu"], + "椲" => &["wei"], + "椳" => &["wei"], + "椴" => &["duan"], + "椵" => &["jia"], + "椶" => &["zong"], + "椷" => &["jian"], + "椸" => &["yi"], + "椹" => &["shen","zhen"], + "椺" => &["xi"], + "椻" => &["yan"], + "椼" => &["yan"], + "椽" => &["chuan"], + "椾" => &["zhan"], + "椿" => &["chun"], + "楀" => &["yu","ju"], + "楁" => &["he"], + "楂" => &["zha","cha"], + "楃" => &["wo"], + "楄" => &["bian"], + "楅" => &["bi"], + "楆" => &["yao"], + "楇" => &["huo"], + "楈" => &["xu"], + "楉" => &["ruo"], + "楊" => &["yang"], + "楋" => &["la"], + "楌" => &["yan"], + "楍" => &["ben"], + "楎" => &["hun"], + "楏" => &["kui"], + "楐" => &["jie"], + "楑" => &["kui"], + "楒" => &["si"], + "楓" => &["feng"], + "楔" => &["xie"], + "楕" => &["tuo"], + "楖" => &["ji"], + "楗" => &["jian"], + "楘" => &["mu"], + "楙" => &["mao"], + "楚" => &["chu"], + "楛" => &["hu","ku"], + "楜" => &["hu"], + "楝" => &["lian"], + "楞" => &["leng"], + "楟" => &["ting"], + "楠" => &["nan"], + "楡" => &["yu"], + "楢" => &["you"], + "楣" => &["mei"], + "楤" => &["song"], + "楥" => &["xuan"], + "楦" => &["xuan"], + "楧" => &["ying"], + "楨" => &["zhen"], + "楩" => &["pian"], + "楪" => &["die"], + "楫" => &["ji"], + "楬" => &["jie"], + "業" => &["ye"], + "楮" => &["chu"], + "楯" => &["shun","dun"], + "楰" => &["yu"], + "楱" => &["cou"], + "楲" => &["wei"], + "楳" => &["mei"], + "楴" => &["di"], + "極" => &["ji"], + "楶" => &["jie"], + "楷" => &["kai","jie"], + "楸" => &["qiu"], + "楹" => &["ying"], + "楺" => &["rou"], + "楻" => &["heng"], + "楼" => &["lou"], + "楽" => &["le","yue"], + "楿" => &["gui"], + "榀" => &["pin"], + "概" => &["gai"], + "榃" => &["tan"], + "榄" => &["lan"], + "榅" => &["yun"], + "榆" => &["yu"], + "榇" => &["chen"], + "榈" => &["lu:"], + "榉" => &["ju"], + "榍" => &["xie"], + "榎" => &["jia"], + "榏" => &["yi"], + "榐" => &["zhan"], + "榑" => &["fu"], + "榒" => &["nuo"], + "榓" => &["mi"], + "榔" => &["lang"], + "榕" => &["rong"], + "榖" => &["gu"], + "榗" => &["jian"], + "榘" => &["ju"], + "榙" => &["ta"], + "榚" => &["yao"], + "榛" => &["zhen"], + "榜" => &["bang"], + "榝" => &["sha"], + "榞" => &["yuan"], + "榟" => &["zi"], + "榠" => &["ming"], + "榡" => &["su"], + "榢" => &["jia"], + "榣" => &["yao"], + "榤" => &["jie"], + "榥" => &["huang"], + "榦" => &["gan","han"], + "榧" => &["fei"], + "榨" => &["zha"], + "榩" => &["qian"], + "榪" => &["ma"], + "榫" => &["sun"], + "榬" => &["yuan"], + "榭" => &["xie"], + "榮" => &["rong"], + "榯" => &["shi"], + "榰" => &["zhi"], + "榱" => &["cui"], + "榲" => &["yun"], + "榳" => &["ting"], + "榴" => &["liu"], + "榵" => &["rong"], + "榶" => &["tang"], + "榷" => &["que"], + "榸" => &["zhai"], + "榹" => &["si"], + "榺" => &["sheng"], + "榻" => &["ta"], + "榼" => &["ke"], + "榽" => &["xi"], + "榾" => &["gu"], + "榿" => &["qi"], + "槀" => &["kao"], + "槁" => &["gao"], + "槂" => &["sun"], + "槃" => &["pan"], + "槄" => &["tao"], + "槅" => &["ge"], + "槆" => &["xun"], + "槇" => &["dian","zhen"], + "槈" => &["nou"], + "槉" => &["ji"], + "槊" => &["shuo"], + "構" => &["gou"], + "槌" => &["chui"], + "槍" => &["qiang"], + "槎" => &["cha"], + "槏" => &["qian"], + "槐" => &["huai"], + "槑" => &["mei"], + "槒" => &["xu"], + "槓" => &["gang"], + "槔" => &["gao"], + "槕" => &["zhuo"], + "槖" => &["tuo"], + "槗" => &["qiao"], + "様" => &["yang"], + "槙" => &["dian"], + "槚" => &["jia"], + "槛" => &["jian","kan"], + "槜" => &["zui"], + "槞" => &["long"], + "槟" => &["bin","bing"], + "槠" => &["zhu"], + "槢" => &["xi"], + "槣" => &["qi"], + "槤" => &["lian"], + "槥" => &["hui"], + "槦" => &["yong"], + "槧" => &["qian"], + "槨" => &["guo"], + "槩" => &["gai"], + "槪" => &["gai"], + "槫" => &["tuan"], + "槬" => &["hua"], + "槭" => &["qi","cu"], + "槮" => &["sen"], + "槯" => &["cui"], + "槰" => &["beng"], + "槱" => &["you"], + "槲" => &["hu"], + "槳" => &["jiang"], + "槴" => &["hu"], + "槵" => &["huan"], + "槶" => &["kui"], + "槷" => &["yi"], + "槸" => &["yi"], + "槹" => &["gao"], + "槺" => &["kang"], + "槻" => &["gui"], + "槼" => &["gui"], + "槽" => &["cao"], + "槾" => &["man"], + "槿" => &["jin"], + "樀" => &["di"], + "樁" => &["zhuang"], + "樂" => &["le","yue"], + "樃" => &["lang"], + "樄" => &["chen"], + "樅" => &["cong","zong"], + "樆" => &["li"], + "樇" => &["xiu"], + "樈" => &["qing"], + "樉" => &["shuang"], + "樊" => &["fan"], + "樋" => &["tong"], + "樌" => &["guan"], + "樍" => &["ji"], + "樎" => &["suo"], + "樏" => &["lei"], + "樐" => &["lu"], + "樑" => &["liang"], + "樒" => &["mi"], + "樓" => &["lou"], + "樔" => &["chao"], + "樕" => &["su"], + "樖" => &["ke"], + "樗" => &["chu","shu"], + "樘" => &["tang"], + "標" => &["biao"], + "樚" => &["lu"], + "樛" => &["jiu"], + "樜" => &["shu"], + "樝" => &["zha"], + "樞" => &["shu"], + "樟" => &["zhang"], + "樠" => &["men"], + "模" => &["mo","mu"], + "樢" => &["niao"], + "樣" => &["yang"], + "樤" => &["tiao"], + "樥" => &["peng"], + "樦" => &["zhu"], + "樧" => &["sha"], + "樨" => &["xi"], + "権" => &["quan"], + "横" => &["heng"], + "樫" => &["jian"], + "樬" => &["cong"], + "樯" => &["qiang"], + "樱" => &["ying"], + "樲" => &["er"], + "樳" => &["xin"], + "樴" => &["zhi"], + "樵" => &["qiao"], + "樶" => &["zui"], + "樷" => &["cong"], + "樸" => &["pu","po"], + "樹" => &["shu"], + "樺" => &["hua"], + "樻" => &["kui"], + "樼" => &["zhen"], + "樽" => &["zun"], + "樾" => &["yue"], + "樿" => &["zhan"], + "橀" => &["xi"], + "橁" => &["xun"], + "橂" => &["dian"], + "橃" => &["fa"], + "橄" => &["gan"], + "橅" => &["mo","mu"], + "橆" => &["wu"], + "橇" => &["qiao","cui"], + "橈" => &["rao","nao"], + "橉" => &["lin"], + "橊" => &["liu"], + "橋" => &["qiao"], + "橌" => &["xian"], + "橍" => &["run"], + "橎" => &["fan"], + "橏" => &["zhan"], + "橐" => &["tuo"], + "橑" => &["lao"], + "橒" => &["yun"], + "橓" => &["shun"], + "橔" => &["tui"], + "橕" => &["cheng"], + "橖" => &["tang"], + "橗" => &["meng"], + "橘" => &["ju"], + "橙" => &["cheng","chen"], + "橚" => &["su"], + "橛" => &["jue"], + "橜" => &["jue"], + "橝" => &["tan"], + "橞" => &["hui"], + "機" => &["ji"], + "橠" => &["nuo"], + "橡" => &["xiang"], + "橢" => &["tuo"], + "橣" => &["ning"], + "橤" => &["rui"], + "橥" => &["zhu"], + "橦" => &["tong","chuang"], + "橧" => &["zeng"], + "橨" => &["fen"], + "橩" => &["qiong"], + "橪" => &["ran"], + "橫" => &["heng"], + "橬" => &["cen"], + "橭" => &["gu","ku"], + "橮" => &["liu"], + "橯" => &["lao"], + "橰" => &["gao"], + "橱" => &["chu"], + "橶" => &["ji"], + "橷" => &["dou"], + "橹" => &["lu"], + "橼" => &["yuan"], + "橽" => &["ta"], + "橾" => &["shu"], + "橿" => &["jiang"], + "檀" => &["tan"], + "檁" => &["lin"], + "檂" => &["nong"], + "檃" => &["yin"], + "檄" => &["xi"], + "檅" => &["sui"], + "檆" => &["shan"], + "檇" => &["zui"], + "檈" => &["xuan"], + "檉" => &["cheng"], + "檊" => &["gan"], + "檋" => &["ju"], + "檌" => &["zui"], + "檍" => &["yi"], + "檎" => &["qin"], + "檏" => &["pu"], + "檐" => &["yan","yin"], + "檑" => &["lei"], + "檒" => &["feng"], + "檓" => &["hui"], + "檔" => &["dang"], + "檕" => &["ji"], + "檖" => &["sui"], + "檗" => &["bo"], + "檘" => &["bi"], + "檙" => &["ding"], + "檚" => &["chu"], + "檛" => &["zhua"], + "檜" => &["gui","hui","kuai"], + "檝" => &["ji"], + "檞" => &["jia"], + "檟" => &["jia"], + "檠" => &["qing"], + "檡" => &["zhe"], + "檢" => &["jian"], + "檣" => &["qiang"], + "檤" => &["dao"], + "檥" => &["yi"], + "檦" => &["biao"], + "檧" => &["song"], + "檨" => &["she"], + "檩" => &["lin"], + "檪" => &["li","yue"], + "檫" => &["cha"], + "檬" => &["meng"], + "檭" => &["yin"], + "檮" => &["tao"], + "檯" => &["tai"], + "檰" => &["mian"], + "檱" => &["qi"], + "檳" => &["bin","bing"], + "檴" => &["huo"], + "檵" => &["ji"], + "檶" => &["qian"], + "檷" => &["mi","ni"], + "檸" => &["ning"], + "檹" => &["yi"], + "檺" => &["gao"], + "檻" => &["jian","kan"], + "檼" => &["yin"], + "檽" => &["er"], + "檾" => &["qing"], + "檿" => &["yan"], + "櫀" => &["qi"], + "櫁" => &["mi"], + "櫂" => &["zhao"], + "櫃" => &["gui","ju"], + "櫄" => &["chun"], + "櫅" => &["ji"], + "櫆" => &["kui"], + "櫇" => &["po"], + "櫈" => &["deng"], + "櫉" => &["chu"], + "櫋" => &["mian"], + "櫌" => &["you"], + "櫍" => &["zhi"], + "櫎" => &["guang"], + "櫏" => &["qian"], + "櫐" => &["lei"], + "櫑" => &["lei"], + "櫒" => &["sa"], + "櫓" => &["lu"], + "櫕" => &["cuan"], + "櫖" => &["lu:"], + "櫗" => &["mie"], + "櫘" => &["hui"], + "櫙" => &["ou"], + "櫚" => &["lu:"], + "櫛" => &["zhi","jie"], + "櫜" => &["gao"], + "櫝" => &["du"], + "櫞" => &["yuan"], + "櫟" => &["li","yue"], + "櫠" => &["fei"], + "櫡" => &["zhu"], + "櫢" => &["sou"], + "櫣" => &["lian"], + "櫥" => &["chu"], + "櫧" => &["zhu"], + "櫨" => &["lu"], + "櫩" => &["yan"], + "櫪" => &["li"], + "櫫" => &["zhu"], + "櫬" => &["chen"], + "櫭" => &["jie"], + "櫮" => &["e"], + "櫯" => &["su"], + "櫰" => &["huai"], + "櫱" => &["nie"], + "櫲" => &["yu"], + "櫳" => &["long"], + "櫴" => &["lai"], + "櫶" => &["xian"], + "櫸" => &["ju"], + "櫹" => &["xiao"], + "櫺" => &["ling"], + "櫻" => &["ying"], + "櫼" => &["jian"], + "櫽" => &["yin"], + "櫾" => &["you"], + "櫿" => &["ying"], + "欀" => &["xiang"], + "欁" => &["nong"], + "欂" => &["bo"], + "欃" => &["chan"], + "欄" => &["lan"], + "欅" => &["ju"], + "欆" => &["shuang"], + "欇" => &["she"], + "欈" => &["wei"], + "欉" => &["cong"], + "權" => &["quan"], + "欋" => &["qu"], + "欎" => &["yu"], + "欏" => &["luo"], + "欐" => &["li"], + "欑" => &["zan"], + "欒" => &["luan"], + "欓" => &["dang"], + "欔" => &["jue"], + "欖" => &["lan"], + "欗" => &["lan"], + "欘" => &["zhu"], + "欙" => &["lei"], + "欚" => &["li","ji"], + "欛" => &["ba"], + "欜" => &["nang"], + "欝" => &["yu"], + "欞" => &["ling"], + "欠" => &["qian"], + "次" => &["ci"], + "欢" => &["huan"], + "欣" => &["xin"], + "欤" => &["yu"], + "欥" => &["yu"], + "欦" => &["qian"], + "欧" => &["ou"], + "欨" => &["xu"], + "欩" => &["chao"], + "欪" => &["chu"], + "欫" => &["qi"], + "欬" => &["kai"], + "欭" => &["yi"], + "欮" => &["jue"], + "欯" => &["xi"], + "欰" => &["xu"], + "欱" => &["xia"], + "欲" => &["yu"], + "欳" => &["kuai"], + "欴" => &["lang"], + "欵" => &["kuan"], + "欶" => &["shuo"], + "欷" => &["xi"], + "欸" => &["e^","ai"], + "欹" => &["yi","qi"], + "欺" => &["qi"], + "欻" => &["hu"], + "欼" => &["chi"], + "欽" => &["qin"], + "款" => &["kuan"], + "欿" => &["kan"], + "歀" => &["kuan"], + "歁" => &["kan"], + "歂" => &["chuan"], + "歃" => &["sha"], + "歅" => &["yin"], + "歆" => &["xin"], + "歇" => &["xie"], + "歈" => &["yu"], + "歉" => &["qian"], + "歊" => &["xiao"], + "歋" => &["yi"], + "歌" => &["ge"], + "歍" => &["wu"], + "歎" => &["tan"], + "歏" => &["jin"], + "歐" => &["ou"], + "歑" => &["hu"], + "歒" => &["ti"], + "歓" => &["huan"], + "歔" => &["xu"], + "歕" => &["pen"], + "歖" => &["xi"], + "歗" => &["xiao"], + "歘" => &["hu"], + "歙" => &["she","xi"], + "歛" => &["lian"], + "歜" => &["chu"], + "歝" => &["yi"], + "歞" => &["kan"], + "歟" => &["yu"], + "歠" => &["chuo"], + "歡" => &["huan"], + "止" => &["zhi"], + "正" => &["zheng"], + "此" => &["ci"], + "步" => &["bu"], + "武" => &["wu"], + "歧" => &["qi"], + "歨" => &["bu"], + "歩" => &["bu"], + "歪" => &["wai"], + "歫" => &["ju"], + "歬" => &["qian"], + "歭" => &["chi"], + "歮" => &["se"], + "歯" => &["chi"], + "歰" => &["se"], + "歱" => &["zhong"], + "歲" => &["sui"], + "歳" => &["sui"], + "歴" => &["li"], + "歵" => &["cuo"], + "歶" => &["yu"], + "歷" => &["li"], + "歸" => &["gui"], + "歹" => &["dai"], + "歺" => &["dai"], + "死" => &["si"], + "歼" => &["jian"], + "歽" => &["zhe"], + "歾" => &["mo"], + "歿" => &["mo"], + "殀" => &["yao"], + "殁" => &["mo"], + "殂" => &["cu"], + "殃" => &["yang"], + "殄" => &["tian"], + "殅" => &["sheng"], + "殆" => &["dai"], + "殇" => &["shang"], + "殈" => &["xu"], + "殉" => &["xun"], + "殊" => &["shu"], + "残" => &["can"], + "殌" => &["jue"], + "殍" => &["piao"], + "殎" => &["qia"], + "殏" => &["qiu"], + "殐" => &["su"], + "殑" => &["qing"], + "殒" => &["yun"], + "殓" => &["lian"], + "殔" => &["yi"], + "殕" => &["fou"], + "殖" => &["zhi","shi"], + "殗" => &["ye"], + "殘" => &["can"], + "殙" => &["hun"], + "殚" => &["dan"], + "殛" => &["ji"], + "殜" => &["ye"], + "殞" => &["yun"], + "殟" => &["wen"], + "殠" => &["chou","xiu"], + "殡" => &["bin"], + "殢" => &["ti"], + "殣" => &["jin"], + "殤" => &["shang"], + "殥" => &["yin"], + "殦" => &["diao"], + "殧" => &["cu"], + "殨" => &["hui"], + "殩" => &["cuan"], + "殪" => &["yi"], + "殫" => &["dan"], + "殬" => &["du"], + "殭" => &["jiang"], + "殮" => &["lian"], + "殯" => &["bin"], + "殰" => &["du"], + "殱" => &["jian"], + "殲" => &["jian"], + "殳" => &["shu"], + "殴" => &["ou"], + "段" => &["duan"], + "殶" => &["zhu"], + "殷" => &["yin","yan"], + "殸" => &["qing"], + "殹" => &["yi"], + "殺" => &["sha","shai"], + "殻" => &["ke","qiao"], + "殼" => &["ke","qiao","que"], + "殽" => &["yao"], + "殾" => &["xun"], + "殿" => &["dian"], + "毀" => &["hui"], + "毁" => &["hui"], + "毂" => &["gu"], + "毃" => &["que"], + "毄" => &["ji"], + "毅" => &["yi"], + "毆" => &["ou"], + "毇" => &["hui"], + "毈" => &["duan"], + "毉" => &["yi"], + "毊" => &["xiao"], + "毋" => &["wu"], + "毌" => &["guan"], + "母" => &["mu"], + "毎" => &["mei"], + "每" => &["mei"], + "毐" => &["ai"], + "毑" => &["zuo"], + "毒" => &["du"], + "毓" => &["yu"], + "比" => &["bi"], + "毕" => &["bi"], + "毖" => &["bi"], + "毗" => &["pi"], + "毘" => &["pi"], + "毙" => &["bi"], + "毚" => &["chan"], + "毛" => &["mao"], + "毞" => &["pi"], + "毠" => &["jia"], + "毡" => &["zhan"], + "毢" => &["sai"], + "毣" => &["mu"], + "毤" => &["tuo"], + "毥" => &["xun"], + "毦" => &["er"], + "毧" => &["rong"], + "毨" => &["xian"], + "毩" => &["ju"], + "毪" => &["mu"], + "毫" => &["hao"], + "毬" => &["qiu"], + "毭" => &["dou"], + "毯" => &["tan"], + "毰" => &["pei"], + "毱" => &["ju"], + "毲" => &["duo"], + "毳" => &["cui"], + "毴" => &["bi"], + "毵" => &["san"], + "毷" => &["mao"], + "毸" => &["sui"], + "毹" => &["shu"], + "毺" => &["yu"], + "毻" => &["tuo"], + "毼" => &["he"], + "毽" => &["jian"], + "毾" => &["ta"], + "毿" => &["san"], + "氀" => &["lu:"], + "氁" => &["mu"], + "氂" => &["li"], + "氃" => &["tong"], + "氄" => &["rong"], + "氅" => &["chang"], + "氆" => &["pu"], + "氇" => &["lu"], + "氈" => &["zhan"], + "氉" => &["sao"], + "氊" => &["zhan"], + "氋" => &["meng"], + "氌" => &["lu"], + "氍" => &["qu"], + "氎" => &["die"], + "氏" => &["shi","zhi"], + "氐" => &["di"], + "民" => &["min"], + "氒" => &["jue"], + "氓" => &["mang","meng"], + "气" => &["qi"], + "氕" => &["pie"], + "氖" => &["nai"], + "気" => &["qi"], + "氘" => &["dao"], + "氙" => &["xian"], + "氚" => &["chuan"], + "氛" => &["fen"], + "氜" => &["ri"], + "氝" => &["nei","nai"], + "氟" => &["fu"], + "氠" => &["shen"], + "氡" => &["dong"], + "氢" => &["qing"], + "氣" => &["qi"], + "氤" => &["yin"], + "氥" => &["xi"], + "氦" => &["hai"], + "氧" => &["yang"], + "氨" => &["an"], + "氩" => &["ya"], + "氪" => &["ke"], + "氫" => &["qing"], + "氬" => &["ya"], + "氭" => &["dong"], + "氮" => &["dan"], + "氯" => &["lu:"], + "氰" => &["qing"], + "氱" => &["yang"], + "氲" => &["yun"], + "氳" => &["yun"], + "水" => &["shui"], + "氵" => &["shui"], + "氶" => &["zheng"], + "氷" => &["bing"], + "永" => &["yong"], + "氹" => &["dang"], + "氺" => &["shui"], + "氻" => &["le"], + "氼" => &["ni"], + "氽" => &["tun"], + "氾" => &["fan"], + "氿" => &["gui"], + "汀" => &["ting"], + "汁" => &["zhi"], + "求" => &["qiu"], + "汃" => &["bin"], + "汄" => &["ze"], + "汅" => &["mian"], + "汆" => &["cuan"], + "汇" => &["hui"], + "汈" => &["diao"], + "汉" => &["han"], + "汊" => &["cha"], + "汋" => &["zhuo"], + "汌" => &["chuan"], + "汍" => &["wan"], + "汎" => &["fan"], + "汏" => &["dai"], + "汐" => &["xi"], + "汑" => &["tuo"], + "汒" => &["mang"], + "汓" => &["qiu"], + "汔" => &["qi"], + "汕" => &["shan"], + "汖" => &["pai"], + "汗" => &["han"], + "汘" => &["qian"], + "汙" => &["wu"], + "汚" => &["wu"], + "汛" => &["xun"], + "汜" => &["si"], + "汝" => &["ru"], + "汞" => &["gong","hong"], + "江" => &["jiang"], + "池" => &["chi"], + "污" => &["wu"], + "汤" => &["tang","shang"], + "汥" => &["zhi"], + "汦" => &["chi"], + "汧" => &["qian"], + "汨" => &["mi"], + "汩" => &["gu"], + "汪" => &["wang"], + "汫" => &["qing"], + "汬" => &["jing"], + "汭" => &["rui"], + "汮" => &["jun"], + "汯" => &["hong"], + "汰" => &["tai"], + "汱" => &["quan"], + "汲" => &["ji"], + "汳" => &["bian"], + "汴" => &["bian"], + "汵" => &["gan"], + "汶" => &["wen"], + "汷" => &["zhong"], + "汸" => &["fang"], + "汹" => &["xiong"], + "決" => &["jue"], + "汻" => &["hu"], + "汽" => &["qi"], + "汾" => &["fen"], + "汿" => &["xu"], + "沀" => &["xu"], + "沁" => &["qin","shen"], + "沂" => &["yi"], + "沃" => &["wo"], + "沄" => &["yun"], + "沅" => &["yuan"], + "沆" => &["hang"], + "沇" => &["yan"], + "沈" => &["shen","chen"], + "沉" => &["chen"], + "沊" => &["dan"], + "沋" => &["you"], + "沌" => &["dun","zhuan"], + "沍" => &["hu"], + "沎" => &["huo"], + "沏" => &["qi"], + "沐" => &["mu"], + "沑" => &["rou"], + "沒" => &["mei","mo"], + "沓" => &["ta","da"], + "沔" => &["mian"], + "沕" => &["wu"], + "沖" => &["chong"], + "沗" => &["tian"], + "沘" => &["bi"], + "沙" => &["sha"], + "沚" => &["zhi"], + "沛" => &["pei"], + "沜" => &["pan"], + "沝" => &["zhui"], + "沞" => &["za"], + "沟" => &["gou"], + "沠" => &["liu"], + "没" => &["mei","mo"], + "沢" => &["ze"], + "沣" => &["feng"], + "沤" => &["ou"], + "沥" => &["li"], + "沦" => &["lun"], + "沧" => &["cang"], + "沨" => &["feng"], + "沩" => &["wei"], + "沪" => &["hu"], + "沫" => &["mo"], + "沬" => &["mei"], + "沭" => &["shu"], + "沮" => &["ju"], + "沯" => &["zan"], + "沰" => &["tuo"], + "沱" => &["tuo"], + "沲" => &["duo"], + "河" => &["he"], + "沴" => &["li"], + "沵" => &["mi"], + "沶" => &["yi"], + "沷" => &["fu"], + "沸" => &["fei"], + "油" => &["you"], + "沺" => &["tian"], + "治" => &["zhi"], + "沼" => &["zhao"], + "沽" => &["gu"], + "沾" => &["zhan"], + "沿" => &["yan"], + "泀" => &["si"], + "況" => &["kuang"], + "泂" => &["jiong"], + "泃" => &["ju"], + "泄" => &["xie"], + "泅" => &["qiu"], + "泆" => &["yi"], + "泇" => &["jia"], + "泈" => &["zhong"], + "泉" => &["quan"], + "泊" => &["bo","po"], + "泋" => &["hui"], + "泌" => &["mi","bi"], + "泍" => &["ben"], + "泎" => &["zhuo"], + "泏" => &["chu"], + "泐" => &["le"], + "泑" => &["you"], + "泒" => &["gu"], + "泓" => &["hong"], + "泔" => &["gan"], + "法" => &["fa"], + "泖" => &["mao"], + "泗" => &["si"], + "泘" => &["hu"], + "泙" => &["ping"], + "泚" => &["ci"], + "泛" => &["fan"], + "泜" => &["zhi"], + "泝" => &["su"], + "泞" => &["ning"], + "泟" => &["cheng"], + "泠" => &["ling"], + "泡" => &["pao"], + "波" => &["bo","po"], + "泣" => &["qi","xie"], + "泤" => &["si"], + "泥" => &["ni"], + "泦" => &["ju"], + "泧" => &["yue"], + "注" => &["zhu"], + "泩" => &["sheng"], + "泪" => &["lei"], + "泫" => &["xuan"], + "泬" => &["xue"], + "泭" => &["fu"], + "泮" => &["pan"], + "泯" => &["min"], + "泰" => &["tai"], + "泱" => &["yang"], + "泲" => &["ji"], + "泳" => &["yong"], + "泴" => &["guan"], + "泵" => &["beng"], + "泶" => &["xue"], + "泷" => &["long","shuang"], + "泸" => &["lu"], + "泹" => &["dan"], + "泺" => &["luo","po"], + "泻" => &["xie"], + "泼" => &["po"], + "泽" => &["ze"], + "泾" => &["jing"], + "泿" => &["yin"], + "洀" => &["zhou"], + "洁" => &["jie"], + "洂" => &["yi"], + "洃" => &["hui"], + "洄" => &["hui"], + "洅" => &["zui"], + "洆" => &["cheng"], + "洇" => &["yin"], + "洈" => &["wei"], + "洉" => &["hou"], + "洊" => &["jian"], + "洋" => &["yang"], + "洌" => &["lie"], + "洍" => &["si"], + "洎" => &["ji"], + "洏" => &["er"], + "洐" => &["xing"], + "洑" => &["fu"], + "洒" => &["sa"], + "洓" => &["zi"], + "洔" => &["zhi"], + "洕" => &["yin"], + "洖" => &["wu"], + "洗" => &["xi","xian"], + "洘" => &["kao"], + "洙" => &["zhu"], + "洚" => &["jiang"], + "洛" => &["luo"], + "洝" => &["an"], + "洞" => &["dong"], + "洟" => &["yi"], + "洠" => &["mou"], + "洡" => &["lei"], + "洢" => &["yi"], + "洣" => &["mi"], + "洤" => &["quan"], + "津" => &["jin"], + "洦" => &["po"], + "洧" => &["wei"], + "洨" => &["xiao"], + "洩" => &["xie"], + "洪" => &["hong"], + "洫" => &["xu"], + "洬" => &["su"], + "洭" => &["kuang"], + "洮" => &["tao"], + "洯" => &["qie","jie"], + "洰" => &["ju"], + "洱" => &["er"], + "洲" => &["zhou"], + "洳" => &["ru"], + "洴" => &["ping"], + "洵" => &["xun"], + "洶" => &["xiong"], + "洷" => &["zhi"], + "洸" => &["guang","huang"], + "洹" => &["huan"], + "洺" => &["ming"], + "活" => &["huo"], + "洼" => &["wa"], + "洽" => &["qia","xia"], + "派" => &["pai","pa"], + "洿" => &["wu"], + "浀" => &["qu"], + "流" => &["liu"], + "浂" => &["yi"], + "浃" => &["jia"], + "浄" => &["jing"], + "浅" => &["qian","jian"], + "浆" => &["jiang"], + "浇" => &["jiao"], + "浈" => &["zhen"], + "浉" => &["shi"], + "浊" => &["zhuo"], + "测" => &["ce"], + "浍" => &["hui","kuai"], + "济" => &["ji"], + "浏" => &["liu"], + "浐" => &["chan"], + "浑" => &["hun"], + "浒" => &["hu","xu"], + "浓" => &["nong"], + "浔" => &["xun"], + "浕" => &["jin"], + "浖" => &["lie"], + "浗" => &["qiu"], + "浘" => &["wei"], + "浙" => &["zhe"], + "浚" => &["jun","xun"], + "浛" => &["han"], + "浜" => &["bang"], + "浝" => &["mang"], + "浞" => &["zhuo"], + "浟" => &["you"], + "浠" => &["xi"], + "浡" => &["bo"], + "浢" => &["dou"], + "浣" => &["huan","wan"], + "浤" => &["hong"], + "浥" => &["yi"], + "浦" => &["pu"], + "浧" => &["ying"], + "浨" => &["lan"], + "浩" => &["hao"], + "浪" => &["lang"], + "浫" => &["han"], + "浬" => &["li"], + "浭" => &["geng"], + "浮" => &["fu"], + "浯" => &["wu"], + "浰" => &["li"], + "浱" => &["chun"], + "浲" => &["feng"], + "浳" => &["yi"], + "浴" => &["yu"], + "浵" => &["tong"], + "浶" => &["lao"], + "海" => &["hai"], + "浸" => &["jin"], + "浹" => &["jia"], + "浺" => &["chong"], + "浻" => &["weng"], + "浼" => &["mei"], + "浽" => &["sui"], + "浾" => &["cheng"], + "浿" => &["pei"], + "涀" => &["xian"], + "涁" => &["shen"], + "涂" => &["tu"], + "涃" => &["kun"], + "涄" => &["pin"], + "涅" => &["nie"], + "涆" => &["han"], + "涇" => &["jing"], + "消" => &["xiao"], + "涉" => &["she"], + "涊" => &["nian"], + "涋" => &["tu"], + "涌" => &["yong","chong"], + "涍" => &["xiao"], + "涎" => &["xian"], + "涏" => &["ting"], + "涐" => &["e"], + "涑" => &["su"], + "涒" => &["tun"], + "涓" => &["juan"], + "涔" => &["cen"], + "涕" => &["ti"], + "涖" => &["li"], + "涗" => &["shui"], + "涘" => &["si"], + "涙" => &["lei"], + "涚" => &["shui"], + "涛" => &["tao"], + "涜" => &["du"], + "涝" => &["lao"], + "涞" => &["lai"], + "涟" => &["lian"], + "涠" => &["wei"], + "涡" => &["wo","guo"], + "涢" => &["yun"], + "涣" => &["huan"], + "涤" => &["di"], + "润" => &["run"], + "涧" => &["jian"], + "涨" => &["zhang"], + "涩" => &["se"], + "涪" => &["fu"], + "涫" => &["guan"], + "涬" => &["xing"], + "涭" => &["shou"], + "涮" => &["shuan"], + "涯" => &["ya"], + "涰" => &["chuo"], + "涱" => &["zhang"], + "液" => &["ye","yi"], + "涳" => &["kong"], + "涴" => &["wan"], + "涵" => &["han"], + "涶" => &["tuo"], + "涷" => &["dong"], + "涸" => &["he","hao"], + "涹" => &["wo"], + "涺" => &["ju"], + "涻" => &["gan"], + "涼" => &["liang"], + "涽" => &["hun"], + "涾" => &["ta"], + "涿" => &["zhuo"], + "淀" => &["dian"], + "淁" => &["qie"], + "淂" => &["de"], + "淃" => &["juan"], + "淄" => &["zi"], + "淅" => &["xi"], + "淆" => &["xiao","yao"], + "淇" => &["qi"], + "淈" => &["gu"], + "淉" => &["guo"], + "淊" => &["han"], + "淋" => &["lin"], + "淌" => &["tang"], + "淍" => &["zhou"], + "淎" => &["peng"], + "淏" => &["hao"], + "淐" => &["chang"], + "淑" => &["shu"], + "淒" => &["qi"], + "淓" => &["fang"], + "淔" => &["chi"], + "淕" => &["lu"], + "淖" => &["nao"], + "淗" => &["ju"], + "淘" => &["tao"], + "淙" => &["cong"], + "淚" => &["lei"], + "淛" => &["zhi"], + "淜" => &["peng"], + "淝" => &["fei"], + "淞" => &["song"], + "淟" => &["tian"], + "淠" => &["pi"], + "淡" => &["dan"], + "淢" => &["yu"], + "淣" => &["ni"], + "淤" => &["yu"], + "淥" => &["lu"], + "淦" => &["gan"], + "淧" => &["mi"], + "淨" => &["jing"], + "淩" => &["ling"], + "淪" => &["lun"], + "淫" => &["yin"], + "淬" => &["cui"], + "淭" => &["qu"], + "淮" => &["huai"], + "淯" => &["yu"], + "淰" => &["nian"], + "深" => &["shen"], + "淲" => &["piao","hu"], + "淳" => &["chun"], + "淴" => &["hu"], + "淵" => &["yuan"], + "淶" => &["lai"], + "混" => &["hun"], + "淸" => &["qing"], + "淹" => &["yan"], + "淺" => &["qian","jian"], + "添" => &["tian"], + "淼" => &["miao"], + "淽" => &["zhi"], + "淾" => &["yin"], + "淿" => &["mi"], + "渀" => &["ben"], + "渁" => &["yuan"], + "渂" => &["wen"], + "渃" => &["re"], + "渄" => &["fei"], + "清" => &["qing"], + "渆" => &["yuan"], + "渇" => &["ke"], + "済" => &["ji"], + "渉" => &["she"], + "渊" => &["yuan"], + "渋" => &["se"], + "渌" => &["lu"], + "渍" => &["zi"], + "渎" => &["du"], + "渐" => &["jian"], + "渑" => &["mian","sheng"], + "渒" => &["pi"], + "渓" => &["xi"], + "渔" => &["yu"], + "渕" => &["yuan"], + "渖" => &["shen"], + "渗" => &["shen"], + "渘" => &["rou"], + "渙" => &["huan"], + "渚" => &["zhu"], + "減" => &["jian"], + "渜" => &["nuan"], + "渝" => &["yu"], + "渞" => &["qiu"], + "渟" => &["ting"], + "渠" => &["qu"], + "渡" => &["du"], + "渢" => &["feng"], + "渣" => &["zha"], + "渤" => &["bo"], + "渥" => &["wo"], + "渦" => &["wo","guo"], + "渧" => &["di"], + "渨" => &["wei"], + "温" => &["wen"], + "渪" => &["ru"], + "渫" => &["xie"], + "測" => &["ce"], + "渭" => &["wei"], + "渮" => &["ge"], + "港" => &["gang"], + "渰" => &["yan"], + "渱" => &["hong"], + "渲" => &["xuan"], + "渳" => &["mi"], + "渴" => &["ke"], + "渵" => &["mao"], + "渶" => &["ying"], + "渷" => &["yan"], + "游" => &["you"], + "渹" => &["hong"], + "渺" => &["miao"], + "渻" => &["xing"], + "渼" => &["mei"], + "渽" => &["zai"], + "渾" => &["hun"], + "渿" => &["nai"], + "湀" => &["kui"], + "湁" => &["shi"], + "湂" => &["e"], + "湃" => &["pai"], + "湄" => &["mei"], + "湅" => &["lian"], + "湆" => &["qi"], + "湇" => &["qi"], + "湈" => &["mei"], + "湉" => &["tian"], + "湊" => &["cou"], + "湋" => &["wei"], + "湌" => &["can"], + "湍" => &["tuan"], + "湎" => &["mian"], + "湏" => &["xu"], + "湐" => &["mo"], + "湑" => &["xu"], + "湒" => &["ji"], + "湓" => &["pen"], + "湔" => &["jian"], + "湕" => &["jian"], + "湖" => &["hu"], + "湗" => &["feng"], + "湘" => &["xiang"], + "湙" => &["yi"], + "湚" => &["yin"], + "湛" => &["zhan"], + "湜" => &["shi"], + "湝" => &["jie"], + "湞" => &["zhen"], + "湟" => &["huang"], + "湠" => &["tan"], + "湡" => &["yu"], + "湢" => &["bi"], + "湣" => &["min"], + "湤" => &["shi"], + "湥" => &["tu"], + "湦" => &["sheng"], + "湧" => &["yong","chong"], + "湨" => &["ju"], + "湩" => &["zhong"], + "湫" => &["qiu","jia","jiao","jiu"], + "湬" => &["jiao"], + "湮" => &["yin","yan"], + "湯" => &["tang","shang"], + "湰" => &["long"], + "湱" => &["huo"], + "湲" => &["yuan"], + "湳" => &["nan"], + "湴" => &["ban"], + "湵" => &["you"], + "湶" => &["quan"], + "湷" => &["chui"], + "湸" => &["liang"], + "湹" => &["chan"], + "湺" => &["yan"], + "湻" => &["chun"], + "湼" => &["nie"], + "湽" => &["zi"], + "湾" => &["wan"], + "湿" => &["shi"], + "満" => &["man"], + "溁" => &["ying"], + "溂" => &["la"], + "溃" => &["kui","hui"], + "溅" => &["jian"], + "溆" => &["xu"], + "溇" => &["lou"], + "溈" => &["gui"], + "溉" => &["gai"], + "溌" => &["po"], + "溍" => &["jin"], + "溎" => &["gui"], + "溏" => &["tang"], + "源" => &["yuan"], + "溑" => &["suo"], + "溒" => &["yuan"], + "溓" => &["lian"], + "溔" => &["yao"], + "溕" => &["meng"], + "準" => &["zhun"], + "溗" => &["sheng"], + "溘" => &["ke"], + "溙" => &["tai"], + "溚" => &["ta"], + "溛" => &["wa"], + "溜" => &["liu"], + "溝" => &["gou"], + "溞" => &["sao"], + "溟" => &["ming"], + "溠" => &["zha"], + "溡" => &["shi"], + "溢" => &["yi"], + "溣" => &["lun"], + "溤" => &["ma"], + "溥" => &["pu"], + "溦" => &["wei"], + "溧" => &["li"], + "溨" => &["cai"], + "溩" => &["wu"], + "溪" => &["xi","qi"], + "溫" => &["wen"], + "溬" => &["qiang"], + "溭" => &["ce"], + "溮" => &["shi"], + "溯" => &["su"], + "溰" => &["yi"], + "溱" => &["zhen","qin"], + "溲" => &["sou"], + "溳" => &["yun"], + "溴" => &["xiu"], + "溵" => &["yin"], + "溶" => &["rong"], + "溷" => &["hun"], + "溸" => &["su"], + "溹" => &["su"], + "溺" => &["ni","niao"], + "溻" => &["ta"], + "溼" => &["shi"], + "溽" => &["ru"], + "溾" => &["wei"], + "溿" => &["pan"], + "滀" => &["chu"], + "滁" => &["chu"], + "滂" => &["pang"], + "滃" => &["weng"], + "滄" => &["cang"], + "滅" => &["mie"], + "滆" => &["he"], + "滇" => &["dian"], + "滈" => &["hao"], + "滉" => &["huang"], + "滊" => &["xi"], + "滋" => &["zi"], + "滌" => &["di"], + "滍" => &["zhi"], + "滎" => &["ying","xing"], + "滏" => &["fu"], + "滐" => &["jie"], + "滑" => &["hua","gu"], + "滒" => &["ge"], + "滓" => &["zi"], + "滔" => &["tao"], + "滕" => &["teng"], + "滖" => &["sui"], + "滗" => &["bi"], + "滘" => &["jiao"], + "滙" => &["hui"], + "滚" => &["gun"], + "滛" => &["yin"], + "滜" => &["gao"], + "滝" => &["long","shuang"], + "滞" => &["zhi"], + "滟" => &["yan"], + "滠" => &["she"], + "满" => &["man"], + "滢" => &["ying"], + "滣" => &["chun"], + "滤" => &["lu:"], + "滥" => &["lan"], + "滦" => &["luan"], + "滧" => &["xiao"], + "滨" => &["bin"], + "滩" => &["tan"], + "滪" => &["yu"], + "滫" => &["xiu"], + "滬" => &["hu"], + "滭" => &["bi"], + "滮" => &["biao"], + "滯" => &["zhi"], + "滰" => &["jiang"], + "滱" => &["kou"], + "滲" => &["shen"], + "滳" => &["shang"], + "滴" => &["di"], + "滵" => &["mi"], + "滶" => &["ao"], + "滷" => &["lu"], + "滸" => &["hu","xu"], + "滹" => &["hu"], + "滺" => &["you"], + "滻" => &["chan"], + "滼" => &["fan"], + "滽" => &["yong"], + "滾" => &["gun"], + "滿" => &["man"], + "漀" => &["qing"], + "漁" => &["yu"], + "漂" => &["piao"], + "漃" => &["ji"], + "漄" => &["ya"], + "漅" => &["jiao"], + "漆" => &["qi","qu","xi"], + "漇" => &["xi"], + "漈" => &["ji"], + "漉" => &["lu"], + "漊" => &["lu:","lou"], + "漋" => &["long"], + "漌" => &["jin"], + "漍" => &["guo"], + "漎" => &["cong"], + "漏" => &["lou"], + "漐" => &["zhi"], + "漑" => &["gai"], + "漒" => &["qiang"], + "漓" => &["li"], + "演" => &["yan"], + "漕" => &["cao"], + "漖" => &["jiao"], + "漗" => &["cong"], + "漘" => &["chun"], + "漙" => &["tuan"], + "漚" => &["ou"], + "漛" => &["teng"], + "漜" => &["ye"], + "漝" => &["xi"], + "漞" => &["mi"], + "漟" => &["tang"], + "漠" => &["mo"], + "漡" => &["shang"], + "漢" => &["han"], + "漣" => &["lian"], + "漤" => &["lan"], + "漥" => &["wa"], + "漦" => &["li"], + "漧" => &["qian"], + "漨" => &["feng"], + "漩" => &["xuan"], + "漪" => &["yi"], + "漫" => &["man"], + "漬" => &["zi"], + "漭" => &["mang"], + "漮" => &["kang"], + "漯" => &["luo","ta"], + "漰" => &["peng"], + "漱" => &["shu"], + "漲" => &["zhang"], + "漳" => &["zhang"], + "漴" => &["chong"], + "漵" => &["xu"], + "漶" => &["huan"], + "漷" => &["kuo","huo"], + "漸" => &["jian"], + "漹" => &["yan"], + "漺" => &["chuang","shuang"], + "漻" => &["liao"], + "漼" => &["cui"], + "漽" => &["ti"], + "漾" => &["yang"], + "漿" => &["jiang"], + "潀" => &["cong"], + "潁" => &["ying"], + "潂" => &["hong"], + "潃" => &["xiu"], + "潄" => &["shu"], + "潅" => &["guan"], + "潆" => &["ying"], + "潇" => &["xiao"], + "潊" => &["xu"], + "潋" => &["lian"], + "潌" => &["zhi"], + "潍" => &["wei"], + "潎" => &["pi"], + "潏" => &["yu"], + "潐" => &["jiao"], + "潑" => &["po"], + "潒" => &["xiang"], + "潓" => &["hui"], + "潔" => &["jie"], + "潕" => &["wu"], + "潖" => &["pa"], + "潗" => &["ji"], + "潘" => &["pan"], + "潙" => &["wei"], + "潚" => &["xiao","su"], + "潛" => &["qian"], + "潜" => &["qian"], + "潝" => &["xi"], + "潞" => &["lu"], + "潟" => &["xi"], + "潠" => &["sun"], + "潡" => &["dun"], + "潢" => &["huang"], + "潣" => &["min"], + "潤" => &["run"], + "潥" => &["su"], + "潦" => &["liao","lao"], + "潧" => &["zhen"], + "潨" => &["zhong"], + "潩" => &["yi"], + "潪" => &["di"], + "潫" => &["wan"], + "潬" => &["dan"], + "潭" => &["tan"], + "潮" => &["chao"], + "潯" => &["xun"], + "潰" => &["kui","hui"], + "潲" => &["shao"], + "潳" => &["tu"], + "潴" => &["zhu"], + "潵" => &["sa"], + "潶" => &["hei"], + "潷" => &["bi"], + "潸" => &["shan"], + "潹" => &["chan"], + "潺" => &["chan"], + "潻" => &["shu"], + "潼" => &["tong"], + "潽" => &["pu"], + "潾" => &["lin"], + "潿" => &["wei"], + "澀" => &["se"], + "澁" => &["se"], + "澂" => &["cheng","deng"], + "澃" => &["jiong"], + "澄" => &["cheng","deng"], + "澅" => &["hua"], + "澆" => &["jiao"], + "澇" => &["lao"], + "澈" => &["che"], + "澉" => &["gan"], + "澊" => &["cun"], + "澋" => &["heng"], + "澌" => &["si"], + "澍" => &["shu"], + "澎" => &["peng"], + "澏" => &["han"], + "澐" => &["yun"], + "澑" => &["liu"], + "澒" => &["hong"], + "澓" => &["fu"], + "澔" => &["hao"], + "澕" => &["he"], + "澖" => &["xian"], + "澗" => &["jian"], + "澘" => &["shan"], + "澙" => &["xi"], + "澚" => &["ao"], + "澛" => &["lu"], + "澜" => &["lan"], + "澞" => &["yu"], + "澟" => &["lin"], + "澠" => &["min","mian","sheng"], + "澡" => &["zao"], + "澢" => &["dang"], + "澣" => &["huan"], + "澤" => &["ze"], + "澥" => &["xie"], + "澦" => &["yu"], + "澧" => &["li"], + "澨" => &["shi"], + "澩" => &["xue"], + "澪" => &["ling"], + "澫" => &["man"], + "澬" => &["zi"], + "澭" => &["yong"], + "澮" => &["kuai","hui"], + "澯" => &["can"], + "澰" => &["lian"], + "澱" => &["dian"], + "澲" => &["ye"], + "澳" => &["ao"], + "澴" => &["huan"], + "澵" => &["lian"], + "澶" => &["chan"], + "澷" => &["man"], + "澸" => &["dan"], + "澹" => &["dan","tan"], + "澺" => &["yi"], + "澻" => &["sui"], + "澼" => &["pi"], + "澽" => &["ju"], + "澾" => &["ta"], + "澿" => &["qin"], + "激" => &["ji"], + "濁" => &["zhuo"], + "濂" => &["lian"], + "濃" => &["nong"], + "濄" => &["guo"], + "濅" => &["jin"], + "濆" => &["fen"], + "濇" => &["se"], + "濈" => &["ji"], + "濉" => &["sui"], + "濊" => &["hui"], + "濋" => &["chu"], + "濌" => &["ta"], + "濍" => &["song"], + "濎" => &["ding"], + "濏" => &["se"], + "濐" => &["zhu"], + "濑" => &["lai"], + "濒" => &["bin"], + "濓" => &["lian"], + "濔" => &["mi"], + "濕" => &["shi"], + "濖" => &["shu"], + "濗" => &["mi"], + "濘" => &["ning","neng"], + "濙" => &["ying"], + "濚" => &["ying","xing"], + "濛" => &["meng"], + "濜" => &["jin"], + "濝" => &["qi"], + "濞" => &["bi"], + "濟" => &["ji"], + "濠" => &["hao"], + "濡" => &["ru"], + "濢" => &["zui","cui"], + "濣" => &["wo"], + "濤" => &["tao"], + "濥" => &["yin"], + "濦" => &["yin"], + "濧" => &["dui"], + "濨" => &["ci"], + "濩" => &["huo"], + "濪" => &["jing"], + "濫" => &["lan"], + "濬" => &["jun"], + "濭" => &["ai"], + "濮" => &["pu"], + "濯" => &["zhuo"], + "濰" => &["wei"], + "濱" => &["bin"], + "濲" => &["gu"], + "濳" => &["qian"], + "濴" => &["xing"], + "濵" => &["bin"], + "濶" => &["kuo"], + "濷" => &["fei"], + "濹" => &["bin"], + "濺" => &["jian"], + "濻" => &["dui","wei"], + "濼" => &["luo"], + "濽" => &["luo"], + "濾" => &["lu:"], + "濿" => &["li"], + "瀀" => &["you"], + "瀁" => &["yang"], + "瀂" => &["lu"], + "瀃" => &["si"], + "瀄" => &["jie"], + "瀅" => &["ying"], + "瀆" => &["du"], + "瀇" => &["wang"], + "瀈" => &["hui"], + "瀉" => &["xie"], + "瀊" => &["pan"], + "瀋" => &["shen"], + "瀌" => &["biao"], + "瀍" => &["chan"], + "瀎" => &["mie"], + "瀏" => &["liu"], + "瀐" => &["jian"], + "瀑" => &["pu","bao"], + "瀒" => &["se"], + "瀓" => &["cheng"], + "瀔" => &["gu"], + "瀕" => &["bin","pin"], + "瀖" => &["huo"], + "瀗" => &["xian"], + "瀘" => &["lu"], + "瀙" => &["qin"], + "瀚" => &["han"], + "瀛" => &["ying"], + "瀜" => &["rong"], + "瀝" => &["li"], + "瀞" => &["jing"], + "瀟" => &["xiao"], + "瀠" => &["ying"], + "瀡" => &["sui"], + "瀢" => &["wei"], + "瀣" => &["xie"], + "瀤" => &["huai"], + "瀥" => &["hao"], + "瀦" => &["zhu"], + "瀧" => &["long","shuang"], + "瀨" => &["lai"], + "瀩" => &["dui"], + "瀪" => &["fan"], + "瀫" => &["hu"], + "瀬" => &["lai"], + "瀯" => &["ying"], + "瀰" => &["mi"], + "瀱" => &["ji"], + "瀲" => &["lian"], + "瀳" => &["jian"], + "瀴" => &["ying"], + "瀵" => &["fen"], + "瀶" => &["lin"], + "瀷" => &["yi"], + "瀸" => &["jian"], + "瀹" => &["yue"], + "瀺" => &["chan"], + "瀻" => &["dai"], + "瀼" => &["rang"], + "瀽" => &["jian"], + "瀾" => &["lan"], + "瀿" => &["fan"], + "灀" => &["shuang"], + "灁" => &["yuan"], + "灂" => &["zhuo"], + "灃" => &["feng"], + "灄" => &["she"], + "灅" => &["lei"], + "灆" => &["lan"], + "灇" => &["cong"], + "灈" => &["qu"], + "灉" => &["yong"], + "灊" => &["qian"], + "灋" => &["fa"], + "灌" => &["guan"], + "灍" => &["que"], + "灎" => &["yan"], + "灏" => &["hao"], + "灑" => &["sa"], + "灒" => &["zan"], + "灓" => &["luan"], + "灔" => &["yan"], + "灕" => &["li"], + "灖" => &["mi"], + "灗" => &["dan"], + "灘" => &["tan"], + "灙" => &["dang"], + "灚" => &["jiao"], + "灛" => &["chan"], + "灝" => &["hao"], + "灞" => &["ba"], + "灟" => &["zhu"], + "灠" => &["lan"], + "灡" => &["lan"], + "灢" => &["nang"], + "灣" => &["wan"], + "灤" => &["luan"], + "灥" => &["quan"], + "灦" => &["xian"], + "灧" => &["yan"], + "灨" => &["gan"], + "灩" => &["yan"], + "灪" => &["yu"], + "火" => &["huo"], + "灬" => &["biao"], + "灭" => &["mie"], + "灮" => &["guang"], + "灯" => &["deng"], + "灰" => &["hui"], + "灱" => &["xiao"], + "灲" => &["xiao"], + "灴" => &["hong"], + "灵" => &["ling"], + "灶" => &["zao"], + "灷" => &["zhuan"], + "灸" => &["jiu"], + "灹" => &["zha"], + "灺" => &["xie"], + "灻" => &["chi"], + "灼" => &["zhuo"], + "災" => &["zai"], + "灾" => &["zai"], + "灿" => &["can"], + "炀" => &["yang"], + "炁" => &["qi"], + "炂" => &["zhong"], + "炃" => &["fen"], + "炄" => &["niu"], + "炅" => &["gui","jiong"], + "炆" => &["wen"], + "炇" => &["po"], + "炈" => &["yi"], + "炉" => &["lu"], + "炊" => &["chui"], + "炋" => &["pi"], + "炌" => &["kai"], + "炍" => &["pan"], + "炎" => &["yan"], + "炏" => &["kai"], + "炐" => &["pang"], + "炑" => &["mu"], + "炒" => &["chao"], + "炓" => &["liao"], + "炔" => &["gui","que"], + "炕" => &["kang"], + "炖" => &["dun"], + "炗" => &["guang"], + "炘" => &["xin"], + "炙" => &["zhi"], + "炚" => &["guang"], + "炛" => &["xin"], + "炜" => &["wei"], + "炝" => &["qiang"], + "炞" => &["bian"], + "炟" => &["da"], + "炠" => &["xia"], + "炡" => &["zheng"], + "炢" => &["zhu"], + "炣" => &["ke"], + "炤" => &["zhao"], + "炥" => &["fu"], + "炦" => &["ba"], + "炧" => &["duo"], + "炨" => &["duo"], + "炩" => &["ling"], + "炪" => &["zhuo"], + "炫" => &["xuan"], + "炬" => &["ju"], + "炭" => &["tan"], + "炮" => &["pao","bao"], + "炯" => &["jiong"], + "炰" => &["pao"], + "炱" => &["tai"], + "炲" => &["tai"], + "炳" => &["bing"], + "炴" => &["yang"], + "炵" => &["tong","dong"], + "炶" => &["han"], + "炷" => &["zhu"], + "炸" => &["zha"], + "点" => &["dian"], + "為" => &["wei"], + "炻" => &["shi"], + "炼" => &["lian"], + "炽" => &["chi"], + "炾" => &["ping"], + "烀" => &["hu"], + "烁" => &["shuo"], + "烂" => &["lan"], + "烃" => &["ting"], + "烄" => &["jiao"], + "烅" => &["xu"], + "烆" => &["xing"], + "烇" => &["quan"], + "烈" => &["lie"], + "烉" => &["huan"], + "烊" => &["yang"], + "烋" => &["xiao"], + "烌" => &["xiu"], + "烍" => &["xian"], + "烎" => &["yin"], + "烏" => &["wu"], + "烐" => &["zhou"], + "烑" => &["yao"], + "烒" => &["shi"], + "烓" => &["wei"], + "烔" => &["tong"], + "烕" => &["tong"], + "烖" => &["zai"], + "烗" => &["kai"], + "烘" => &["hong"], + "烙" => &["lao","luo"], + "烚" => &["xia"], + "烛" => &["zhu"], + "烜" => &["xuan"], + "烝" => &["zheng"], + "烞" => &["po"], + "烟" => &["yan","yin"], + "烠" => &["hui"], + "烡" => &["guang"], + "烢" => &["zhe"], + "烣" => &["hui"], + "烤" => &["kao"], + "烦" => &["fan"], + "烧" => &["shao"], + "烨" => &["ye"], + "烩" => &["hui"], + "烫" => &["tang"], + "烬" => &["jin"], + "热" => &["re"], + "烯" => &["xi"], + "烰" => &["fu"], + "烱" => &["jiong"], + "烲" => &["che"], + "烳" => &["pu"], + "烴" => &["jing","ting"], + "烵" => &["zhuo"], + "烶" => &["ting"], + "烷" => &["wan"], + "烸" => &["hai"], + "烹" => &["peng"], + "烺" => &["lang"], + "烻" => &["shan"], + "烼" => &["hu"], + "烽" => &["feng"], + "烾" => &["chi"], + "烿" => &["rong"], + "焀" => &["hu"], + "焂" => &["shu"], + "焃" => &["lang"], + "焄" => &["xun"], + "焅" => &["xun"], + "焆" => &["jue"], + "焇" => &["xiao"], + "焈" => &["xi"], + "焉" => &["yan"], + "焊" => &["han"], + "焋" => &["zhuang"], + "焌" => &["qu","jun"], + "焍" => &["di","ti"], + "焎" => &["xie"], + "焏" => &["qi"], + "焐" => &["wu"], + "焓" => &["han"], + "焔" => &["yan"], + "焕" => &["huan"], + "焖" => &["men"], + "焗" => &["ju"], + "焘" => &["dao","tao"], + "焙" => &["bei"], + "焚" => &["fen"], + "焛" => &["lin"], + "焜" => &["kun"], + "焝" => &["hun"], + "焞" => &["chun"], + "焟" => &["xi"], + "焠" => &["cui"], + "無" => &["wu","mo"], + "焢" => &["hong"], + "焣" => &["ju"], + "焤" => &["fu"], + "焥" => &["yue"], + "焦" => &["jiao"], + "焧" => &["cong"], + "焨" => &["feng"], + "焩" => &["ping"], + "焪" => &["qiong"], + "焫" => &["cui"], + "焬" => &["xi"], + "焭" => &["qiong"], + "焮" => &["xin"], + "焯" => &["zhuo","chao"], + "焰" => &["yan"], + "焱" => &["yan"], + "焲" => &["yi"], + "焳" => &["jue"], + "焴" => &["yu"], + "焵" => &["gang"], + "然" => &["ran"], + "焷" => &["pi"], + "焸" => &["yan"], + "焺" => &["sheng"], + "焻" => &["chang"], + "焼" => &["shao"], + "煁" => &["chen"], + "煂" => &["he"], + "煃" => &["kui"], + "煄" => &["zhong"], + "煅" => &["duan"], + "煆" => &["ya"], + "煇" => &["hui"], + "煈" => &["feng"], + "煉" => &["lian"], + "煊" => &["xuan"], + "煋" => &["xing"], + "煌" => &["huang"], + "煍" => &["jiao"], + "煎" => &["jian"], + "煏" => &["bi"], + "煐" => &["ying"], + "煑" => &["zhu"], + "煒" => &["wei"], + "煓" => &["tuan"], + "煔" => &["tian"], + "煕" => &["xi"], + "煖" => &["nuan","xuan"], + "煗" => &["nuan"], + "煘" => &["chan"], + "煙" => &["yan"], + "煚" => &["jiong"], + "煛" => &["jiong"], + "煜" => &["yu"], + "煝" => &["mei"], + "煞" => &["sha"], + "煟" => &["wu"], + "煠" => &["ye"], + "煡" => &["xin"], + "煢" => &["qiong"], + "煣" => &["rou"], + "煤" => &["mei"], + "煥" => &["huan"], + "煦" => &["xu"], + "照" => &["zhao"], + "煨" => &["wei"], + "煩" => &["fan"], + "煪" => &["qiu"], + "煫" => &["sui"], + "煬" => &["yang"], + "煭" => &["lie"], + "煮" => &["zhu"], + "煰" => &["gao"], + "煱" => &["gua"], + "煲" => &["bao"], + "煳" => &["hu"], + "煴" => &["yun"], + "煵" => &["xia"], + "煸" => &["bian"], + "煹" => &["wei"], + "煺" => &["tui"], + "煻" => &["tang"], + "煼" => &["chao"], + "煽" => &["shan"], + "煾" => &["yun"], + "煿" => &["bo"], + "熀" => &["huang"], + "熁" => &["xie"], + "熂" => &["xi"], + "熃" => &["wu"], + "熄" => &["xi"], + "熅" => &["yun"], + "熆" => &["he"], + "熇" => &["he"], + "熈" => &["xi"], + "熉" => &["yun"], + "熊" => &["xiong"], + "熋" => &["nai"], + "熌" => &["kao"], + "熎" => &["yao"], + "熏" => &["xun"], + "熐" => &["ming"], + "熑" => &["lian"], + "熒" => &["ying"], + "熓" => &["wen"], + "熔" => &["rong"], + "熗" => &["qiang"], + "熘" => &["liu"], + "熙" => &["xi"], + "熚" => &["bi"], + "熛" => &["biao"], + "熜" => &["cong"], + "熝" => &["lu"], + "熞" => &["jian"], + "熟" => &["shu","shou"], + "熠" => &["yi"], + "熡" => &["lou"], + "熢" => &["feng"], + "熣" => &["sui"], + "熤" => &["yi"], + "熥" => &["teng"], + "熦" => &["jue"], + "熧" => &["zong"], + "熨" => &["yun","yu"], + "熩" => &["hu"], + "熪" => &["yi"], + "熫" => &["zhi"], + "熬" => &["ao"], + "熭" => &["wei"], + "熮" => &["liao"], + "熯" => &["han"], + "熰" => &["ou"], + "熱" => &["re"], + "熲" => &["jiong"], + "熳" => &["man"], + "熵" => &["shang"], + "熶" => &["cuan"], + "熷" => &["zeng"], + "熸" => &["jian"], + "熹" => &["xi"], + "熺" => &["xi"], + "熻" => &["xi"], + "熼" => &["yi"], + "熽" => &["xiao"], + "熾" => &["chi"], + "熿" => &["huang"], + "燀" => &["chan"], + "燁" => &["ye"], + "燂" => &["qian"], + "燃" => &["ran"], + "燄" => &["yan"], + "燅" => &["xian"], + "燆" => &["qiao"], + "燇" => &["zun"], + "燈" => &["deng"], + "燉" => &["dun"], + "燊" => &["shen"], + "燋" => &["jiao"], + "燌" => &["fen"], + "燍" => &["si"], + "燎" => &["liao"], + "燏" => &["yu"], + "燐" => &["lin"], + "燑" => &["tong"], + "燒" => &["shao"], + "燓" => &["fen"], + "燔" => &["fan"], + "燕" => &["yan"], + "燖" => &["xun"], + "燗" => &["lan"], + "燘" => &["mei"], + "燙" => &["tang"], + "燚" => &["yi"], + "燛" => &["jing"], + "燜" => &["men"], + "營" => &["ying"], + "燠" => &["yu"], + "燡" => &["yi"], + "燢" => &["xue"], + "燣" => &["lan"], + "燤" => &["tai"], + "燥" => &["zao"], + "燦" => &["can"], + "燧" => &["sui"], + "燨" => &["xi"], + "燩" => &["que"], + "燪" => &["cong"], + "燫" => &["lian"], + "燬" => &["hui"], + "燭" => &["zhu"], + "燮" => &["xie"], + "燯" => &["ling"], + "燰" => &["wei"], + "燱" => &["yi"], + "燲" => &["xie"], + "燳" => &["zhao"], + "燴" => &["hui"], + "燷" => &["lan"], + "燸" => &["ru"], + "燹" => &["xian"], + "燺" => &["kao"], + "燻" => &["xun"], + "燼" => &["jin"], + "燽" => &["chou"], + "燾" => &["dao","tao"], + "燿" => &["yao"], + "爀" => &["he"], + "爁" => &["lan"], + "爂" => &["biao"], + "爃" => &["rong"], + "爄" => &["li"], + "爅" => &["mo"], + "爆" => &["bao"], + "爇" => &["ruo"], + "爈" => &["di"], + "爉" => &["lu:"], + "爊" => &["ao"], + "爋" => &["xun"], + "爌" => &["kuang"], + "爍" => &["shuo"], + "爏" => &["li"], + "爐" => &["lu"], + "爑" => &["jue"], + "爒" => &["liao"], + "爓" => &["yan"], + "爔" => &["xi"], + "爕" => &["xie"], + "爖" => &["long"], + "爗" => &["yan"], + "爙" => &["rang","shang"], + "爚" => &["yue"], + "爛" => &["lan"], + "爜" => &["cong"], + "爝" => &["jue","jiao"], + "爞" => &["tong"], + "爟" => &["guan"], + "爡" => &["che"], + "爢" => &["mi"], + "爣" => &["tang"], + "爤" => &["lan"], + "爥" => &["zhu"], + "爦" => &["lan"], + "爧" => &["ling"], + "爨" => &["cuan"], + "爩" => &["yu"], + "爪" => &["zhua","zhao"], + "爫" => &["lan"], + "爬" => &["pa"], + "爭" => &["zheng"], + "爮" => &["pao"], + "爯" => &["zhao"], + "爰" => &["yuan"], + "爱" => &["ai"], + "爲" => &["wei"], + "爴" => &["jue"], + "爵" => &["jue"], + "父" => &["fu"], + "爷" => &["ye"], + "爸" => &["ba"], + "爹" => &["die"], + "爺" => &["ye"], + "爻" => &["yao"], + "爼" => &["zu"], + "爽" => &["shuang"], + "爾" => &["er"], + "爿" => &["pan","qiang","ban"], + "牀" => &["chuan"], + "牁" => &["ke"], + "牂" => &["zang"], + "牃" => &["zang"], + "牄" => &["qiang"], + "牅" => &["die"], + "牆" => &["qiang"], + "片" => &["pian"], + "版" => &["ban"], + "牉" => &["pan"], + "牊" => &["shao"], + "牋" => &["jian"], + "牌" => &["pai"], + "牍" => &["du"], + "牎" => &["yong"], + "牏" => &["tou"], + "牐" => &["tou"], + "牑" => &["bian"], + "牒" => &["die"], + "牓" => &["bang"], + "牔" => &["bo"], + "牕" => &["bang"], + "牖" => &["you"], + "牘" => &["du"], + "牙" => &["ya"], + "牚" => &["cheng"], + "牛" => &["niu"], + "牜" => &["cheng"], + "牝" => &["pin"], + "牞" => &["jiu"], + "牟" => &["mou","mu"], + "牠" => &["ta"], + "牡" => &["mu"], + "牢" => &["lao"], + "牣" => &["ren"], + "牤" => &["mang"], + "牥" => &["fang"], + "牦" => &["mao"], + "牧" => &["mu"], + "牨" => &["ren"], + "物" => &["wu"], + "牪" => &["yan"], + "牫" => &["fa"], + "牬" => &["bei"], + "牭" => &["si"], + "牮" => &["jian"], + "牯" => &["gu"], + "牰" => &["you"], + "牱" => &["gu"], + "牲" => &["sheng"], + "牳" => &["mu"], + "牴" => &["di"], + "牵" => &["qian"], + "牶" => &["quan"], + "牷" => &["quan"], + "牸" => &["zi"], + "特" => &["te"], + "牺" => &["xi"], + "牻" => &["mang"], + "牼" => &["keng"], + "牽" => &["qian"], + "牾" => &["wu"], + "牿" => &["gu"], + "犀" => &["xi"], + "犁" => &["li"], + "犂" => &["li"], + "犃" => &["pou"], + "犄" => &["ji"], + "犅" => &["gang"], + "犆" => &["zhi"], + "犇" => &["ben"], + "犈" => &["quan"], + "犉" => &["run"], + "犊" => &["du"], + "犋" => &["ju"], + "犌" => &["jia"], + "犍" => &["jian","qian"], + "犎" => &["feng"], + "犏" => &["pian"], + "犐" => &["ke"], + "犑" => &["ju"], + "犒" => &["kao","di"], + "犓" => &["chu"], + "犔" => &["xi"], + "犕" => &["bei"], + "犖" => &["luo"], + "犗" => &["jie"], + "犘" => &["ma"], + "犙" => &["san"], + "犚" => &["wei"], + "犛" => &["li"], + "犜" => &["dun"], + "犝" => &["tong"], + "犞" => &["se"], + "犟" => &["jiang"], + "犠" => &["xi"], + "犡" => &["li"], + "犢" => &["du"], + "犣" => &["lie"], + "犤" => &["pi"], + "犥" => &["piao"], + "犦" => &["bao"], + "犧" => &["xi"], + "犨" => &["chou"], + "犩" => &["wei"], + "犪" => &["kui"], + "犫" => &["chou"], + "犬" => &["quan"], + "犭" => &["quan"], + "犮" => &["ba"], + "犯" => &["fan"], + "犰" => &["qiu"], + "犱" => &["bo"], + "犲" => &["chai"], + "犳" => &["chuo"], + "犴" => &["an","han"], + "犵" => &["jie"], + "状" => &["zhuang"], + "犷" => &["guang"], + "犸" => &["ma"], + "犹" => &["you"], + "犺" => &["kang"], + "犻" => &["bo"], + "犼" => &["hou"], + "犽" => &["ya"], + "犾" => &["han"], + "犿" => &["huan"], + "狀" => &["zhuang"], + "狁" => &["yun"], + "狂" => &["kuang"], + "狃" => &["niu"], + "狄" => &["di"], + "狅" => &["qing"], + "狆" => &["zhong"], + "狇" => &["yun"], + "狈" => &["bei"], + "狉" => &["pi"], + "狊" => &["ju"], + "狋" => &["ni"], + "狌" => &["sheng"], + "狍" => &["pao"], + "狎" => &["xia"], + "狏" => &["tuo"], + "狐" => &["hu"], + "狑" => &["ling"], + "狒" => &["fei"], + "狓" => &["pi"], + "狔" => &["ni"], + "狕" => &["sheng"], + "狖" => &["you"], + "狗" => &["gou"], + "狘" => &["yue"], + "狙" => &["ju"], + "狚" => &["dan"], + "狛" => &["bo"], + "狜" => &["gu"], + "狝" => &["xian"], + "狞" => &["ning"], + "狟" => &["huan"], + "狠" => &["hen"], + "狡" => &["jiao","jia"], + "狢" => &["he","hao","mo"], + "狣" => &["zhao"], + "狤" => &["ji"], + "狥" => &["huan"], + "狦" => &["shan"], + "狧" => &["ta"], + "狨" => &["rong"], + "狩" => &["shou"], + "狪" => &["tong"], + "狫" => &["lao"], + "独" => &["du"], + "狭" => &["xia"], + "狮" => &["shi"], + "狯" => &["kuai"], + "狰" => &["zheng"], + "狱" => &["yu"], + "狲" => &["sun"], + "狳" => &["yu"], + "狴" => &["bi"], + "狵" => &["mang"], + "狶" => &["xi"], + "狷" => &["juan"], + "狸" => &["li"], + "狹" => &["xia"], + "狺" => &["yin"], + "狻" => &["suan"], + "狼" => &["lang"], + "狽" => &["bei"], + "狾" => &["zhi"], + "狿" => &["yan"], + "猀" => &["sha"], + "猁" => &["li"], + "猂" => &["zhi"], + "猃" => &["xian"], + "猄" => &["jing"], + "猅" => &["han"], + "猆" => &["fei"], + "猇" => &["yao"], + "猈" => &["ba","pi"], + "猉" => &["qi"], + "猊" => &["ni"], + "猋" => &["biao"], + "猌" => &["yin"], + "猍" => &["li"], + "猎" => &["lie"], + "猏" => &["jian"], + "猐" => &["qiang"], + "猑" => &["kun"], + "猒" => &["yan"], + "猓" => &["guo"], + "猔" => &["zong"], + "猕" => &["mi"], + "猖" => &["chang"], + "猗" => &["yi"], + "猘" => &["zhi"], + "猙" => &["zheng"], + "猚" => &["ya"], + "猛" => &["meng"], + "猜" => &["cai"], + "猝" => &["cu"], + "猞" => &["she"], + "猟" => &["lie"], + "猡" => &["luo"], + "猢" => &["hu"], + "猣" => &["zong"], + "猤" => &["hu"], + "猥" => &["wei"], + "猦" => &["feng"], + "猧" => &["wo"], + "猨" => &["yuan"], + "猩" => &["xing"], + "猪" => &["zhu"], + "猫" => &["mao"], + "猬" => &["wei"], + "猭" => &["yuan"], + "献" => &["xian"], + "猯" => &["tuan"], + "猰" => &["ya"], + "猱" => &["nao"], + "猲" => &["xie","he"], + "猳" => &["jia"], + "猴" => &["hou"], + "猵" => &["bian"], + "猶" => &["you"], + "猷" => &["you"], + "猸" => &["mei"], + "猹" => &["cha"], + "猺" => &["yao"], + "猻" => &["sun"], + "猼" => &["bo"], + "猽" => &["ming"], + "猾" => &["hua"], + "猿" => &["yuan"], + "獀" => &["sou"], + "獁" => &["ma"], + "獂" => &["yuan"], + "獃" => &["dai"], + "獄" => &["yu"], + "獅" => &["shi"], + "獆" => &["hao"], + "獈" => &["yi"], + "獉" => &["zhen"], + "獊" => &["chuang"], + "獋" => &["hao"], + "獌" => &["man"], + "獍" => &["jing"], + "獎" => &["jiang"], + "獏" => &["mo"], + "獐" => &["zhang"], + "獑" => &["chan"], + "獒" => &["ao"], + "獓" => &["ao"], + "獔" => &["hao"], + "獕" => &["cui"], + "獖" => &["ben"], + "獗" => &["jue"], + "獘" => &["bi"], + "獙" => &["bi"], + "獚" => &["huang"], + "獛" => &["bu"], + "獜" => &["lin"], + "獝" => &["yu"], + "獞" => &["tong"], + "獟" => &["yao"], + "獠" => &["liao"], + "獡" => &["shuo"], + "獢" => &["xiao"], + "獣" => &["shou"], + "獥" => &["xi"], + "獦" => &["ge"], + "獧" => &["juan"], + "獨" => &["du"], + "獩" => &["hui"], + "獪" => &["kuai"], + "獫" => &["xian"], + "獬" => &["xie"], + "獭" => &["ta"], + "獮" => &["xian"], + "獯" => &["xun"], + "獰" => &["ning"], + "獱" => &["bian"], + "獲" => &["huo"], + "獳" => &["nou"], + "獴" => &["meng"], + "獵" => &["lie"], + "獶" => &["nao"], + "獷" => &["guang"], + "獸" => &["shou"], + "獹" => &["lu"], + "獺" => &["ta"], + "獻" => &["xian"], + "獼" => &["mi"], + "獽" => &["rang"], + "獾" => &["huan"], + "獿" => &["nao"], + "玀" => &["luo"], + "玁" => &["xian"], + "玂" => &["qi"], + "玃" => &["qu"], + "玄" => &["xuan"], + "玅" => &["miao"], + "玆" => &["zi"], + "率" => &["lu:","shuai","shuo"], + "玈" => &["lu"], + "玉" => &["yu"], + "玊" => &["su"], + "王" => &["wang"], + "玌" => &["qiu"], + "玍" => &["ga"], + "玎" => &["ding"], + "玏" => &["le"], + "玐" => &["ba"], + "玑" => &["ji"], + "玒" => &["hong"], + "玓" => &["di"], + "玔" => &["chuan"], + "玕" => &["gan"], + "玖" => &["jiu"], + "玗" => &["yu"], + "玘" => &["qi"], + "玙" => &["yu"], + "玚" => &["yang","chang"], + "玛" => &["ma"], + "玜" => &["hong"], + "玝" => &["wu"], + "玞" => &["fu"], + "玟" => &["min","wen"], + "玠" => &["jie"], + "玡" => &["ya"], + "玢" => &["bin","fen"], + "玣" => &["bian"], + "玤" => &["beng"], + "玥" => &["yue"], + "玦" => &["jue"], + "玧" => &["yun"], + "玨" => &["jue"], + "玩" => &["wan"], + "玪" => &["jian"], + "玫" => &["mei"], + "玬" => &["dan"], + "玭" => &["pi"], + "玮" => &["wei"], + "环" => &["huan"], + "现" => &["xian"], + "玱" => &["qiang"], + "玲" => &["ling"], + "玳" => &["dai"], + "玴" => &["yi"], + "玵" => &["an"], + "玶" => &["ping"], + "玷" => &["dian"], + "玸" => &["fu"], + "玹" => &["xuan"], + "玺" => &["xi"], + "玻" => &["bo"], + "玼" => &["ci"], + "玽" => &["gou"], + "玾" => &["jia"], + "玿" => &["shao"], + "珀" => &["po"], + "珁" => &["ci"], + "珂" => &["ke"], + "珃" => &["ran"], + "珄" => &["sheng"], + "珅" => &["shen"], + "珆" => &["yi"], + "珇" => &["zu"], + "珈" => &["jia"], + "珉" => &["min"], + "珊" => &["shan"], + "珋" => &["liu"], + "珌" => &["bi"], + "珍" => &["zhen"], + "珎" => &["zhen"], + "珏" => &["jue"], + "珐" => &["fa"], + "珑" => &["long"], + "珒" => &["jin"], + "珓" => &["jiao"], + "珔" => &["jian"], + "珕" => &["li"], + "珖" => &["guang"], + "珗" => &["xian"], + "珘" => &["zhou"], + "珙" => &["gong"], + "珚" => &["yan"], + "珛" => &["xiu"], + "珜" => &["yang"], + "珝" => &["xu"], + "珞" => &["luo"], + "珟" => &["su"], + "珠" => &["zhu"], + "珡" => &["qin"], + "珢" => &["ken"], + "珣" => &["xun"], + "珤" => &["bao"], + "珥" => &["er"], + "珦" => &["xiang"], + "珧" => &["yao"], + "珨" => &["xia"], + "珩" => &["heng","hang"], + "珪" => &["gui"], + "珫" => &["chong"], + "珬" => &["xu"], + "班" => &["ban"], + "珮" => &["pei"], + "珰" => &["dang"], + "珱" => &["ying"], + "珲" => &["hun","hui"], + "珳" => &["wen"], + "珴" => &["e"], + "珵" => &["cheng"], + "珶" => &["ti","di"], + "珷" => &["wu"], + "珸" => &["wu"], + "珹" => &["cheng"], + "珺" => &["jun"], + "珻" => &["mei"], + "珼" => &["bei"], + "珽" => &["ting"], + "現" => &["xian"], + "珿" => &["chuo"], + "琀" => &["han"], + "琁" => &["xuan"], + "琂" => &["yan"], + "球" => &["qiu"], + "琄" => &["quan"], + "琅" => &["lang"], + "理" => &["li"], + "琇" => &["xiu"], + "琈" => &["fu"], + "琉" => &["liu"], + "琊" => &["ya","ye"], + "琋" => &["xi"], + "琌" => &["ling"], + "琍" => &["li"], + "琎" => &["jin"], + "琏" => &["lian"], + "琐" => &["suo"], + "琑" => &["suo"], + "琓" => &["wan"], + "琔" => &["dian"], + "琕" => &["bing"], + "琖" => &["zhan"], + "琗" => &["cui"], + "琘" => &["min"], + "琙" => &["yu"], + "琚" => &["ju"], + "琛" => &["chen"], + "琜" => &["lai"], + "琝" => &["wen"], + "琞" => &["sheng"], + "琟" => &["wei"], + "琠" => &["dian"], + "琡" => &["chu"], + "琢" => &["zhuo","zuo"], + "琣" => &["pei"], + "琤" => &["cheng"], + "琥" => &["hu"], + "琦" => &["qi"], + "琧" => &["e"], + "琨" => &["kun"], + "琩" => &["chang"], + "琪" => &["qi"], + "琫" => &["beng"], + "琬" => &["wan"], + "琭" => &["lu"], + "琮" => &["cong"], + "琯" => &["guan"], + "琰" => &["yan"], + "琱" => &["diao"], + "琲" => &["bei"], + "琳" => &["lin"], + "琴" => &["qin"], + "琵" => &["pi"], + "琶" => &["pa","ba"], + "琷" => &["qiang"], + "琸" => &["zhuo"], + "琹" => &["qin"], + "琺" => &["fa"], + "琼" => &["qiong"], + "琽" => &["du"], + "琾" => &["jie"], + "琿" => &["hun","hui"], + "瑀" => &["yu"], + "瑁" => &["mao","mei"], + "瑂" => &["mei"], + "瑃" => &["chun"], + "瑄" => &["xuan"], + "瑅" => &["ti"], + "瑆" => &["xing"], + "瑇" => &["dai"], + "瑈" => &["rou"], + "瑉" => &["min"], + "瑊" => &["zhen"], + "瑋" => &["wei"], + "瑌" => &["ruan"], + "瑍" => &["huan"], + "瑎" => &["xie"], + "瑏" => &["chuan"], + "瑐" => &["jian"], + "瑑" => &["zhuan"], + "瑒" => &["yang"], + "瑓" => &["lian"], + "瑔" => &["quan"], + "瑕" => &["xia"], + "瑖" => &["duan"], + "瑗" => &["yuan"], + "瑘" => &["ye"], + "瑙" => &["nao"], + "瑚" => &["hu"], + "瑛" => &["ying"], + "瑜" => &["yu"], + "瑝" => &["huang"], + "瑞" => &["rui"], + "瑟" => &["se"], + "瑠" => &["liu"], + "瑢" => &["rong"], + "瑣" => &["suo"], + "瑤" => &["yao"], + "瑥" => &["wen"], + "瑦" => &["wu"], + "瑧" => &["jin"], + "瑨" => &["jin"], + "瑩" => &["ying"], + "瑪" => &["ma"], + "瑫" => &["tao"], + "瑬" => &["liu"], + "瑭" => &["tang"], + "瑮" => &["li"], + "瑯" => &["lang"], + "瑰" => &["gui"], + "瑱" => &["tian"], + "瑲" => &["qiang"], + "瑳" => &["cuo"], + "瑴" => &["jue"], + "瑵" => &["zhao"], + "瑶" => &["yao"], + "瑷" => &["ai"], + "瑸" => &["bin"], + "瑹" => &["tu"], + "瑺" => &["chang"], + "瑻" => &["kun"], + "瑼" => &["zhuan"], + "瑽" => &["cong"], + "瑾" => &["jin"], + "瑿" => &["yi"], + "璀" => &["cui"], + "璁" => &["cong"], + "璂" => &["qi"], + "璃" => &["li"], + "璄" => &["ying"], + "璅" => &["suo"], + "璆" => &["qiu"], + "璇" => &["xuan"], + "璈" => &["ao"], + "璉" => &["lian"], + "璊" => &["man"], + "璋" => &["zhang"], + "璌" => &["yin"], + "璎" => &["ying"], + "璏" => &["wei"], + "璐" => &["lu"], + "璑" => &["wu"], + "璒" => &["deng"], + "璔" => &["zeng"], + "璕" => &["xun"], + "璖" => &["qu"], + "璗" => &["dang"], + "璘" => &["lin"], + "璙" => &["liao"], + "璚" => &["qiong"], + "璛" => &["su"], + "璜" => &["huang"], + "璝" => &["gui"], + "璞" => &["pu"], + "璟" => &["jing"], + "璠" => &["fan"], + "璡" => &["jin"], + "璢" => &["liu"], + "璣" => &["ji"], + "璥" => &["jing"], + "璦" => &["ai"], + "璧" => &["bi"], + "璨" => &["can"], + "璩" => &["qu"], + "璪" => &["zao"], + "璫" => &["dang"], + "璬" => &["jiao"], + "璭" => &["gun"], + "璮" => &["tan"], + "璯" => &["hui"], + "環" => &["huan"], + "璱" => &["se"], + "璲" => &["sui"], + "璳" => &["tian"], + "璵" => &["yu"], + "璶" => &["jin"], + "璷" => &["fu"], + "璸" => &["bin"], + "璹" => &["shu"], + "璺" => &["wen"], + "璻" => &["zui"], + "璼" => &["lan"], + "璽" => &["xi"], + "璾" => &["ji"], + "璿" => &["xuan"], + "瓀" => &["ruan"], + "瓁" => &["huo"], + "瓂" => &["gai"], + "瓃" => &["lei"], + "瓄" => &["du"], + "瓅" => &["li"], + "瓆" => &["zhi"], + "瓇" => &["rou"], + "瓈" => &["li"], + "瓉" => &["zan"], + "瓊" => &["qiong"], + "瓋" => &["zhe"], + "瓌" => &["gui"], + "瓍" => &["sui"], + "瓎" => &["la"], + "瓏" => &["long"], + "瓐" => &["lu"], + "瓑" => &["li"], + "瓒" => &["zan"], + "瓓" => &["lan"], + "瓔" => &["ying"], + "瓕" => &["mi"], + "瓖" => &["xiang"], + "瓗" => &["xi"], + "瓘" => &["guan"], + "瓙" => &["dao"], + "瓚" => &["zan"], + "瓛" => &["huan"], + "瓜" => &["gua"], + "瓝" => &["bao"], + "瓞" => &["die"], + "瓟" => &["pao"], + "瓠" => &["hu"], + "瓡" => &["zhi"], + "瓢" => &["piao"], + "瓣" => &["ban"], + "瓤" => &["rang"], + "瓥" => &["li"], + "瓦" => &["wa"], + "瓨" => &["jiang","hong"], + "瓩" => &["qian","wa"], + "瓪" => &["ban"], + "瓫" => &["pen"], + "瓬" => &["fang"], + "瓭" => &["dan"], + "瓮" => &["weng"], + "瓯" => &["ou"], + "瓳" => &["hu"], + "瓴" => &["ling"], + "瓵" => &["yi"], + "瓶" => &["ping"], + "瓷" => &["ci"], + "瓹" => &["juan"], + "瓺" => &["chang"], + "瓻" => &["chi"], + "瓽" => &["dang"], + "瓾" => &["meng"], + "瓿" => &["bu"], + "甀" => &["chui"], + "甁" => &["ping"], + "甂" => &["bian"], + "甃" => &["zhou"], + "甄" => &["zhen"], + "甆" => &["ci"], + "甇" => &["ying"], + "甈" => &["qi"], + "甉" => &["xian"], + "甊" => &["lou"], + "甋" => &["di"], + "甌" => &["ou"], + "甍" => &["meng"], + "甎" => &["zhuan"], + "甏" => &["beng"], + "甐" => &["lin"], + "甑" => &["zeng"], + "甒" => &["wu"], + "甓" => &["pi"], + "甔" => &["dan"], + "甕" => &["weng"], + "甖" => &["ying"], + "甗" => &["yan"], + "甘" => &["gan"], + "甙" => &["dai"], + "甚" => &["shen","she"], + "甛" => &["tian"], + "甜" => &["tian"], + "甝" => &["han"], + "甞" => &["chang"], + "生" => &["sheng"], + "甠" => &["qing"], + "甡" => &["shen"], + "產" => &["chan"], + "産" => &["chan"], + "甤" => &["rui"], + "甥" => &["sheng"], + "甦" => &["su"], + "甧" => &["shen"], + "用" => &["yong"], + "甩" => &["shuai"], + "甪" => &["lu"], + "甫" => &["fu"], + "甬" => &["yong"], + "甭" => &["beng"], + "甯" => &["ning"], + "田" => &["tian"], + "由" => &["you"], + "甲" => &["jia"], + "申" => &["shen"], + "甴" => &["zha"], + "电" => &["dian"], + "甶" => &["fu"], + "男" => &["nan"], + "甸" => &["dian"], + "甹" => &["ping"], + "町" => &["ding","ting"], + "画" => &["hua"], + "甼" => &["ting","ding"], + "甽" => &["quan"], + "甾" => &["zai"], + "甿" => &["meng"], + "畀" => &["bi"], + "畁" => &["qi"], + "畂" => &["liu"], + "畃" => &["xun"], + "畄" => &["liu"], + "畅" => &["chang"], + "畆" => &["mu"], + "畇" => &["yun"], + "畈" => &["fan"], + "畉" => &["fu"], + "畊" => &["geng"], + "畋" => &["tian"], + "界" => &["jie"], + "畍" => &["jie"], + "畎" => &["quan"], + "畏" => &["wei"], + "畐" => &["fu"], + "畑" => &["tian"], + "畒" => &["mu"], + "畔" => &["pan"], + "畕" => &["jiang"], + "畖" => &["wa"], + "畗" => &["da"], + "畘" => &["nan"], + "留" => &["liu"], + "畚" => &["ben"], + "畛" => &["zhen"], + "畜" => &["chu","xu"], + "畝" => &["mu"], + "畞" => &["mu"], + "畟" => &["ce"], + "畡" => &["gai"], + "畢" => &["bi"], + "畣" => &["da"], + "畤" => &["zhi"], + "略" => &["lu:e"], + "畦" => &["qi","xi"], + "畧" => &["lu:e"], + "畨" => &["pan"], + "番" => &["fan","pan"], + "畫" => &["hua"], + "畬" => &["yu"], + "畭" => &["yu"], + "畮" => &["mu"], + "畯" => &["jun"], + "異" => &["yi"], + "畱" => &["liu"], + "畲" => &["she"], + "畳" => &["die"], + "畴" => &["chou"], + "畵" => &["hua"], + "當" => &["dang"], + "畷" => &["chuo"], + "畸" => &["ji"], + "畹" => &["wan"], + "畺" => &["jiang"], + "畻" => &["cheng"], + "畼" => &["chang"], + "畽" => &["tun"], + "畾" => &["lei"], + "畿" => &["ji"], + "疀" => &["cha"], + "疁" => &["liu"], + "疂" => &["die"], + "疃" => &["tuan"], + "疄" => &["lin"], + "疅" => &["jiang"], + "疆" => &["jiang"], + "疇" => &["chou"], + "疈" => &["bo"], + "疉" => &["die"], + "疊" => &["die"], + "疋" => &["pi","shu","ya"], + "疌" => &["nie"], + "疍" => &["dan"], + "疎" => &["shu"], + "疏" => &["shu"], + "疐" => &["zhi"], + "疑" => &["yi"], + "疒" => &["chuang"], + "疓" => &["nai"], + "疔" => &["ding"], + "疕" => &["bi"], + "疖" => &["jie"], + "疗" => &["liao"], + "疘" => &["gong","gang"], + "疙" => &["ge"], + "疚" => &["jiu"], + "疛" => &["zhou"], + "疜" => &["xia"], + "疝" => &["shan"], + "疞" => &["xu"], + "疟" => &["nu:e","yao"], + "疠" => &["li"], + "疡" => &["yang"], + "疢" => &["chen"], + "疣" => &["you"], + "疤" => &["ba"], + "疥" => &["jie"], + "疦" => &["jue"], + "疧" => &["xi"], + "疨" => &["xia"], + "疩" => &["cui"], + "疪" => &["bi"], + "疫" => &["yi"], + "疬" => &["li"], + "疭" => &["zong"], + "疮" => &["chuang"], + "疯" => &["feng"], + "疰" => &["zhu"], + "疱" => &["pao"], + "疲" => &["pi"], + "疳" => &["gan"], + "疴" => &["ke"], + "疵" => &["ci"], + "疶" => &["xie"], + "疷" => &["qi"], + "疸" => &["dan","da"], + "疹" => &["zhen"], + "疺" => &["fa"], + "疻" => &["zhi"], + "疼" => &["teng"], + "疽" => &["ju"], + "疾" => &["ji"], + "疿" => &["fei"], + "痀" => &["ju"], + "痁" => &["dian"], + "痂" => &["jia"], + "痃" => &["xuan","xian"], + "痄" => &["zha"], + "病" => &["bing"], + "痆" => &["nie"], + "症" => &["zheng"], + "痈" => &["yong"], + "痉" => &["jing"], + "痊" => &["quan"], + "痋" => &["chong"], + "痌" => &["tong"], + "痍" => &["yi"], + "痎" => &["jie"], + "痏" => &["wei"], + "痐" => &["hui"], + "痑" => &["duo"], + "痒" => &["yang"], + "痓" => &["chi"], + "痔" => &["zhi"], + "痕" => &["hen"], + "痖" => &["ya"], + "痗" => &["mei"], + "痘" => &["dou"], + "痙" => &["jing"], + "痚" => &["xiao"], + "痛" => &["tong"], + "痜" => &["tu"], + "痝" => &["mang"], + "痞" => &["pi"], + "痟" => &["xiao"], + "痠" => &["suan"], + "痡" => &["pu"], + "痢" => &["li"], + "痣" => &["zhi"], + "痤" => &["cuo"], + "痥" => &["duo"], + "痦" => &["wu"], + "痧" => &["sha"], + "痨" => &["lao"], + "痩" => &["shou"], + "痪" => &["huan"], + "痫" => &["xian"], + "痬" => &["yi"], + "痭" => &["peng"], + "痮" => &["zhang"], + "痯" => &["guan"], + "痰" => &["tan"], + "痱" => &["fei"], + "痲" => &["ma"], + "痳" => &["lin"], + "痴" => &["chi"], + "痵" => &["ji"], + "痶" => &["tian"], + "痷" => &["an"], + "痸" => &["chi"], + "痹" => &["bi"], + "痺" => &["bi"], + "痻" => &["min"], + "痼" => &["gu"], + "痽" => &["dui"], + "痾" => &["e"], + "痿" => &["wei"], + "瘀" => &["yu"], + "瘁" => &["cui"], + "瘂" => &["ya"], + "瘃" => &["zhu"], + "瘄" => &["xi"], + "瘅" => &["dan"], + "瘆" => &["shen"], + "瘇" => &["zhong"], + "瘈" => &["ji","zhi"], + "瘉" => &["yu"], + "瘊" => &["hou"], + "瘋" => &["feng"], + "瘌" => &["la"], + "瘍" => &["yang"], + "瘎" => &["shen"], + "瘏" => &["tu"], + "瘐" => &["yu"], + "瘑" => &["gua"], + "瘒" => &["wen"], + "瘓" => &["huan"], + "瘔" => &["ku"], + "瘕" => &["jia","xia"], + "瘖" => &["yin"], + "瘗" => &["yi"], + "瘘" => &["lou"], + "瘙" => &["sao"], + "瘚" => &["jue"], + "瘛" => &["chi"], + "瘜" => &["xi"], + "瘝" => &["guan"], + "瘞" => &["yi"], + "瘟" => &["wen"], + "瘠" => &["ji"], + "瘡" => &["chuang"], + "瘢" => &["ban"], + "瘣" => &["lei"], + "瘤" => &["liu"], + "瘥" => &["chai","cuo"], + "瘦" => &["shou"], + "瘧" => &["nu:e","yao"], + "瘨" => &["dian"], + "瘩" => &["da"], + "瘪" => &["bie"], + "瘫" => &["tan"], + "瘬" => &["zhang"], + "瘭" => &["biao"], + "瘮" => &["shen"], + "瘯" => &["cu"], + "瘰" => &["luo"], + "瘱" => &["yi"], + "瘲" => &["zong"], + "瘳" => &["chou"], + "瘴" => &["zhang"], + "瘵" => &["zhai"], + "瘶" => &["sou"], + "瘷" => &["suo"], + "瘸" => &["que"], + "瘹" => &["diao"], + "瘺" => &["lou"], + "瘻" => &["lou"], + "瘼" => &["mo"], + "瘽" => &["jin"], + "瘾" => &["yin"], + "瘿" => &["ying"], + "癀" => &["huang"], + "癁" => &["fu"], + "療" => &["liao"], + "癃" => &["long"], + "癄" => &["qiao"], + "癅" => &["liu"], + "癆" => &["lao"], + "癇" => &["xian"], + "癈" => &["fei"], + "癉" => &["dan"], + "癊" => &["yin"], + "癋" => &["he"], + "癌" => &["ai","yan"], + "癍" => &["ban"], + "癎" => &["xian"], + "癏" => &["guan"], + "癐" => &["guai"], + "癑" => &["nong"], + "癒" => &["yu"], + "癓" => &["wei"], + "癔" => &["yi"], + "癕" => &["yong"], + "癖" => &["pi"], + "癗" => &["lei"], + "癘" => &["li","ji"], + "癙" => &["shu"], + "癚" => &["dan"], + "癛" => &["lin"], + "癜" => &["dian"], + "癝" => &["lin"], + "癞" => &["lai"], + "癟" => &["bie"], + "癠" => &["ji"], + "癡" => &["chi"], + "癢" => &["yang"], + "癣" => &["xuan"], + "癤" => &["jie"], + "癥" => &["zheng"], + "癧" => &["li"], + "癨" => &["huo"], + "癩" => &["lai"], + "癪" => &["ji"], + "癫" => &["dian"], + "癬" => &["xian","xuan"], + "癭" => &["ying"], + "癮" => &["yin"], + "癯" => &["qu"], + "癰" => &["yong"], + "癱" => &["tan"], + "癲" => &["dian"], + "癳" => &["luo"], + "癴" => &["luan"], + "癵" => &["luan"], + "癶" => &["bo"], + "癸" => &["gui"], + "癹" => &["po"], + "発" => &["fa"], + "登" => &["deng"], + "發" => &["fa"], + "白" => &["bai"], + "百" => &["bai","bo"], + "癿" => &["qie"], + "皀" => &["bi"], + "皁" => &["zao"], + "皂" => &["zao"], + "皃" => &["mao"], + "的" => &["de","di"], + "皅" => &["pa"], + "皆" => &["jie"], + "皇" => &["huang"], + "皈" => &["gui"], + "皉" => &["ci"], + "皊" => &["ling"], + "皋" => &["gao"], + "皌" => &["mo"], + "皍" => &["ji"], + "皎" => &["jiao","jia"], + "皏" => &["peng"], + "皐" => &["gao"], + "皑" => &["ai"], + "皒" => &["e"], + "皓" => &["hao"], + "皔" => &["han"], + "皕" => &["bi"], + "皖" => &["wan","huan"], + "皗" => &["chou"], + "皘" => &["qian"], + "皙" => &["xi"], + "皚" => &["ai"], + "皛" => &["jiong"], + "皜" => &["hao"], + "皝" => &["huang"], + "皞" => &["hao"], + "皟" => &["ze"], + "皠" => &["cui"], + "皡" => &["hao"], + "皢" => &["xiao"], + "皣" => &["ye"], + "皤" => &["po"], + "皥" => &["hao"], + "皦" => &["jiao"], + "皧" => &["ai"], + "皨" => &["xing"], + "皩" => &["huang"], + "皪" => &["li"], + "皫" => &["piao"], + "皬" => &["he"], + "皭" => &["jiao"], + "皮" => &["pi"], + "皯" => &["gan"], + "皰" => &["pao"], + "皱" => &["zhou"], + "皲" => &["jun"], + "皳" => &["qiu"], + "皴" => &["cun"], + "皵" => &["que"], + "皶" => &["zha"], + "皷" => &["gu"], + "皸" => &["jun"], + "皹" => &["jun"], + "皺" => &["zhou"], + "皻" => &["zha"], + "皼" => &["gu"], + "皽" => &["zhan"], + "皾" => &["du"], + "皿" => &["min"], + "盀" => &["qi"], + "盁" => &["ying"], + "盂" => &["yu"], + "盃" => &["bei"], + "盄" => &["zhao"], + "盅" => &["zhong"], + "盆" => &["pen"], + "盇" => &["he"], + "盈" => &["ying"], + "盉" => &["he"], + "益" => &["yi"], + "盋" => &["bo"], + "盌" => &["wan"], + "盍" => &["he"], + "盎" => &["ang"], + "盏" => &["zhan"], + "盐" => &["yan"], + "监" => &["jian"], + "盒" => &["he"], + "盓" => &["yu"], + "盔" => &["kui"], + "盕" => &["fan"], + "盖" => &["gai","ge"], + "盗" => &["dao"], + "盘" => &["pan"], + "盙" => &["fu"], + "盚" => &["qiu"], + "盛" => &["sheng","cheng"], + "盜" => &["dao"], + "盝" => &["lu"], + "盞" => &["zhan"], + "盟" => &["meng","ming"], + "盠" => &["lu"], + "盡" => &["jin"], + "盢" => &["xu"], + "監" => &["jian"], + "盤" => &["pan"], + "盥" => &["guan"], + "盦" => &["an"], + "盧" => &["lu"], + "盨" => &["xu"], + "盩" => &["zhou"], + "盪" => &["dang"], + "盫" => &["an"], + "盬" => &["gu"], + "盭" => &["li"], + "目" => &["mu"], + "盯" => &["ding"], + "盰" => &["gan"], + "盱" => &["xu"], + "盲" => &["mang"], + "盳" => &["mang"], + "直" => &["zhi"], + "盵" => &["qi"], + "盶" => &["wan"], + "盷" => &["tian"], + "相" => &["xiang"], + "盹" => &["dun"], + "盺" => &["xin"], + "盻" => &["xi"], + "盼" => &["pan"], + "盽" => &["feng"], + "盾" => &["dun","shun"], + "盿" => &["min"], + "眀" => &["ming"], + "省" => &["sheng","xing"], + "眂" => &["shi"], + "眃" => &["yun"], + "眄" => &["mian"], + "眅" => &["pan"], + "眆" => &["fang"], + "眇" => &["miao"], + "眈" => &["dan"], + "眉" => &["mei"], + "眊" => &["mao"], + "看" => &["kan"], + "県" => &["xian"], + "眍" => &["kou"], + "眎" => &["shi"], + "眏" => &["yang"], + "眐" => &["zheng"], + "眑" => &["yao"], + "眒" => &["shen"], + "眓" => &["huo"], + "眔" => &["da"], + "眕" => &["zhen"], + "眖" => &["kuang"], + "眗" => &["ju"], + "眘" => &["shen"], + "眙" => &["yi"], + "眚" => &["sheng"], + "眛" => &["mei"], + "眜" => &["mo"], + "眝" => &["zhu"], + "眞" => &["zhen"], + "真" => &["zhen"], + "眠" => &["mian"], + "眡" => &["di"], + "眢" => &["yuan"], + "眣" => &["die"], + "眤" => &["yi"], + "眥" => &["zi"], + "眦" => &["zi"], + "眧" => &["chao"], + "眨" => &["zha"], + "眩" => &["xuan"], + "眪" => &["bing"], + "眫" => &["mi"], + "眬" => &["long"], + "眭" => &["sui"], + "眮" => &["tong"], + "眯" => &["mi"], + "眰" => &["die"], + "眱" => &["yi"], + "眲" => &["er"], + "眳" => &["ming"], + "眴" => &["xuan"], + "眵" => &["chi"], + "眶" => &["kuang"], + "眷" => &["juan"], + "眸" => &["mou"], + "眹" => &["zhen"], + "眺" => &["tiao"], + "眻" => &["yang"], + "眼" => &["yan"], + "眽" => &["mo"], + "眾" => &["zhong"], + "眿" => &["mai"], + "着" => &["zhe","zhuo","zhao"], + "睁" => &["zheng"], + "睂" => &["mei"], + "睃" => &["suo"], + "睄" => &["shao"], + "睅" => &["han"], + "睆" => &["huan"], + "睇" => &["di"], + "睈" => &["cheng"], + "睉" => &["cuo"], + "睊" => &["juan"], + "睋" => &["e"], + "睌" => &["wan"], + "睍" => &["xian"], + "睎" => &["xi"], + "睏" => &["kun"], + "睐" => &["lai"], + "睑" => &["jian"], + "睒" => &["shan"], + "睓" => &["tian"], + "睔" => &["hun"], + "睕" => &["wan"], + "睖" => &["ling"], + "睗" => &["shi"], + "睘" => &["qiong"], + "睙" => &["lie"], + "睚" => &["ya","ai"], + "睛" => &["jing"], + "睜" => &["zheng"], + "睝" => &["li"], + "睞" => &["lai"], + "睟" => &["sui"], + "睠" => &["juan"], + "睡" => &["shui"], + "睢" => &["sui"], + "督" => &["du"], + "睤" => &["pi"], + "睥" => &["pi","bi"], + "睦" => &["mu"], + "睧" => &["hun"], + "睨" => &["ni"], + "睩" => &["lu"], + "睪" => &["gao"], + "睫" => &["jie"], + "睬" => &["cai"], + "睭" => &["zhou"], + "睮" => &["yu"], + "睯" => &["hun"], + "睰" => &["ma"], + "睱" => &["xia"], + "睲" => &["xing"], + "睳" => &["hui"], + "睴" => &["gun"], + "睶" => &["chun"], + "睷" => &["jian"], + "睸" => &["mei"], + "睹" => &["du"], + "睺" => &["hou"], + "睻" => &["xuan"], + "睼" => &["ti"], + "睽" => &["kui"], + "睾" => &["gao"], + "睿" => &["rui"], + "瞀" => &["mao"], + "瞁" => &["xu"], + "瞂" => &["fa"], + "瞃" => &["wen"], + "瞄" => &["miao"], + "瞅" => &["chou"], + "瞆" => &["kui"], + "瞇" => &["mi"], + "瞈" => &["weng"], + "瞉" => &["kou"], + "瞊" => &["dang"], + "瞋" => &["chen"], + "瞌" => &["ke"], + "瞍" => &["sou"], + "瞎" => &["xia"], + "瞏" => &["qiong"], + "瞐" => &["mao"], + "瞑" => &["ming"], + "瞒" => &["man"], + "瞓" => &["shui"], + "瞔" => &["ze"], + "瞕" => &["zhang"], + "瞖" => &["yi"], + "瞗" => &["diao"], + "瞘" => &["kou"], + "瞙" => &["mo"], + "瞚" => &["shun"], + "瞛" => &["cong"], + "瞜" => &["lou"], + "瞝" => &["chi"], + "瞞" => &["man"], + "瞟" => &["piao"], + "瞠" => &["cheng"], + "瞡" => &["ji"], + "瞢" => &["meng"], + "瞣" => &["huan"], + "瞤" => &["run"], + "瞥" => &["pie"], + "瞦" => &["xi"], + "瞧" => &["qiao","ya"], + "瞨" => &["pu"], + "瞩" => &["zhu"], + "瞪" => &["deng"], + "瞫" => &["shen"], + "瞬" => &["shun"], + "瞭" => &["liao"], + "瞮" => &["che"], + "瞯" => &["xian"], + "瞰" => &["kan"], + "瞱" => &["ye"], + "瞲" => &["xu"], + "瞳" => &["tong"], + "瞴" => &["wu"], + "瞵" => &["lin"], + "瞶" => &["kui"], + "瞷" => &["jian"], + "瞸" => &["ye"], + "瞹" => &["ai"], + "瞺" => &["hui"], + "瞻" => &["zhan"], + "瞼" => &["jian"], + "瞽" => &["gu"], + "瞾" => &["zhao"], + "瞿" => &["qu","ju"], + "矀" => &["wei"], + "矁" => &["chou"], + "矂" => &["ji"], + "矃" => &["ning"], + "矄" => &["xun"], + "矅" => &["yao"], + "矆" => &["huo"], + "矇" => &["meng"], + "矈" => &["mian"], + "矉" => &["bin","pin"], + "矊" => &["mian"], + "矋" => &["li"], + "矌" => &["guang"], + "矍" => &["jue"], + "矎" => &["xuan"], + "矏" => &["mian"], + "矐" => &["huo"], + "矑" => &["lu"], + "矒" => &["meng"], + "矓" => &["long"], + "矔" => &["guan"], + "矕" => &["man"], + "矖" => &["xi"], + "矗" => &["chu"], + "矘" => &["tang"], + "矙" => &["kan"], + "矚" => &["zhu"], + "矛" => &["mao"], + "矜" => &["jin","qin","guan"], + "矝" => &["lin"], + "矞" => &["yu"], + "矟" => &["shuo"], + "矠" => &["ce"], + "矡" => &["jue"], + "矢" => &["shi"], + "矣" => &["yi"], + "矤" => &["shen"], + "知" => &["zhi"], + "矦" => &["hou"], + "矧" => &["shen"], + "矨" => &["ying"], + "矩" => &["ju"], + "矪" => &["zhou"], + "矫" => &["jiao","jia"], + "矬" => &["cuo"], + "短" => &["duan"], + "矮" => &["ai"], + "矯" => &["jiao","jia"], + "矰" => &["zeng"], + "矱" => &["huo"], + "矲" => &["bai","pai"], + "石" => &["shi","dan"], + "矴" => &["ding"], + "矵" => &["qi"], + "矶" => &["ji"], + "矷" => &["zi"], + "矸" => &["gan"], + "矹" => &["wu"], + "矺" => &["tuo"], + "矻" => &["ku"], + "矼" => &["qiang"], + "矽" => &["xi"], + "矾" => &["fan"], + "矿" => &["kuang"], + "砀" => &["dang"], + "码" => &["ma"], + "砂" => &["sha"], + "砃" => &["dan"], + "砄" => &["jue"], + "砅" => &["li"], + "砆" => &["fu"], + "砇" => &["min"], + "砈" => &["nuo"], + "砉" => &["hua","xu"], + "砊" => &["kang"], + "砋" => &["zhi"], + "砌" => &["qi","qie"], + "砍" => &["kan"], + "砎" => &["jie"], + "砏" => &["fen"], + "砐" => &["e"], + "砑" => &["ya"], + "砒" => &["pi"], + "砓" => &["zhe"], + "研" => &["yan"], + "砕" => &["sui"], + "砖" => &["zhuan"], + "砗" => &["che"], + "砘" => &["dun"], + "砙" => &["pan"], + "砚" => &["yan"], + "砜" => &["feng"], + "砝" => &["fa"], + "砞" => &["mo"], + "砟" => &["zha","zuo"], + "砠" => &["qu"], + "砡" => &["yu"], + "砢" => &["ke"], + "砣" => &["tuo"], + "砤" => &["tuo"], + "砥" => &["di"], + "砦" => &["zhai"], + "砧" => &["zhen"], + "砨" => &["e"], + "砩" => &["fu","fei"], + "砪" => &["mu"], + "砫" => &["zhu"], + "砬" => &["la","li"], + "砭" => &["bian"], + "砮" => &["nu"], + "砯" => &["ping"], + "砰" => &["peng"], + "砱" => &["ling"], + "砲" => &["pao"], + "砳" => &["le"], + "破" => &["po"], + "砵" => &["bo"], + "砶" => &["po"], + "砷" => &["shen"], + "砸" => &["za"], + "砹" => &["ai"], + "砺" => &["li"], + "砻" => &["long"], + "砼" => &["tong"], + "砾" => &["li"], + "砿" => &["kuang"], + "础" => &["chu"], + "硁" => &["keng"], + "硂" => &["quan"], + "硃" => &["zhu"], + "硄" => &["kuang"], + "硅" => &["gui","huo"], + "硆" => &["e"], + "硇" => &["nao"], + "硈" => &["jia"], + "硉" => &["lu"], + "硊" => &["wei","kui"], + "硋" => &["ai"], + "硌" => &["luo","ge"], + "硍" => &["ken"], + "硎" => &["xing"], + "硏" => &["yan"], + "硐" => &["dong"], + "硑" => &["peng"], + "硒" => &["xi"], + "硔" => &["hong"], + "硕" => &["shuo"], + "硖" => &["xia"], + "硗" => &["qiao"], + "硙" => &["wei"], + "硚" => &["qiao"], + "硜" => &["keng"], + "硝" => &["xiao"], + "硞" => &["que"], + "硟" => &["chan"], + "硠" => &["lang"], + "硡" => &["hong"], + "硢" => &["yu"], + "硣" => &["xiao"], + "硤" => &["xia"], + "硥" => &["mang"], + "硦" => &["long"], + "硨" => &["che"], + "硩" => &["che"], + "硪" => &["wo"], + "硫" => &["liu"], + "硬" => &["ying"], + "硭" => &["mang"], + "确" => &["que"], + "硯" => &["yan"], + "硰" => &["cuo"], + "硱" => &["kun"], + "硲" => &["yu"], + "硵" => &["lu"], + "硶" => &["chen"], + "硷" => &["jian"], + "硹" => &["song"], + "硺" => &["zhuo"], + "硻" => &["keng"], + "硼" => &["peng"], + "硽" => &["yan"], + "硾" => &["zhui"], + "硿" => &["kong"], + "碀" => &["ceng"], + "碁" => &["qi"], + "碂" => &["zong"], + "碃" => &["qing"], + "碄" => &["lin"], + "碅" => &["jun"], + "碆" => &["bo"], + "碇" => &["ding"], + "碈" => &["min"], + "碉" => &["diao"], + "碊" => &["jian"], + "碋" => &["he"], + "碌" => &["liu","lu"], + "碍" => &["ai"], + "碎" => &["sui"], + "碏" => &["que"], + "碐" => &["ling"], + "碑" => &["bei"], + "碒" => &["yin"], + "碓" => &["dui"], + "碔" => &["wu"], + "碕" => &["qi"], + "碖" => &["lun"], + "碗" => &["wan"], + "碘" => &["dian"], + "碙" => &["gang"], + "碚" => &["bei"], + "碛" => &["qi"], + "碜" => &["chen"], + "碝" => &["ruan"], + "碞" => &["yan"], + "碟" => &["die"], + "碠" => &["ding"], + "碡" => &["zhou"], + "碢" => &["tuo"], + "碣" => &["jie"], + "碤" => &["ying"], + "碥" => &["bian"], + "碦" => &["ke"], + "碧" => &["bi"], + "碨" => &["wei"], + "碩" => &["shuo","shi"], + "碪" => &["zhen"], + "碫" => &["duan"], + "碬" => &["xia"], + "碭" => &["dang"], + "碮" => &["ti"], + "碯" => &["nao"], + "碰" => &["peng"], + "碱" => &["jian"], + "碲" => &["di"], + "碳" => &["tan"], + "碴" => &["cha"], + "碶" => &["qi"], + "碸" => &["feng"], + "碹" => &["xuan"], + "確" => &["que"], + "碻" => &["que"], + "碼" => &["ma"], + "碽" => &["gong"], + "碾" => &["nian"], + "碿" => &["su"], + "磀" => &["e"], + "磁" => &["ci"], + "磂" => &["liu"], + "磃" => &["si"], + "磄" => &["tang"], + "磅" => &["bang","pang"], + "磆" => &["hua"], + "磇" => &["pi"], + "磈" => &["wei"], + "磉" => &["sang"], + "磊" => &["lei"], + "磋" => &["cuo"], + "磌" => &["tian"], + "磍" => &["xia"], + "磎" => &["xi"], + "磏" => &["lian"], + "磐" => &["pan"], + "磑" => &["wei"], + "磒" => &["yun"], + "磓" => &["dui"], + "磔" => &["zhe"], + "磕" => &["ke"], + "磖" => &["la"], + "磘" => &["qing"], + "磙" => &["gun"], + "磚" => &["zhuan"], + "磛" => &["chan"], + "磜" => &["qi"], + "磝" => &["ao"], + "磞" => &["peng"], + "磟" => &["lu"], + "磠" => &["lu"], + "磡" => &["kan"], + "磢" => &["qiang"], + "磣" => &["chen"], + "磤" => &["yin"], + "磥" => &["lei"], + "磦" => &["biao"], + "磧" => &["qi"], + "磨" => &["mo"], + "磩" => &["qi"], + "磪" => &["cui"], + "磫" => &["zong"], + "磬" => &["qing"], + "磭" => &["chuo"], + "磯" => &["ji"], + "磰" => &["shan"], + "磱" => &["lao"], + "磲" => &["qu"], + "磳" => &["zeng"], + "磴" => &["deng"], + "磵" => &["jian"], + "磶" => &["xi"], + "磷" => &["lin"], + "磸" => &["ding"], + "磹" => &["dian"], + "磺" => &["huang"], + "磻" => &["pan"], + "磼" => &["za"], + "磽" => &["qiao"], + "磾" => &["di"], + "磿" => &["li"], + "礀" => &["jian"], + "礁" => &["jiao"], + "礂" => &["xi"], + "礃" => &["zhang"], + "礄" => &["qiao"], + "礅" => &["dun"], + "礆" => &["jian"], + "礇" => &["yu"], + "礈" => &["zhui"], + "礉" => &["he"], + "礊" => &["huo"], + "礋" => &["zhai"], + "礌" => &["lei"], + "礍" => &["ke"], + "礎" => &["chu"], + "礏" => &["ji"], + "礐" => &["que"], + "礑" => &["dang"], + "礒" => &["wo","yi"], + "礓" => &["jiang"], + "礔" => &["pi"], + "礕" => &["pi"], + "礖" => &["yu"], + "礗" => &["pin"], + "礘" => &["qi"], + "礙" => &["ai"], + "礚" => &["ke"], + "礛" => &["jian"], + "礜" => &["yu"], + "礝" => &["ruan"], + "礞" => &["meng"], + "礟" => &["pao"], + "礠" => &["zi"], + "礡" => &["bo"], + "礣" => &["mie"], + "礤" => &["ca"], + "礥" => &["xian"], + "礦" => &["kuang","gong"], + "礧" => &["lei"], + "礨" => &["lei"], + "礩" => &["zhi"], + "礪" => &["li"], + "礫" => &["li"], + "礬" => &["fan"], + "礭" => &["que"], + "礮" => &["pao"], + "礯" => &["ying"], + "礰" => &["li"], + "礱" => &["long"], + "礲" => &["long"], + "礳" => &["mo"], + "礴" => &["bo"], + "礵" => &["shuang"], + "礶" => &["guan"], + "礷" => &["lan"], + "礸" => &["zan"], + "礹" => &["yan"], + "示" => &["shi"], + "礻" => &["shi"], + "礼" => &["li"], + "礽" => &["reng"], + "社" => &["she"], + "礿" => &["yue"], + "祀" => &["si"], + "祁" => &["qi"], + "祂" => &["ta"], + "祃" => &["ma"], + "祄" => &["xie"], + "祅" => &["yao"], + "祆" => &["xian"], + "祇" => &["zhi","qi"], + "祈" => &["qi"], + "祉" => &["zhi"], + "祊" => &["beng"], + "祋" => &["shu"], + "祌" => &["chong"], + "祎" => &["yi"], + "祏" => &["shi"], + "祐" => &["you"], + "祑" => &["zhi"], + "祒" => &["tiao"], + "祓" => &["fu"], + "祔" => &["fu"], + "祕" => &["mi"], + "祖" => &["zu"], + "祗" => &["zhi"], + "祘" => &["suan"], + "祙" => &["mei"], + "祚" => &["zuo"], + "祛" => &["qu"], + "祜" => &["hu"], + "祝" => &["zhu"], + "神" => &["shen"], + "祟" => &["sui"], + "祠" => &["ci"], + "祡" => &["chai"], + "祢" => &["mi","ni"], + "祣" => &["lu:"], + "祤" => &["yu"], + "祥" => &["xiang"], + "祦" => &["wu"], + "祧" => &["tiao"], + "票" => &["piao"], + "祩" => &["zhu"], + "祪" => &["gui"], + "祫" => &["xia"], + "祬" => &["zhi"], + "祭" => &["ji","zhai"], + "祮" => &["gao"], + "祯" => &["zhen"], + "祰" => &["gao"], + "祱" => &["shui"], + "祲" => &["jin"], + "祳" => &["zhen"], + "祴" => &["gai","jie"], + "祵" => &["kun"], + "祶" => &["di"], + "祷" => &["dao"], + "祸" => &["huo"], + "祹" => &["tao"], + "祺" => &["qi"], + "祻" => &["gu"], + "祼" => &["guan"], + "祽" => &["zui"], + "祾" => &["ling"], + "祿" => &["lu"], + "禀" => &["bing"], + "禁" => &["jin"], + "禂" => &["dao"], + "禃" => &["zhi"], + "禄" => &["lu"], + "禅" => &["chan","shan"], + "禆" => &["bei"], + "禇" => &["zhe"], + "禈" => &["hui"], + "禉" => &["you"], + "禊" => &["xi"], + "禋" => &["yin"], + "禌" => &["zi"], + "禍" => &["huo"], + "禎" => &["zhen"], + "福" => &["fu"], + "禐" => &["yuan"], + "禑" => &["wu"], + "禒" => &["xian"], + "禓" => &["yang"], + "禔" => &["ti"], + "禕" => &["yi"], + "禖" => &["mei"], + "禗" => &["si"], + "禘" => &["di"], + "禚" => &["zhuo"], + "禛" => &["zhen"], + "禜" => &["yong"], + "禝" => &["ji"], + "禞" => &["gao"], + "禟" => &["tang"], + "禠" => &["chi"], + "禡" => &["ma"], + "禢" => &["ta"], + "禤" => &["xuan"], + "禥" => &["qi"], + "禦" => &["yu"], + "禧" => &["xi"], + "禨" => &["ji"], + "禩" => &["si"], + "禪" => &["chan","shan"], + "禫" => &["xuan"], + "禬" => &["hui"], + "禭" => &["sui"], + "禮" => &["li"], + "禯" => &["nong"], + "禰" => &["ni","mi"], + "禱" => &["dao"], + "禲" => &["li"], + "禳" => &["rang"], + "禴" => &["yue"], + "禵" => &["ti"], + "禶" => &["zan"], + "禷" => &["lei"], + "禸" => &["rou"], + "禹" => &["yu"], + "禺" => &["yu"], + "离" => &["li"], + "禼" => &["xie"], + "禽" => &["qin"], + "禾" => &["he"], + "禿" => &["tu"], + "秀" => &["xiu"], + "私" => &["si"], + "秂" => &["ren"], + "秃" => &["tu"], + "秄" => &["zi"], + "秅" => &["cha"], + "秆" => &["gan"], + "秇" => &["yi"], + "秈" => &["xian"], + "秉" => &["bing"], + "秊" => &["nian"], + "秋" => &["qiu"], + "秌" => &["qiu"], + "种" => &["zhong","chong"], + "秎" => &["fen"], + "秏" => &["hao"], + "秐" => &["yun"], + "科" => &["ke"], + "秒" => &["miao"], + "秓" => &["zhi"], + "秔" => &["jing"], + "秕" => &["bi"], + "秖" => &["zhi"], + "秗" => &["yu"], + "秘" => &["mi","bi","lin"], + "秙" => &["ku"], + "秚" => &["ban"], + "秛" => &["pi"], + "秜" => &["ni"], + "秝" => &["li"], + "秞" => &["you"], + "租" => &["zu"], + "秠" => &["pi"], + "秡" => &["ba"], + "秢" => &["ling"], + "秣" => &["mo"], + "秤" => &["cheng","chen"], + "秥" => &["nian"], + "秦" => &["qin"], + "秧" => &["yang"], + "秨" => &["zuo"], + "秩" => &["zhi"], + "秪" => &["zhi"], + "秫" => &["shu"], + "秬" => &["ju"], + "秭" => &["zi"], + "秮" => &["tai"], + "积" => &["ji"], + "称" => &["cheng","chen"], + "秱" => &["tong"], + "秲" => &["zhi"], + "秳" => &["huo"], + "秴" => &["he"], + "秵" => &["yin"], + "秶" => &["zi"], + "秷" => &["zhi"], + "秸" => &["jie"], + "秹" => &["ren"], + "秺" => &["du"], + "移" => &["yi"], + "秼" => &["zhu"], + "秽" => &["hui"], + "秾" => &["nong"], + "秿" => &["fu"], + "稀" => &["xi"], + "稁" => &["kao"], + "稂" => &["lang"], + "稃" => &["fu"], + "稄" => &["ze"], + "稅" => &["shui"], + "稆" => &["lu:"], + "稇" => &["kun"], + "稈" => &["gan"], + "稉" => &["jing"], + "稊" => &["ti"], + "程" => &["cheng"], + "稌" => &["tu"], + "稍" => &["shao"], + "税" => &["shui"], + "稏" => &["ya"], + "稐" => &["lun"], + "稑" => &["lu"], + "稒" => &["gu"], + "稓" => &["zuo"], + "稔" => &["ren"], + "稕" => &["zhun"], + "稖" => &["bang"], + "稗" => &["bai","bi"], + "稘" => &["ji","qi"], + "稙" => &["zhi"], + "稚" => &["zhi"], + "稛" => &["kun"], + "稜" => &["leng"], + "稝" => &["peng"], + "稞" => &["ke"], + "稟" => &["bing"], + "稠" => &["chou"], + "稡" => &["zui"], + "稢" => &["yu"], + "稣" => &["su"], + "稦" => &["yi"], + "稧" => &["xi"], + "稨" => &["bian"], + "稩" => &["ji"], + "稪" => &["fu"], + "稫" => &["bi"], + "稬" => &["nuo"], + "稭" => &["jie"], + "種" => &["zhong","chong"], + "稯" => &["zong"], + "稰" => &["xu"], + "稱" => &["cheng","chen"], + "稲" => &["dao"], + "稳" => &["wen"], + "稴" => &["lian"], + "稵" => &["zi"], + "稶" => &["yu"], + "稷" => &["ji"], + "稸" => &["xu"], + "稹" => &["zhen"], + "稺" => &["zhi"], + "稻" => &["dao"], + "稼" => &["jia"], + "稽" => &["ji","qi"], + "稾" => &["gao"], + "稿" => &["gao"], + "穀" => &["gu"], + "穁" => &["rong"], + "穂" => &["sui"], + "穄" => &["ji"], + "穅" => &["kang"], + "穆" => &["mu"], + "穇" => &["shan"], + "穈" => &["men"], + "穉" => &["zhi"], + "穊" => &["ji"], + "穋" => &["lu"], + "穌" => &["su","wei"], + "積" => &["ji"], + "穎" => &["ying"], + "穏" => &["wen"], + "穐" => &["qiu"], + "穑" => &["se"], + "穓" => &["yi"], + "穔" => &["huang"], + "穕" => &["qie"], + "穖" => &["ji"], + "穗" => &["sui"], + "穘" => &["xiao"], + "穙" => &["pu"], + "穚" => &["jiao"], + "穛" => &["zhuo"], + "穜" => &["tong"], + "穞" => &["lu:"], + "穟" => &["sui"], + "穠" => &["nong"], + "穡" => &["se"], + "穢" => &["hui"], + "穣" => &["rang"], + "穤" => &["nuo"], + "穥" => &["yu"], + "穧" => &["ji"], + "穨" => &["tui"], + "穩" => &["wen"], + "穪" => &["cheng","chen"], + "穫" => &["huo"], + "穬" => &["gong"], + "穭" => &["lu:"], + "穮" => &["biao"], + "穰" => &["rang"], + "穱" => &["jue"], + "穲" => &["li"], + "穳" => &["zan"], + "穴" => &["xue"], + "穵" => &["wa"], + "究" => &["jiu"], + "穷" => &["qiong"], + "穸" => &["xi"], + "穹" => &["qiong"], + "空" => &["kong"], + "穻" => &["yu"], + "穼" => &["sen"], + "穽" => &["jing"], + "穾" => &["yao"], + "穿" => &["chuan"], + "窀" => &["zhun"], + "突" => &["tu"], + "窂" => &["lao"], + "窃" => &["qie"], + "窄" => &["zhai","ze"], + "窅" => &["yao"], + "窆" => &["bian"], + "窇" => &["bao"], + "窈" => &["yao"], + "窉" => &["bing"], + "窊" => &["yu"], + "窋" => &["zhu"], + "窌" => &["jiao"], + "窍" => &["qiao"], + "窎" => &["diao"], + "窏" => &["wu"], + "窐" => &["gui"], + "窑" => &["yao"], + "窒" => &["zhi"], + "窓" => &["chuan"], + "窔" => &["yao"], + "窕" => &["tiao"], + "窖" => &["jiao"], + "窗" => &["chuang"], + "窘" => &["jiong","jun"], + "窙" => &["xiao"], + "窚" => &["cheng"], + "窛" => &["kou"], + "窜" => &["cuan"], + "窝" => &["wo"], + "窞" => &["dan"], + "窟" => &["ku"], + "窠" => &["ke"], + "窡" => &["zhui"], + "窢" => &["xu"], + "窣" => &["su"], + "窥" => &["kui"], + "窦" => &["dou"], + "窨" => &["yin","xun"], + "窩" => &["wo"], + "窪" => &["wa"], + "窫" => &["ya"], + "窬" => &["yu"], + "窭" => &["ju"], + "窮" => &["qiong"], + "窯" => &["yao"], + "窰" => &["yao"], + "窱" => &["tiao"], + "窲" => &["liao"], + "窳" => &["yu"], + "窴" => &["tian"], + "窵" => &["diao"], + "窶" => &["ju"], + "窷" => &["liao"], + "窸" => &["xi"], + "窹" => &["wu"], + "窺" => &["kui"], + "窻" => &["chuang"], + "窼" => &["ju"], + "窾" => &["kuan"], + "窿" => &["long"], + "竀" => &["cheng"], + "竁" => &["cui"], + "竂" => &["piao"], + "竃" => &["zao"], + "竄" => &["cuan"], + "竅" => &["qiao"], + "竆" => &["qiong"], + "竇" => &["dou"], + "竈" => &["zao"], + "竉" => &["zao"], + "竊" => &["qie"], + "立" => &["li"], + "竌" => &["chu"], + "竍" => &["shi","gong","sheng"], + "竎" => &["fu"], + "竏" => &["qian","gong","sheng"], + "竐" => &["chu"], + "竑" => &["hong"], + "竒" => &["qi","ji"], + "竓" => &["qian","fen","zhi","yi","gong","sheng"], + "竔" => &["gong","sheng"], + "竕" => &["shi","fen","zhi","yi","gong","sheng"], + "竖" => &["shu"], + "竗" => &["miao"], + "竘" => &["ju"], + "站" => &["zhan"], + "竚" => &["zhu"], + "竛" => &["ling"], + "竜" => &["long"], + "竝" => &["bing"], + "竞" => &["jing"], + "竟" => &["jing"], + "章" => &["zhang"], + "竡" => &["yi","gong","sheng","bai","bei","si"], + "竢" => &["si","qi"], + "竣" => &["jun"], + "竤" => &["hong"], + "童" => &["tong"], + "竦" => &["song"], + "竧" => &["jing"], + "竨" => &["diao"], + "竩" => &["yi"], + "竪" => &["shu"], + "竫" => &["jing"], + "竬" => &["qu"], + "竭" => &["jie"], + "竮" => &["ping"], + "端" => &["duan"], + "竰" => &["shao"], + "竱" => &["zhuan"], + "竲" => &["ceng"], + "竳" => &["deng"], + "竴" => &["cun"], + "竵" => &["huai"], + "競" => &["jing"], + "竷" => &["kan"], + "竸" => &["jing"], + "竹" => &["zhu"], + "竺" => &["zhu"], + "竻" => &["le"], + "竼" => &["peng"], + "竽" => &["yu"], + "竾" => &["chi"], + "竿" => &["gan"], + "笀" => &["mang"], + "笁" => &["zhu"], + "笃" => &["du"], + "笄" => &["ji"], + "笅" => &["xiao"], + "笆" => &["ba"], + "笇" => &["suan"], + "笈" => &["ji"], + "笉" => &["zhen"], + "笊" => &["zhao"], + "笋" => &["sun"], + "笌" => &["ya"], + "笍" => &["zhui"], + "笎" => &["yuan"], + "笏" => &["hu"], + "笐" => &["gang"], + "笑" => &["xiao"], + "笒" => &["cen"], + "笓" => &["pi"], + "笔" => &["bi"], + "笕" => &["jian"], + "笖" => &["yi"], + "笗" => &["dong"], + "笘" => &["shan"], + "笙" => &["sheng"], + "笚" => &["xia"], + "笛" => &["di"], + "笜" => &["zhu"], + "笝" => &["na"], + "笞" => &["chi"], + "笟" => &["gu"], + "笠" => &["li"], + "笡" => &["qie"], + "笢" => &["min"], + "笣" => &["bao"], + "笤" => &["tiao"], + "笥" => &["si"], + "符" => &["fu"], + "笧" => &["ce"], + "笨" => &["ben"], + "笩" => &["fa"], + "笪" => &["da"], + "笫" => &["zi"], + "第" => &["di"], + "笭" => &["ling"], + "笮" => &["ze","zuo"], + "笯" => &["nu"], + "笰" => &["fu"], + "笱" => &["gou"], + "笲" => &["fan"], + "笳" => &["jia"], + "笴" => &["ge"], + "笵" => &["fan"], + "笶" => &["shi"], + "笷" => &["mao"], + "笸" => &["po"], + "笺" => &["jian"], + "笻" => &["qiong"], + "笼" => &["long"], + "笾" => &["bian"], + "笿" => &["luo"], + "筀" => &["gui"], + "筁" => &["qu"], + "筂" => &["chi"], + "筃" => &["yin"], + "筄" => &["yao"], + "筅" => &["xian"], + "筆" => &["bi"], + "筇" => &["qiong"], + "筈" => &["gua"], + "等" => &["deng"], + "筊" => &["jiao"], + "筋" => &["jin"], + "筌" => &["quan"], + "筍" => &["sun"], + "筎" => &["ru"], + "筏" => &["fa"], + "筐" => &["kuang"], + "筑" => &["zhu"], + "筒" => &["tong"], + "筓" => &["ji"], + "答" => &["da"], + "筕" => &["hang"], + "策" => &["ce"], + "筗" => &["zhong"], + "筘" => &["kou"], + "筙" => &["lai"], + "筚" => &["bi"], + "筛" => &["shai"], + "筜" => &["dang"], + "筝" => &["zheng"], + "筞" => &["ce"], + "筟" => &["fu"], + "筠" => &["yun","jun"], + "筡" => &["tu"], + "筢" => &["pa"], + "筣" => &["li"], + "筤" => &["lang"], + "筥" => &["ju"], + "筦" => &["guan"], + "筧" => &["jian"], + "筨" => &["han"], + "筩" => &["tong"], + "筪" => &["xia"], + "筫" => &["zhi"], + "筬" => &["cheng"], + "筭" => &["suan"], + "筮" => &["shi"], + "筯" => &["zhu"], + "筰" => &["zuo"], + "筱" => &["xiao"], + "筲" => &["shao"], + "筳" => &["ting"], + "筴" => &["jia","ce"], + "筵" => &["yan"], + "筶" => &["gao"], + "筷" => &["kuai"], + "筸" => &["gan"], + "筹" => &["chou"], + "筺" => &["kuang"], + "筻" => &["gang"], + "筼" => &["yun"], + "签" => &["qian"], + "筿" => &["xiao"], + "简" => &["jian"], + "箁" => &["pu"], + "箂" => &["lai"], + "箃" => &["zou"], + "箄" => &["bi"], + "箅" => &["bi"], + "箆" => &["bi"], + "箇" => &["ge"], + "箈" => &["chi"], + "箉" => &["guai"], + "箊" => &["yu"], + "箋" => &["jian"], + "箌" => &["zhao"], + "箍" => &["gu"], + "箎" => &["chi"], + "箏" => &["zheng"], + "箐" => &["qing"], + "箑" => &["sha"], + "箒" => &["zhou"], + "箓" => &["lu"], + "箔" => &["bo"], + "箕" => &["ji"], + "箖" => &["lin"], + "算" => &["suan"], + "箘" => &["jun"], + "箙" => &["fu"], + "箚" => &["zha"], + "箛" => &["gu"], + "箜" => &["kong"], + "箝" => &["qian"], + "箞" => &["qian"], + "箟" => &["jun"], + "箠" => &["chui"], + "管" => &["guan"], + "箢" => &["yuan"], + "箣" => &["ce"], + "箤" => &["ju"], + "箥" => &["bo"], + "箦" => &["ze"], + "箧" => &["qie"], + "箨" => &["tuo"], + "箩" => &["luo"], + "箪" => &["dan"], + "箫" => &["xiao"], + "箬" => &["ruo"], + "箭" => &["jian"], + "箯" => &["bian"], + "箰" => &["sun"], + "箱" => &["xiang"], + "箲" => &["xian"], + "箳" => &["ping"], + "箴" => &["zhen"], + "箵" => &["sheng"], + "箶" => &["hu"], + "箷" => &["shi","yi"], + "箸" => &["zhu"], + "箹" => &["yue"], + "箺" => &["chun"], + "箻" => &["fu"], + "箼" => &["wu"], + "箽" => &["dong"], + "箾" => &["shuo"], + "箿" => &["ji"], + "節" => &["jie"], + "篁" => &["huang"], + "篂" => &["xing"], + "篃" => &["mei"], + "範" => &["fan"], + "篅" => &["chuan"], + "篆" => &["zhuan"], + "篇" => &["pian"], + "篈" => &["feng"], + "築" => &["zhu"], + "篊" => &["hong"], + "篋" => &["qie"], + "篌" => &["hou"], + "篍" => &["qiu"], + "篎" => &["miao"], + "篏" => &["qian"], + "篑" => &["kui"], + "篓" => &["lou"], + "篔" => &["yun"], + "篕" => &["he"], + "篖" => &["tang"], + "篗" => &["yue"], + "篘" => &["chou"], + "篙" => &["gao"], + "篚" => &["fei"], + "篛" => &["ruo"], + "篜" => &["zheng"], + "篝" => &["gou"], + "篞" => &["nie"], + "篟" => &["qian"], + "篠" => &["xiao"], + "篡" => &["cuan"], + "篢" => &["gong"], + "篣" => &["pang"], + "篤" => &["du"], + "篥" => &["li"], + "篦" => &["bi"], + "篧" => &["zhuo"], + "篨" => &["chu"], + "篩" => &["shai"], + "篪" => &["chi"], + "篫" => &["zhu"], + "篬" => &["qiang"], + "篭" => &["long"], + "篮" => &["lan"], + "篯" => &["jian"], + "篰" => &["bu"], + "篱" => &["li"], + "篲" => &["hui"], + "篳" => &["bi"], + "篴" => &["di"], + "篵" => &["cong"], + "篶" => &["yan"], + "篷" => &["peng"], + "篸" => &["sen"], + "篹" => &["cuan"], + "篺" => &["pai"], + "篻" => &["piao"], + "篼" => &["dou"], + "篽" => &["yu"], + "篾" => &["mie"], + "篿" => &["zhuan"], + "簀" => &["ze","kui"], + "簁" => &["xi"], + "簂" => &["guo"], + "簃" => &["yi"], + "簄" => &["hu"], + "簅" => &["chan"], + "簆" => &["kou"], + "簇" => &["cu"], + "簈" => &["ping"], + "簉" => &["zao"], + "簊" => &["ji"], + "簋" => &["gui"], + "簌" => &["su"], + "簍" => &["lou"], + "簎" => &["zha"], + "簏" => &["lu"], + "簐" => &["nian"], + "簑" => &["suo"], + "簒" => &["cuan"], + "簔" => &["suo"], + "簕" => &["le"], + "簖" => &["duan"], + "簗" => &["liang"], + "簘" => &["xiao"], + "簙" => &["bo"], + "簚" => &["mi"], + "簛" => &["shai"], + "簜" => &["dang"], + "簝" => &["liao"], + "簞" => &["dan"], + "簟" => &["dian"], + "簠" => &["fu"], + "簡" => &["jian"], + "簢" => &["min"], + "簣" => &["kui"], + "簤" => &["dai"], + "簥" => &["qiao"], + "簦" => &["deng"], + "簧" => &["huang"], + "簨" => &["sun"], + "簩" => &["lao"], + "簪" => &["zan"], + "簫" => &["xiao"], + "簬" => &["lu"], + "簭" => &["shi"], + "簮" => &["zan"], + "簰" => &["pai"], + "簱" => &["qi"], + "簲" => &["pai"], + "簳" => &["gan"], + "簴" => &["ju"], + "簵" => &["du"], + "簶" => &["lu"], + "簷" => &["yan"], + "簸" => &["bo"], + "簹" => &["dang"], + "簺" => &["sai"], + "簻" => &["ke"], + "簼" => &["gou"], + "簽" => &["qian"], + "簾" => &["lian"], + "簿" => &["bu"], + "籀" => &["zhou"], + "籁" => &["lai"], + "籃" => &["lan"], + "籄" => &["kui"], + "籅" => &["yu"], + "籆" => &["yue"], + "籇" => &["hao"], + "籈" => &["zhen"], + "籉" => &["tai"], + "籊" => &["ti"], + "籋" => &["mi"], + "籌" => &["chou"], + "籍" => &["ji"], + "籏" => &["qi"], + "籐" => &["teng"], + "籑" => &["zhuan"], + "籒" => &["zhou"], + "籓" => &["fan"], + "籔" => &["sou"], + "籕" => &["zhou"], + "籖" => &["qian"], + "籗" => &["kuo"], + "籘" => &["teng"], + "籙" => &["lu"], + "籚" => &["lu"], + "籛" => &["jian"], + "籜" => &["tuo"], + "籝" => &["ying"], + "籞" => &["yu"], + "籟" => &["lai"], + "籠" => &["long"], + "籢" => &["lian"], + "籣" => &["lan"], + "籤" => &["qian"], + "籥" => &["yue"], + "籦" => &["zhong"], + "籧" => &["qu"], + "籨" => &["lian"], + "籩" => &["bian"], + "籪" => &["duan"], + "籫" => &["zuan"], + "籬" => &["li"], + "籭" => &["shai"], + "籮" => &["luo"], + "籯" => &["ying"], + "籰" => &["yue"], + "籱" => &["zhuo"], + "籲" => &["xu","yu"], + "米" => &["mi"], + "籴" => &["di"], + "籵" => &["fan"], + "籶" => &["shen"], + "籷" => &["zhe"], + "籸" => &["shen"], + "籹" => &["nu:"], + "籺" => &["xie"], + "类" => &["lei"], + "籼" => &["xian"], + "籽" => &["zi"], + "籾" => &["ni"], + "籿" => &["cun"], + "粀" => &["zhang"], + "粁" => &["qian"], + "粃" => &["bi"], + "粄" => &["ban"], + "粅" => &["wu"], + "粆" => &["sha"], + "粇" => &["kang"], + "粈" => &["rou"], + "粉" => &["fen"], + "粊" => &["bi"], + "粋" => &["cui","sui"], + "粌" => &["yin"], + "粍" => &["li"], + "粎" => &["chi"], + "粏" => &["tai"], + "粑" => &["ba"], + "粒" => &["li"], + "粓" => &["gan"], + "粔" => &["ju"], + "粕" => &["po"], + "粖" => &["mo"], + "粗" => &["cu"], + "粘" => &["zhan","nian"], + "粙" => &["zhou"], + "粚" => &["li"], + "粛" => &["su"], + "粜" => &["tiao"], + "粝" => &["li"], + "粞" => &["xi"], + "粟" => &["su"], + "粠" => &["hong"], + "粡" => &["tong"], + "粢" => &["zi","ci"], + "粣" => &["ce"], + "粤" => &["yue"], + "粥" => &["zhou","yu"], + "粦" => &["lin"], + "粧" => &["zhuang"], + "粨" => &["bai"], + "粪" => &["fen"], + "粫" => &["mian"], + "粬" => &["qu"], + "粮" => &["liang"], + "粯" => &["xian"], + "粰" => &["fu"], + "粱" => &["liang"], + "粲" => &["can"], + "粳" => &["jing","geng"], + "粴" => &["li"], + "粵" => &["yue"], + "粶" => &["lu"], + "粷" => &["ju"], + "粸" => &["qi"], + "粹" => &["cui"], + "粺" => &["bai"], + "粻" => &["chang"], + "粼" => &["lin"], + "粽" => &["zong"], + "精" => &["jing"], + "粿" => &["guo"], + "糁" => &["san","shen"], + "糂" => &["san","shen"], + "糃" => &["tang"], + "糄" => &["bian"], + "糅" => &["rou"], + "糆" => &["mian"], + "糇" => &["hou"], + "糈" => &["xu"], + "糉" => &["zong"], + "糊" => &["hu"], + "糋" => &["jian"], + "糌" => &["zan"], + "糍" => &["ci"], + "糎" => &["li"], + "糏" => &["xie"], + "糐" => &["fu"], + "糑" => &["nuo"], + "糒" => &["bei"], + "糓" => &["gu","yu"], + "糔" => &["xiu"], + "糕" => &["gao"], + "糖" => &["tang"], + "糗" => &["qiu"], + "糙" => &["cao"], + "糚" => &["zhuang"], + "糛" => &["tang"], + "糜" => &["mi","mei"], + "糝" => &["san","shen"], + "糞" => &["fen"], + "糟" => &["zao"], + "糠" => &["kang"], + "糡" => &["jiang"], + "糢" => &["mo"], + "糣" => &["san"], + "糤" => &["san"], + "糥" => &["nuo"], + "糦" => &["chi"], + "糧" => &["liang"], + "糨" => &["jiang"], + "糩" => &["kuai"], + "糪" => &["bo"], + "糫" => &["huan"], + "糬" => &["shu"], + "糭" => &["zong"], + "糮" => &["jian"], + "糯" => &["nuo"], + "糰" => &["tuan"], + "糱" => &["nie"], + "糲" => &["li"], + "糳" => &["zuo"], + "糴" => &["di"], + "糵" => &["nie"], + "糶" => &["tiao"], + "糷" => &["lan"], + "糸" => &["mi"], + "糹" => &["mi"], + "糺" => &["jiu"], + "系" => &["xi","ji"], + "糼" => &["gong"], + "糽" => &["zheng"], + "糾" => &["jiu"], + "糿" => &["you"], + "紀" => &["ji"], + "紁" => &["cha"], + "紂" => &["zhou"], + "紃" => &["xun"], + "約" => &["yue","yao"], + "紅" => &["hong","gong"], + "紆" => &["yu"], + "紇" => &["he","ge"], + "紈" => &["wan"], + "紉" => &["ren"], + "紊" => &["wen"], + "紋" => &["wen"], + "紌" => &["qiu"], + "納" => &["na"], + "紎" => &["zi"], + "紏" => &["tou"], + "紐" => &["niu"], + "紑" => &["fou"], + "紒" => &["jie"], + "紓" => &["shu"], + "純" => &["chun"], + "紕" => &["pi"], + "紖" => &["yin"], + "紗" => &["sha"], + "紘" => &["hong"], + "紙" => &["zhi"], + "級" => &["ji"], + "紛" => &["fen"], + "紜" => &["yun"], + "紝" => &["ren"], + "紞" => &["dan"], + "紟" => &["jin"], + "素" => &["su"], + "紡" => &["fang"], + "索" => &["suo"], + "紣" => &["cui"], + "紤" => &["jiu"], + "紥" => &["zha"], + "紦" => &["ba"], + "紧" => &["jin"], + "紨" => &["fu"], + "紩" => &["zhi"], + "紪" => &["qi"], + "紫" => &["zi"], + "紬" => &["chou"], + "紭" => &["hong"], + "紮" => &["zha","za"], + "累" => &["lei"], + "細" => &["xi"], + "紱" => &["fu"], + "紲" => &["xie"], + "紳" => &["shen"], + "紴" => &["bei"], + "紵" => &["zhu"], + "紶" => &["qu"], + "紷" => &["ling"], + "紸" => &["zhu"], + "紹" => &["shao"], + "紺" => &["gan"], + "紻" => &["yang"], + "紼" => &["fu"], + "紽" => &["tuo"], + "紾" => &["zhen"], + "紿" => &["dai"], + "絀" => &["chu"], + "絁" => &["shi"], + "終" => &["zhong"], + "絃" => &["xian"], + "組" => &["zu"], + "絅" => &["jiong"], + "絆" => &["ban"], + "絇" => &["ju"], + "絈" => &["pa"], + "絉" => &["shu"], + "絊" => &["zui"], + "絋" => &["kuang"], + "経" => &["jing"], + "絍" => &["ren"], + "絎" => &["heng","hang"], + "絏" => &["xie"], + "結" => &["jie"], + "絑" => &["zhu"], + "絒" => &["chou"], + "絓" => &["gua"], + "絔" => &["bai"], + "絕" => &["jue"], + "絖" => &["kuang"], + "絗" => &["hu"], + "絘" => &["ci"], + "絙" => &["geng"], + "絚" => &["geng"], + "絛" => &["tao"], + "絜" => &["xie","jie"], + "絝" => &["ku"], + "絞" => &["jiao","jia"], + "絟" => &["quan"], + "絠" => &["gai"], + "絡" => &["luo","lao"], + "絢" => &["xuan"], + "絣" => &["beng","ping"], + "絤" => &["xian"], + "絥" => &["fu"], + "給" => &["gei","ji"], + "絧" => &["tong"], + "絨" => &["rong"], + "絩" => &["tiao"], + "絪" => &["yin"], + "絫" => &["lei"], + "絬" => &["xie"], + "絭" => &["quan"], + "絮" => &["xu"], + "絯" => &["hai"], + "絰" => &["die"], + "統" => &["tong"], + "絲" => &["si"], + "絳" => &["jiang"], + "絴" => &["xiang"], + "絵" => &["hui"], + "絶" => &["jue"], + "絷" => &["zhi"], + "絸" => &["jian"], + "絹" => &["juan"], + "絺" => &["chi"], + "絻" => &["mian"], + "絼" => &["zhen"], + "絽" => &["lu:"], + "絾" => &["cheng"], + "絿" => &["qiu"], + "綀" => &["shu"], + "綁" => &["bang"], + "綂" => &["tong"], + "綃" => &["xiao"], + "綄" => &["wan"], + "綅" => &["qin"], + "綆" => &["geng"], + "綇" => &["xiu"], + "綈" => &["ti","di"], + "綉" => &["xiu"], + "綊" => &["xie"], + "綋" => &["hong"], + "綌" => &["xi"], + "綍" => &["fu"], + "綎" => &["ting"], + "綏" => &["sui"], + "綐" => &["dui"], + "綑" => &["kun"], + "綒" => &["fu"], + "經" => &["jing"], + "綔" => &["hu"], + "綕" => &["zhi"], + "綖" => &["yan"], + "綗" => &["jiong"], + "綘" => &["feng"], + "継" => &["ji"], + "続" => &["xu"], + "綜" => &["zong","zeng"], + "綝" => &["lin"], + "綞" => &["duo"], + "綟" => &["li"], + "綠" => &["lu:"], + "綡" => &["liang"], + "綢" => &["chou"], + "綣" => &["quan"], + "綤" => &["shao"], + "綥" => &["qi"], + "綦" => &["qi"], + "綧" => &["zhun"], + "綨" => &["qi"], + "綩" => &["wan"], + "綪" => &["qian"], + "綫" => &["xian"], + "綬" => &["shou"], + "維" => &["wei"], + "綮" => &["qi","qing"], + "綯" => &["tao"], + "綰" => &["wan"], + "綱" => &["gang"], + "網" => &["wang"], + "綳" => &["beng"], + "綴" => &["zhui"], + "綵" => &["cai"], + "綶" => &["guo"], + "綷" => &["cui"], + "綸" => &["lun","guan"], + "綹" => &["liu"], + "綺" => &["qi"], + "綻" => &["zhan"], + "綼" => &["bei"], + "綽" => &["chuo"], + "綾" => &["ling"], + "綿" => &["mian"], + "緀" => &["qi"], + "緁" => &["jie"], + "緂" => &["tan"], + "緃" => &["zong"], + "緄" => &["gun"], + "緅" => &["zou"], + "緆" => &["yi"], + "緇" => &["zi"], + "緈" => &["xing"], + "緉" => &["liang"], + "緊" => &["jin"], + "緋" => &["fei"], + "緌" => &["rui"], + "緍" => &["min"], + "緎" => &["yu"], + "総" => &["zong"], + "緐" => &["fan"], + "緑" => &["lu:"], + "緒" => &["xu"], + "緔" => &["shang"], + "緖" => &["xu"], + "緗" => &["xiang"], + "緘" => &["jian"], + "緙" => &["ke"], + "線" => &["xian"], + "緛" => &["ruan"], + "緜" => &["mian"], + "緝" => &["ji","qi"], + "緞" => &["duan"], + "緟" => &["zhong"], + "締" => &["di"], + "緡" => &["min"], + "緢" => &["miao"], + "緣" => &["yuan"], + "緤" => &["xie"], + "緥" => &["bao"], + "緦" => &["si"], + "緧" => &["qiu"], + "編" => &["bian"], + "緩" => &["huan"], + "緪" => &["geng"], + "緫" => &["zong"], + "緬" => &["mian"], + "緭" => &["wei"], + "緮" => &["fu"], + "緯" => &["wei"], + "緰" => &["yu"], + "緱" => &["gou"], + "緲" => &["miao"], + "緳" => &["jie"], + "練" => &["lian"], + "緵" => &["zong"], + "緶" => &["bian","pian"], + "緷" => &["yun"], + "緸" => &["yin"], + "緹" => &["ti"], + "緺" => &["gua"], + "緻" => &["zhi"], + "緼" => &["yun"], + "緽" => &["cheng"], + "緾" => &["chan"], + "緿" => &["dai"], + "縀" => &["jia"], + "縁" => &["yuan"], + "縂" => &["zong"], + "縃" => &["xu"], + "縄" => &["sheng"], + "縆" => &["geng"], + "縈" => &["ying"], + "縉" => &["jin"], + "縊" => &["yi"], + "縋" => &["zhui"], + "縌" => &["ni"], + "縍" => &["bang"], + "縎" => &["gu"], + "縏" => &["pan"], + "縐" => &["zhou"], + "縑" => &["jian"], + "縒" => &["cuo"], + "縓" => &["quan"], + "縔" => &["shuang"], + "縕" => &["yun"], + "縖" => &["xia"], + "縗" => &["shuai"], + "縘" => &["xi"], + "縙" => &["rong"], + "縚" => &["tao"], + "縛" => &["fu"], + "縜" => &["yun"], + "縝" => &["zhen"], + "縞" => &["gao"], + "縟" => &["ru"], + "縠" => &["hu"], + "縡" => &["zai"], + "縢" => &["teng"], + "縣" => &["xian","xuan"], + "縤" => &["su"], + "縥" => &["zhen"], + "縦" => &["zong"], + "縧" => &["tao"], + "縨" => &["huang"], + "縩" => &["cai"], + "縪" => &["bi","bie"], + "縫" => &["feng"], + "縬" => &["cu"], + "縭" => &["li"], + "縮" => &["suo","su"], + "縯" => &["yin"], + "縰" => &["xi"], + "縱" => &["zong"], + "縲" => &["lei"], + "縳" => &["zhuan"], + "縴" => &["qian"], + "縵" => &["man"], + "縶" => &["zhi"], + "縷" => &["lu:"], + "縸" => &["mo"], + "縹" => &["piao"], + "縺" => &["lian"], + "縻" => &["mi"], + "縼" => &["xuan"], + "總" => &["zong"], + "績" => &["ji"], + "縿" => &["shan"], + "繀" => &["sui"], + "繁" => &["fan","po"], + "繂" => &["shuai"], + "繃" => &["beng"], + "繄" => &["yi"], + "繅" => &["sao"], + "繆" => &["mou","miao","miu"], + "繇" => &["zhou","yao","you"], + "繈" => &["qiang"], + "繉" => &["hun"], + "繊" => &["xian"], + "繋" => &["xi","ji"], + "繍" => &["xiu"], + "繎" => &["ran"], + "繏" => &["xuan"], + "繐" => &["hui"], + "繑" => &["qiao"], + "繒" => &["zeng"], + "繓" => &["zuo"], + "織" => &["zhi"], + "繕" => &["shan"], + "繖" => &["san"], + "繗" => &["lin"], + "繘" => &["yu"], + "繙" => &["fan"], + "繚" => &["liao"], + "繛" => &["chuo"], + "繜" => &["zun"], + "繝" => &["jian"], + "繞" => &["rao"], + "繟" => &["chan"], + "繠" => &["rui"], + "繡" => &["xiu"], + "繢" => &["hui"], + "繣" => &["hua"], + "繤" => &["zuan"], + "繥" => &["xi"], + "繦" => &["qiang"], + "繨" => &["da"], + "繩" => &["sheng"], + "繪" => &["hui"], + "繫" => &["xi","ji"], + "繬" => &["se"], + "繭" => &["jian"], + "繮" => &["jiang"], + "繯" => &["huan"], + "繰" => &["qiao","zao"], + "繱" => &["cong"], + "繲" => &["jie"], + "繳" => &["jiao","jia","zhuo"], + "繴" => &["bo"], + "繵" => &["chan"], + "繶" => &["yi"], + "繷" => &["nao"], + "繸" => &["sui"], + "繹" => &["yi"], + "繺" => &["shai"], + "繻" => &["xu"], + "繼" => &["ji"], + "繽" => &["bin"], + "繾" => &["qian"], + "繿" => &["jian","kan"], + "纀" => &["pu"], + "纁" => &["xun"], + "纂" => &["zuan"], + "纃" => &["qi"], + "纄" => &["peng"], + "纅" => &["li"], + "纆" => &["mo"], + "纇" => &["lei"], + "纈" => &["xie"], + "纉" => &["zuan"], + "纊" => &["kuang"], + "纋" => &["you"], + "續" => &["xu"], + "纍" => &["lei"], + "纎" => &["xian"], + "纏" => &["chan"], + "纑" => &["lu"], + "纒" => &["chan"], + "纓" => &["ying"], + "纔" => &["cai"], + "纕" => &["xiang"], + "纖" => &["xian"], + "纗" => &["zui"], + "纘" => &["zuan"], + "纙" => &["luo"], + "纚" => &["xi"], + "纛" => &["dao","du"], + "纜" => &["lan"], + "纝" => &["lei"], + "纞" => &["lian"], + "纟" => &["mi"], + "纠" => &["jiu"], + "纡" => &["yu"], + "红" => &["hong","gong"], + "纣" => &["zhou"], + "纤" => &["xian","qian"], + "纥" => &["he","ge"], + "约" => &["yue","yao"], + "级" => &["ji"], + "纨" => &["wan"], + "纩" => &["kuang"], + "纪" => &["ji"], + "纫" => &["ren"], + "纬" => &["wei"], + "纭" => &["yun"], + "纮" => &["hong"], + "纯" => &["chun"], + "纰" => &["pi"], + "纱" => &["sha"], + "纲" => &["gang"], + "纳" => &["na"], + "纴" => &["ren"], + "纵" => &["zong"], + "纶" => &["lun","guan"], + "纷" => &["fen"], + "纸" => &["zhi"], + "纹" => &["wen"], + "纺" => &["fang"], + "纻" => &["zhu"], + "纼" => &["zhen"], + "纽" => &["niu"], + "纾" => &["shu"], + "线" => &["xian"], + "绀" => &["gan"], + "绁" => &["xie"], + "绂" => &["fu"], + "练" => &["lian"], + "组" => &["zu"], + "绅" => &["shen"], + "细" => &["xi"], + "织" => &["zhi"], + "终" => &["zhong"], + "绉" => &["zhou"], + "绊" => &["ban"], + "绋" => &["fu"], + "绌" => &["chu"], + "绍" => &["shao"], + "绎" => &["yi"], + "经" => &["jing"], + "绐" => &["dai"], + "绑" => &["bang"], + "绒" => &["rong"], + "结" => &["jie"], + "绔" => &["ku"], + "绕" => &["rao"], + "绖" => &["die"], + "绗" => &["hang"], + "绘" => &["hui"], + "给" => &["gei","ji"], + "绚" => &["xuan"], + "绛" => &["jiang"], + "络" => &["luo","lao"], + "绝" => &["jue"], + "绞" => &["jiao","jia"], + "统" => &["tong"], + "绠" => &["geng"], + "绡" => &["xiao"], + "绢" => &["juan"], + "绣" => &["xiu"], + "绤" => &["xi"], + "绥" => &["sui"], + "绦" => &["tao"], + "继" => &["ji"], + "绨" => &["ti","di"], + "绩" => &["ji"], + "绪" => &["xu"], + "绫" => &["ling"], + "绬" => &["yin"], + "续" => &["xu"], + "绮" => &["qi"], + "绯" => &["fei"], + "绰" => &["chuo","chao"], + "绱" => &["shang"], + "绲" => &["gun"], + "绳" => &["sheng"], + "维" => &["wei"], + "绵" => &["mian"], + "绶" => &["shou"], + "绷" => &["beng"], + "绸" => &["chou"], + "绹" => &["tao"], + "绺" => &["liu"], + "绻" => &["quan"], + "综" => &["zong","zeng"], + "绽" => &["zhan"], + "绾" => &["wan"], + "绿" => &["lu:","lu"], + "缀" => &["zhui"], + "缁" => &["zi"], + "缂" => &["ke"], + "缃" => &["xiang"], + "缄" => &["jian"], + "缅" => &["mian"], + "缆" => &["lan"], + "缇" => &["ti"], + "缈" => &["miao"], + "缉" => &["ji","qi"], + "缊" => &["yun"], + "缋" => &["hui"], + "缌" => &["si"], + "缍" => &["duo"], + "缎" => &["duan"], + "缏" => &["bian","pian"], + "缐" => &["xian"], + "缑" => &["gou"], + "缒" => &["zhui"], + "缓" => &["huan"], + "缔" => &["di"], + "缕" => &["lu:"], + "编" => &["bian"], + "缗" => &["min"], + "缘" => &["yuan"], + "缙" => &["jin"], + "缚" => &["fu"], + "缛" => &["ru"], + "缜" => &["zhen"], + "缝" => &["feng"], + "缞" => &["cui"], + "缟" => &["gao"], + "缠" => &["chan"], + "缡" => &["li"], + "缢" => &["yi"], + "缣" => &["jian"], + "缤" => &["bin"], + "缥" => &["piao"], + "缦" => &["man"], + "缧" => &["lei"], + "缨" => &["ying"], + "缩" => &["suo","su"], + "缪" => &["mou","miao","miu"], + "缫" => &["sao"], + "缬" => &["xie"], + "缭" => &["liao"], + "缮" => &["shan"], + "缯" => &["zeng"], + "缰" => &["jiang"], + "缱" => &["qian"], + "缲" => &["qiao","sao","zao"], + "缳" => &["huan"], + "缴" => &["jiao","zhuo","jia"], + "缵" => &["zuan"], + "缶" => &["fou"], + "缷" => &["xie"], + "缸" => &["gang"], + "缹" => &["fou"], + "缺" => &["que"], + "缻" => &["fou"], + "缼" => &["que"], + "缽" => &["bo"], + "缾" => &["ping"], + "缿" => &["hou"], + "罁" => &["gang"], + "罂" => &["ying"], + "罃" => &["ying"], + "罄" => &["qing"], + "罅" => &["xia"], + "罆" => &["guan"], + "罇" => &["zun"], + "罈" => &["tan"], + "罊" => &["qing"], + "罋" => &["weng"], + "罌" => &["ying"], + "罍" => &["lei"], + "罎" => &["tan"], + "罏" => &["lu"], + "罐" => &["guan"], + "网" => &["wang"], + "罒" => &["gang"], + "罓" => &["wang"], + "罔" => &["wang"], + "罕" => &["han"], + "罗" => &["luo"], + "罘" => &["fu","fou"], + "罙" => &["mi"], + "罚" => &["fa"], + "罛" => &["gu"], + "罜" => &["zhu"], + "罝" => &["ju"], + "罞" => &["mao"], + "罟" => &["gu"], + "罠" => &["min"], + "罡" => &["gang"], + "罢" => &["ba"], + "罣" => &["gua"], + "罤" => &["ti"], + "罥" => &["juan"], + "罦" => &["fu"], + "罧" => &["lin","sen"], + "罨" => &["yan"], + "罩" => &["zhao"], + "罪" => &["zui"], + "罫" => &["gua"], + "罬" => &["zhuo"], + "罭" => &["yu"], + "置" => &["zhi"], + "罯" => &["an"], + "罰" => &["fa"], + "罱" => &["lan"], + "署" => &["shu"], + "罳" => &["si"], + "罴" => &["pi"], + "罵" => &["ma"], + "罶" => &["liu"], + "罷" => &["ba","pi"], + "罸" => &["fa"], + "罹" => &["li"], + "罺" => &["chao"], + "罻" => &["wei"], + "罼" => &["bi"], + "罽" => &["ji"], + "罾" => &["zeng"], + "罿" => &["tong"], + "羀" => &["liu"], + "羁" => &["ji"], + "羂" => &["juan"], + "羃" => &["mi"], + "羄" => &["zhao"], + "羅" => &["luo"], + "羆" => &["pi","biao"], + "羇" => &["ji"], + "羈" => &["ji"], + "羉" => &["luan"], + "羊" => &["yang"], + "羋" => &["mie"], + "羌" => &["qiang"], + "羍" => &["ta"], + "美" => &["mei"], + "羏" => &["yang"], + "羐" => &["you"], + "羑" => &["you"], + "羒" => &["fen"], + "羓" => &["ba"], + "羔" => &["gao"], + "羕" => &["yang"], + "羖" => &["gu"], + "羗" => &["qiang"], + "羘" => &["zang"], + "羙" => &["gao"], + "羚" => &["ling"], + "羛" => &["yi"], + "羜" => &["zhu"], + "羝" => &["di"], + "羞" => &["xiu"], + "羟" => &["qiang"], + "羠" => &["yi"], + "羡" => &["xian"], + "羢" => &["rong"], + "羣" => &["qun"], + "群" => &["qun"], + "羥" => &["qian","qiang"], + "羦" => &["huan"], + "羧" => &["suo"], + "羨" => &["xian"], + "義" => &["yi"], + "羪" => &["yang"], + "羫" => &["qiang"], + "羬" => &["xian"], + "羭" => &["yu"], + "羮" => &["geng"], + "羯" => &["jie"], + "羰" => &["tang"], + "羱" => &["yuan"], + "羲" => &["xi"], + "羳" => &["fan"], + "羴" => &["shan"], + "羵" => &["fen"], + "羶" => &["shan"], + "羷" => &["lian"], + "羸" => &["lei"], + "羹" => &["geng"], + "羺" => &["nou"], + "羻" => &["qiang"], + "羼" => &["chan"], + "羽" => &["yu"], + "羾" => &["gong"], + "羿" => &["yi"], + "翀" => &["chong"], + "翁" => &["weng"], + "翂" => &["fen"], + "翃" => &["hong"], + "翄" => &["chi"], + "翅" => &["chi"], + "翆" => &["cui"], + "翇" => &["fu","pei"], + "翈" => &["xia"], + "翉" => &["pen"], + "翊" => &["yi"], + "翋" => &["la"], + "翌" => &["yi"], + "翍" => &["pi","po"], + "翎" => &["ling"], + "翏" => &["liu"], + "翐" => &["zhi"], + "翑" => &["qu"], + "習" => &["xi"], + "翓" => &["xie"], + "翔" => &["xiang"], + "翕" => &["xi"], + "翖" => &["xi"], + "翗" => &["qi"], + "翘" => &["qiao"], + "翙" => &["hui"], + "翚" => &["hui"], + "翛" => &["shu"], + "翜" => &["se"], + "翝" => &["hong"], + "翞" => &["jiang"], + "翟" => &["zhai","di"], + "翠" => &["cui"], + "翡" => &["fei"], + "翢" => &["tao"], + "翣" => &["sha"], + "翤" => &["chi"], + "翥" => &["zhu"], + "翦" => &["jian"], + "翧" => &["xuan"], + "翨" => &["shi"], + "翩" => &["pian"], + "翪" => &["zong"], + "翫" => &["wan"], + "翬" => &["hui"], + "翭" => &["hou"], + "翮" => &["he"], + "翯" => &["he"], + "翰" => &["han"], + "翱" => &["ao"], + "翲" => &["piao"], + "翳" => &["yi"], + "翴" => &["lian"], + "翵" => &["qu"], + "翷" => &["lin"], + "翸" => &["pen"], + "翹" => &["qiao"], + "翺" => &["ao"], + "翻" => &["fan"], + "翼" => &["yi"], + "翽" => &["hui"], + "翾" => &["xuan"], + "翿" => &["dao"], + "耀" => &["yao","yue"], + "老" => &["lao"], + "考" => &["kao"], + "耄" => &["mao"], + "者" => &["zhe"], + "耆" => &["qi"], + "耇" => &["gou"], + "耈" => &["gou"], + "耉" => &["gou"], + "耊" => &["die"], + "耋" => &["die"], + "而" => &["er"], + "耍" => &["shua"], + "耎" => &["ruan"], + "耏" => &["er"], + "耐" => &["nai"], + "耑" => &["zhuan","duan"], + "耒" => &["lei"], + "耓" => &["ting"], + "耔" => &["zi"], + "耕" => &["geng"], + "耖" => &["chao"], + "耗" => &["hao"], + "耘" => &["yun"], + "耙" => &["pa","ba"], + "耚" => &["pi"], + "耛" => &["chi"], + "耜" => &["si"], + "耝" => &["qu"], + "耞" => &["jia"], + "耟" => &["ju"], + "耠" => &["huo"], + "耡" => &["chu"], + "耢" => &["lao"], + "耣" => &["lun"], + "耤" => &["ji"], + "耥" => &["tang"], + "耦" => &["ou"], + "耧" => &["lou"], + "耨" => &["nou"], + "耩" => &["jiang"], + "耪" => &["pang"], + "耫" => &["ze"], + "耬" => &["lou"], + "耭" => &["ji"], + "耮" => &["lao"], + "耯" => &["huo"], + "耰" => &["you"], + "耱" => &["mo"], + "耲" => &["huai"], + "耳" => &["er"], + "耴" => &["zhe"], + "耵" => &["ding"], + "耶" => &["ye"], + "耷" => &["da"], + "耸" => &["song"], + "耹" => &["qin"], + "耺" => &["yun"], + "耻" => &["chi"], + "耼" => &["dan"], + "耽" => &["dan"], + "耾" => &["hong"], + "耿" => &["geng"], + "聀" => &["zhi"], + "聂" => &["nie"], + "聃" => &["dan"], + "聄" => &["zhen"], + "聅" => &["che"], + "聆" => &["ling"], + "聇" => &["zheng"], + "聈" => &["you"], + "聉" => &["wa"], + "聊" => &["liao"], + "聋" => &["long"], + "职" => &["zhi"], + "聍" => &["ning"], + "聎" => &["tiao"], + "聏" => &["er"], + "聐" => &["ya"], + "聑" => &["die"], + "聒" => &["guo","gua"], + "联" => &["lian"], + "聕" => &["hao"], + "聖" => &["sheng"], + "聗" => &["lie"], + "聘" => &["pin"], + "聙" => &["jing"], + "聚" => &["ju"], + "聛" => &["bi"], + "聜" => &["di"], + "聝" => &["guo"], + "聞" => &["wen"], + "聟" => &["xu"], + "聠" => &["ping"], + "聡" => &["cong"], + "聤" => &["ting"], + "聥" => &["yu"], + "聦" => &["cong"], + "聧" => &["kui"], + "聨" => &["lian"], + "聩" => &["kui"], + "聪" => &["cong"], + "聫" => &["lian"], + "聬" => &["weng"], + "聭" => &["kui"], + "聮" => &["lian"], + "聯" => &["lian"], + "聰" => &["cong"], + "聱" => &["ao"], + "聲" => &["sheng"], + "聳" => &["song"], + "聴" => &["ting"], + "聵" => &["kui"], + "聶" => &["nie"], + "職" => &["zhi"], + "聸" => &["dan"], + "聹" => &["ning"], + "聻" => &["ji"], + "聼" => &["ting"], + "聽" => &["ting"], + "聾" => &["long"], + "聿" => &["yu"], + "肀" => &["yu"], + "肁" => &["zhao"], + "肂" => &["si"], + "肃" => &["su"], + "肄" => &["yi"], + "肅" => &["su"], + "肆" => &["si"], + "肇" => &["zhao"], + "肈" => &["zhao"], + "肉" => &["rou"], + "肊" => &["yi"], + "肋" => &["lei","le"], + "肌" => &["ji"], + "肍" => &["qiu"], + "肎" => &["ken"], + "肏" => &["cao"], + "肐" => &["ge"], + "肑" => &["di"], + "肒" => &["huan"], + "肓" => &["huang"], + "肔" => &["yi"], + "肕" => &["ren"], + "肖" => &["xiao"], + "肗" => &["ru"], + "肘" => &["zhou"], + "肙" => &["yuan"], + "肚" => &["du"], + "肛" => &["gang"], + "肜" => &["rong"], + "肝" => &["gan"], + "肞" => &["cha"], + "肟" => &["wo"], + "肠" => &["chang"], + "股" => &["gu"], + "肢" => &["zhi"], + "肣" => &["qin"], + "肤" => &["fu"], + "肥" => &["fei"], + "肦" => &["ban"], + "肧" => &["pei"], + "肨" => &["pang"], + "肩" => &["jian"], + "肪" => &["fang"], + "肫" => &["zhun"], + "肬" => &["you"], + "肭" => &["na"], + "肮" => &["ang"], + "肯" => &["ken"], + "肰" => &["ran"], + "肱" => &["gong"], + "育" => &["yu","yo"], + "肳" => &["wen"], + "肴" => &["yao"], + "肵" => &["jin"], + "肶" => &["pi"], + "肷" => &["qian"], + "肸" => &["xi"], + "肹" => &["xi"], + "肺" => &["fei"], + "肻" => &["ken"], + "肼" => &["jing"], + "肽" => &["tai"], + "肾" => &["shen"], + "肿" => &["zhong"], + "胀" => &["zhang"], + "胁" => &["xie"], + "胂" => &["shen"], + "胃" => &["wei"], + "胄" => &["zhou"], + "胅" => &["die"], + "胆" => &["dan"], + "胇" => &["fei"], + "胈" => &["ba"], + "胉" => &["bo"], + "胊" => &["qu"], + "胋" => &["tian"], + "背" => &["bei"], + "胍" => &["gua"], + "胎" => &["tai"], + "胏" => &["zi"], + "胐" => &["ku"], + "胑" => &["zhi"], + "胒" => &["ni"], + "胓" => &["ping"], + "胔" => &["zi"], + "胕" => &["fu"], + "胖" => &["pang","pan"], + "胗" => &["zhen"], + "胘" => &["xian"], + "胙" => &["zuo"], + "胚" => &["pei"], + "胛" => &["jia"], + "胜" => &["sheng"], + "胝" => &["zhi"], + "胞" => &["bao"], + "胟" => &["mu"], + "胠" => &["qu"], + "胡" => &["hu"], + "胢" => &["ke"], + "胣" => &["yi"], + "胤" => &["yin"], + "胥" => &["xu"], + "胦" => &["yang"], + "胧" => &["long"], + "胨" => &["dong"], + "胩" => &["ka"], + "胪" => &["lu"], + "胫" => &["jing"], + "胬" => &["nu"], + "胭" => &["yan"], + "胮" => &["pang"], + "胯" => &["kua"], + "胰" => &["yi"], + "胱" => &["guang"], + "胲" => &["hai","gai"], + "胳" => &["ge","ga"], + "胴" => &["dong"], + "胵" => &["zhi"], + "胶" => &["jiao"], + "胷" => &["xiong"], + "胸" => &["xiong"], + "胹" => &["er"], + "胺" => &["an"], + "胻" => &["xing"], + "胼" => &["pian"], + "能" => &["neng"], + "胾" => &["zi"], + "脀" => &["cheng"], + "脁" => &["tiao"], + "脂" => &["zhi"], + "脃" => &["cui"], + "脄" => &["mei"], + "脅" => &["xie"], + "脆" => &["cui"], + "脇" => &["xie"], + "脈" => &["mo","mai"], + "脉" => &["mai","mo"], + "脊" => &["ji"], + "脋" => &["xie"], + "脍" => &["kuai"], + "脎" => &["sa"], + "脏" => &["zang"], + "脐" => &["qi"], + "脑" => &["nao"], + "脒" => &["mi"], + "脓" => &["nong"], + "脔" => &["luan"], + "脕" => &["wan"], + "脖" => &["bo"], + "脗" => &["wen"], + "脘" => &["wan"], + "脙" => &["qiu"], + "脚" => &["jiao","jue","jia"], + "脛" => &["jing"], + "脜" => &["you"], + "脝" => &["heng"], + "脞" => &["cuo"], + "脟" => &["lie"], + "脠" => &["shan"], + "脡" => &["ting"], + "脢" => &["mei"], + "脣" => &["chun"], + "脤" => &["shen"], + "脥" => &["xie"], + "脧" => &["juan"], + "脨" => &["cu"], + "脩" => &["xiu"], + "脪" => &["xin"], + "脫" => &["tuo"], + "脬" => &["pao"], + "脭" => &["cheng"], + "脮" => &["nei"], + "脯" => &["fu","pu"], + "脰" => &["dou"], + "脱" => &["tuo"], + "脲" => &["niao"], + "脳" => &["nao"], + "脴" => &["pi"], + "脵" => &["gu"], + "脶" => &["luo"], + "脷" => &["li"], + "脸" => &["lian"], + "脹" => &["zhang"], + "脺" => &["cui"], + "脻" => &["jie"], + "脼" => &["liang"], + "脽" => &["shui"], + "脾" => &["pi"], + "脿" => &["biao"], + "腀" => &["lun"], + "腁" => &["pian"], + "腂" => &["guo"], + "腃" => &["juan"], + "腄" => &["chui","zhui"], + "腅" => &["dan"], + "腆" => &["tian"], + "腇" => &["nei"], + "腈" => &["jing"], + "腉" => &["jie"], + "腊" => &["la","xi"], + "腋" => &["ye","yi"], + "腌" => &["yan","a"], + "腍" => &["ren"], + "腎" => &["shen"], + "腏" => &["chuo","duo"], + "腐" => &["fu"], + "腑" => &["fu"], + "腒" => &["ju"], + "腓" => &["fei"], + "腔" => &["qiang"], + "腕" => &["wan"], + "腖" => &["dong"], + "腗" => &["pi"], + "腘" => &["guo"], + "腙" => &["zong"], + "腚" => &["ding"], + "腛" => &["wu"], + "腜" => &["mei"], + "腝" => &["ruan"], + "腞" => &["zhuan","dun"], + "腟" => &["zhi"], + "腠" => &["cou"], + "腡" => &["gua","luo"], + "腢" => &["ou"], + "腣" => &["di"], + "腤" => &["an"], + "腥" => &["xing"], + "腦" => &["nao"], + "腧" => &["shu"], + "腨" => &["shuan"], + "腩" => &["nan"], + "腪" => &["yun"], + "腫" => &["zhong"], + "腬" => &["rou"], + "腭" => &["e"], + "腮" => &["sai"], + "腯" => &["tu"], + "腰" => &["yao"], + "腱" => &["jian"], + "腲" => &["wei"], + "腳" => &["jiao"], + "腴" => &["yu"], + "腵" => &["jia"], + "腶" => &["duan"], + "腷" => &["bi"], + "腸" => &["chang"], + "腹" => &["fu"], + "腺" => &["xian"], + "腻" => &["ni"], + "腼" => &["mian"], + "腽" => &["wa"], + "腾" => &["teng"], + "腿" => &["tui"], + "膀" => &["bang","pang"], + "膁" => &["qian"], + "膂" => &["lu:"], + "膃" => &["wa"], + "膄" => &["shou"], + "膅" => &["tang"], + "膆" => &["su"], + "膇" => &["zhui"], + "膈" => &["ge"], + "膉" => &["yi"], + "膊" => &["bo"], + "膋" => &["liao"], + "膌" => &["ji"], + "膍" => &["pi"], + "膎" => &["xie"], + "膏" => &["gao"], + "膐" => &["lu:"], + "膑" => &["bin"], + "膓" => &["chang"], + "膔" => &["lu"], + "膕" => &["guo"], + "膖" => &["pang"], + "膗" => &["chuai"], + "膘" => &["biao"], + "膙" => &["jiang"], + "膚" => &["fu"], + "膛" => &["tang"], + "膜" => &["mo"], + "膝" => &["xi"], + "膞" => &["zhuan"], + "膟" => &["lu:"], + "膠" => &["jiao"], + "膡" => &["ying"], + "膢" => &["lu:"], + "膣" => &["zhi"], + "膤" => &["xue"], + "膥" => &["chun"], + "膦" => &["lin"], + "膧" => &["tong"], + "膨" => &["peng"], + "膩" => &["ni"], + "膪" => &["chuai"], + "膫" => &["liao"], + "膬" => &["cui"], + "膭" => &["gui"], + "膮" => &["xiao"], + "膯" => &["teng"], + "膰" => &["fan"], + "膱" => &["zhi"], + "膲" => &["jiao"], + "膳" => &["shan"], + "膴" => &["hu"], + "膵" => &["cui"], + "膶" => &["run"], + "膷" => &["xin"], + "膸" => &["sui"], + "膹" => &["fen"], + "膺" => &["ying"], + "膻" => &["shan"], + "膼" => &["gua"], + "膽" => &["dan"], + "膾" => &["kuai"], + "膿" => &["nong"], + "臀" => &["tun"], + "臁" => &["lian"], + "臂" => &["bi","bei"], + "臃" => &["yong"], + "臄" => &["jue"], + "臅" => &["chu"], + "臆" => &["yi"], + "臇" => &["juan"], + "臈" => &["la","xi"], + "臉" => &["lian"], + "臊" => &["sao"], + "臋" => &["tun"], + "臌" => &["gu"], + "臍" => &["qi"], + "臎" => &["cui"], + "臏" => &["bin"], + "臐" => &["xun"], + "臑" => &["nao","ru"], + "臒" => &["huo"], + "臓" => &["zang"], + "臔" => &["xian"], + "臕" => &["biao"], + "臖" => &["xing"], + "臗" => &["kuan"], + "臘" => &["la","xi"], + "臙" => &["yan"], + "臚" => &["lu"], + "臛" => &["hu"], + "臜" => &["za"], + "臝" => &["luo"], + "臞" => &["qu"], + "臟" => &["zang"], + "臠" => &["luan"], + "臡" => &["ni"], + "臢" => &["za"], + "臣" => &["chen"], + "臤" => &["qian"], + "臥" => &["wo"], + "臦" => &["guang","wang"], + "臧" => &["zang"], + "臨" => &["lin"], + "臩" => &["guang"], + "自" => &["zi"], + "臫" => &["jiao"], + "臬" => &["nie"], + "臭" => &["chou","xiu"], + "臮" => &["ji"], + "臯" => &["gao"], + "臰" => &["chou","xiu"], + "臱" => &["mian"], + "臲" => &["nie"], + "至" => &["zhi"], + "致" => &["zhi"], + "臵" => &["ge"], + "臶" => &["jian"], + "臷" => &["die"], + "臸" => &["zhi"], + "臹" => &["xiu"], + "臺" => &["tai"], + "臻" => &["zhen"], + "臼" => &["jiu"], + "臽" => &["xian"], + "臾" => &["yu"], + "臿" => &["cha"], + "舀" => &["yao"], + "舁" => &["yu"], + "舂" => &["chong"], + "舃" => &["xi"], + "舄" => &["xi"], + "舅" => &["jiu"], + "舆" => &["yu"], + "與" => &["yu"], + "興" => &["xing"], + "舉" => &["ju"], + "舊" => &["jiu"], + "舋" => &["xin"], + "舌" => &["she"], + "舍" => &["she"], + "舎" => &["she"], + "舏" => &["jiu"], + "舐" => &["shi"], + "舑" => &["tan"], + "舒" => &["shu"], + "舓" => &["shi"], + "舔" => &["tian"], + "舕" => &["dan"], + "舖" => &["pu"], + "舗" => &["pu"], + "舘" => &["guan"], + "舙" => &["hua"], + "舚" => &["tian"], + "舛" => &["chuan"], + "舜" => &["shun"], + "舝" => &["xia"], + "舞" => &["wu"], + "舟" => &["zhou"], + "舠" => &["dao"], + "舡" => &["chuan"], + "舢" => &["shan"], + "舣" => &["yi"], + "舥" => &["pa"], + "舦" => &["tai"], + "舧" => &["fan"], + "舨" => &["ban"], + "舩" => &["chuan"], + "航" => &["hang"], + "舫" => &["fang"], + "般" => &["ban","bo","pan"], + "舭" => &["bi"], + "舮" => &["lu"], + "舯" => &["zhong"], + "舰" => &["jian"], + "舱" => &["cang"], + "舲" => &["ling"], + "舳" => &["zhu"], + "舴" => &["ze"], + "舵" => &["duo","tuo"], + "舶" => &["bo"], + "舷" => &["xian"], + "舸" => &["ge"], + "船" => &["chuan"], + "舺" => &["jia","xia"], + "舻" => &["lu"], + "舼" => &["hong"], + "舽" => &["pang"], + "舾" => &["xi"], + "艀" => &["fu"], + "艁" => &["zao"], + "艂" => &["feng"], + "艃" => &["li"], + "艄" => &["shao"], + "艅" => &["yu"], + "艆" => &["lang"], + "艇" => &["ting"], + "艉" => &["wei"], + "艊" => &["bo"], + "艋" => &["meng"], + "艌" => &["nian"], + "艍" => &["ju"], + "艎" => &["huang"], + "艏" => &["shou"], + "艐" => &["zong"], + "艑" => &["bian"], + "艒" => &["mao"], + "艓" => &["die"], + "艕" => &["bang"], + "艖" => &["cha"], + "艗" => &["yi"], + "艘" => &["sou","sao"], + "艙" => &["cang"], + "艚" => &["cao"], + "艛" => &["lou"], + "艜" => &["dai"], + "艞" => &["yao"], + "艟" => &["chong","tong"], + "艡" => &["dang"], + "艢" => &["qiang"], + "艣" => &["lu"], + "艤" => &["yi"], + "艥" => &["jie"], + "艦" => &["jian"], + "艧" => &["huo"], + "艨" => &["meng"], + "艩" => &["qi"], + "艪" => &["lu"], + "艫" => &["lu"], + "艬" => &["chan"], + "艭" => &["shuang"], + "艮" => &["gen"], + "良" => &["liang"], + "艰" => &["jian"], + "艱" => &["jian"], + "色" => &["se","shai"], + "艳" => &["yan"], + "艴" => &["fu"], + "艵" => &["ping"], + "艶" => &["yan"], + "艷" => &["yan"], + "艸" => &["cao"], + "艹" => &["cao"], + "艺" => &["yi"], + "艻" => &["le"], + "艼" => &["ting"], + "艽" => &["jiao"], + "艾" => &["ai","yi"], + "艿" => &["nai"], + "芀" => &["tiao"], + "芁" => &["jiao"], + "节" => &["jie"], + "芃" => &["peng"], + "芄" => &["wan"], + "芅" => &["yi"], + "芆" => &["chai"], + "芇" => &["mian"], + "芈" => &["mi"], + "芉" => &["gan"], + "芊" => &["qian"], + "芋" => &["yu"], + "芌" => &["yu"], + "芍" => &["shao"], + "芎" => &["xiong"], + "芏" => &["du"], + "芐" => &["xia"], + "芑" => &["qi"], + "芒" => &["mang","wang"], + "芓" => &["zi"], + "芔" => &["hui"], + "芕" => &["sui"], + "芖" => &["zhi"], + "芗" => &["xiang"], + "芘" => &["bi","pi"], + "芙" => &["fu"], + "芚" => &["tun"], + "芛" => &["wei"], + "芜" => &["wu"], + "芝" => &["zhi"], + "芞" => &["qi"], + "芟" => &["shan"], + "芠" => &["wen"], + "芡" => &["qian"], + "芢" => &["ren"], + "芣" => &["fou"], + "芤" => &["kou"], + "芥" => &["jie","gai"], + "芦" => &["lu"], + "芧" => &["zhu"], + "芨" => &["ji"], + "芩" => &["qin"], + "芪" => &["qi"], + "芫" => &["yan","yuan"], + "芬" => &["fen"], + "芭" => &["ba"], + "芮" => &["rui"], + "芯" => &["xin"], + "芰" => &["ji"], + "花" => &["hua"], + "芲" => &["hua"], + "芳" => &["fang"], + "芴" => &["wu"], + "芵" => &["jue"], + "芶" => &["gou"], + "芷" => &["zhi"], + "芸" => &["yun"], + "芹" => &["qin"], + "芺" => &["ao"], + "芻" => &["chu"], + "芼" => &["mao"], + "芽" => &["ya","di"], + "芾" => &["fei","fu"], + "芿" => &["reng"], + "苀" => &["hang"], + "苁" => &["cong"], + "苂" => &["yin"], + "苃" => &["you"], + "苄" => &["bian"], + "苅" => &["yi"], + "苇" => &["wei"], + "苈" => &["li"], + "苉" => &["pi"], + "苊" => &["e"], + "苋" => &["xian"], + "苌" => &["chang"], + "苍" => &["cang"], + "苎" => &["zhu","ning"], + "苏" => &["su"], + "苐" => &["yi","ti"], + "苑" => &["yuan"], + "苒" => &["ran"], + "苓" => &["ling"], + "苔" => &["tai"], + "苕" => &["tiao","shao"], + "苖" => &["di"], + "苗" => &["miao"], + "苘" => &["qing"], + "苙" => &["li"], + "苚" => &["rao"], + "苛" => &["ke"], + "苜" => &["mu"], + "苝" => &["pei"], + "苞" => &["bao"], + "苟" => &["gou"], + "苠" => &["min"], + "苡" => &["yi"], + "苢" => &["yi"], + "苣" => &["ju","qu"], + "苤" => &["pie"], + "若" => &["ruo","re"], + "苦" => &["ku"], + "苧" => &["zhu","ning"], + "苨" => &["ni"], + "苩" => &["bo"], + "苪" => &["bing"], + "苫" => &["shan"], + "苬" => &["qiu"], + "苭" => &["yao"], + "苮" => &["xian"], + "苯" => &["ben"], + "苰" => &["hong"], + "英" => &["ying"], + "苲" => &["zha"], + "苳" => &["dong"], + "苴" => &["ju"], + "苵" => &["die"], + "苶" => &["nie"], + "苷" => &["gan"], + "苸" => &["hu"], + "苹" => &["ping","pin"], + "苺" => &["mei"], + "苻" => &["fu"], + "苼" => &["sheng"], + "苽" => &["gu"], + "苾" => &["bi"], + "苿" => &["wei"], + "茀" => &["fu"], + "茁" => &["zhuo"], + "茂" => &["mao"], + "范" => &["fan"], + "茄" => &["qie","jia"], + "茅" => &["mao"], + "茆" => &["mao"], + "茇" => &["ba"], + "茈" => &["zi","ci"], + "茉" => &["mo"], + "茊" => &["zi"], + "茋" => &["di"], + "茌" => &["chi"], + "茍" => &["gou"], + "茎" => &["jing"], + "茏" => &["long"], + "茑" => &["niao"], + "茓" => &["xue"], + "茔" => &["ying"], + "茕" => &["qiong"], + "茖" => &["ge"], + "茗" => &["ming"], + "茘" => &["li"], + "茙" => &["rong"], + "茚" => &["yin"], + "茛" => &["gen"], + "茜" => &["qian","xi"], + "茝" => &["chai"], + "茞" => &["chen"], + "茟" => &["yu"], + "茠" => &["xiu"], + "茡" => &["zi"], + "茢" => &["lie"], + "茣" => &["wu"], + "茤" => &["duo"], + "茥" => &["kui"], + "茦" => &["ce"], + "茧" => &["jian"], + "茨" => &["ci"], + "茩" => &["gou"], + "茪" => &["guang"], + "茫" => &["mang"], + "茬" => &["cha","zha"], + "茭" => &["jiao"], + "茮" => &["jiao"], + "茯" => &["fu"], + "茰" => &["yu"], + "茱" => &["zhu"], + "茲" => &["zi"], + "茳" => &["jiang"], + "茴" => &["hui"], + "茵" => &["yin"], + "茶" => &["cha"], + "茷" => &["fa"], + "茸" => &["rong"], + "茹" => &["ru"], + "茺" => &["chong"], + "茻" => &["mang"], + "茼" => &["tong"], + "茽" => &["zhong"], + "茿" => &["zhu"], + "荀" => &["xun"], + "荁" => &["huan"], + "荂" => &["kua"], + "荃" => &["quan"], + "荄" => &["gai"], + "荅" => &["da"], + "荆" => &["jing"], + "荇" => &["xing"], + "荈" => &["chuan"], + "草" => &["cao"], + "荊" => &["jing"], + "荋" => &["er"], + "荌" => &["an"], + "荍" => &["shou"], + "荎" => &["chi"], + "荏" => &["ren"], + "荐" => &["jian"], + "荑" => &["ti","yi"], + "荒" => &["huang"], + "荓" => &["ping"], + "荔" => &["li"], + "荕" => &["jin"], + "荖" => &["lao","pei"], + "荗" => &["rong"], + "荘" => &["zhuang"], + "荙" => &["da"], + "荚" => &["jia"], + "荛" => &["rao"], + "荜" => &["bi"], + "荝" => &["ce"], + "荞" => &["qiao"], + "荟" => &["hui"], + "荠" => &["ji","qi"], + "荡" => &["dang"], + "荣" => &["rong"], + "荤" => &["hun","xun"], + "荥" => &["ying","xing"], + "荦" => &["luo"], + "荧" => &["ying"], + "荨" => &["qian","xun"], + "荩" => &["jin"], + "荪" => &["sun"], + "荫" => &["yin"], + "荬" => &["mai"], + "荭" => &["hong"], + "荮" => &["zhou"], + "药" => &["yao"], + "荰" => &["du"], + "荱" => &["wei"], + "荲" => &["chu"], + "荳" => &["dou"], + "荴" => &["fu"], + "荵" => &["ren"], + "荶" => &["yin"], + "荷" => &["he"], + "荸" => &["bi"], + "荹" => &["bu"], + "荺" => &["yun"], + "荻" => &["di"], + "荼" => &["tu"], + "荽" => &["sui"], + "荾" => &["sui"], + "荿" => &["cheng"], + "莀" => &["chen"], + "莁" => &["wu"], + "莂" => &["bie"], + "莃" => &["xi"], + "莄" => &["geng"], + "莅" => &["li"], + "莆" => &["pu"], + "莇" => &["zhu"], + "莈" => &["mo"], + "莉" => &["li"], + "莊" => &["zhuang"], + "莋" => &["ji"], + "莌" => &["duo"], + "莍" => &["qiu"], + "莎" => &["sha","suo"], + "莏" => &["suo"], + "莐" => &["chen"], + "莑" => &["feng"], + "莒" => &["ju"], + "莓" => &["mei"], + "莔" => &["meng"], + "莕" => &["xing"], + "莖" => &["jing"], + "莗" => &["che"], + "莘" => &["xin","shen"], + "莙" => &["jun"], + "莚" => &["yan"], + "莛" => &["ting"], + "莜" => &["you"], + "莝" => &["cuo"], + "莞" => &["guan","wan"], + "莟" => &["han"], + "莠" => &["you"], + "莡" => &["cuo"], + "莢" => &["jia"], + "莣" => &["wang"], + "莤" => &["you"], + "莥" => &["niu","chou"], + "莦" => &["shao"], + "莧" => &["xian"], + "莨" => &["lang","liang"], + "莩" => &["fu","piao"], + "莪" => &["e"], + "莫" => &["mo"], + "莬" => &["wen"], + "莭" => &["jie"], + "莮" => &["nan"], + "莯" => &["mu"], + "莰" => &["kan"], + "莱" => &["lai"], + "莲" => &["lian"], + "莳" => &["shi"], + "莴" => &["wo","zhua"], + "莵" => &["tu"], + "莶" => &["xian"], + "获" => &["huo"], + "莸" => &["you"], + "莹" => &["ying"], + "莺" => &["ying"], + "莼" => &["chun"], + "莽" => &["mang"], + "莾" => &["mang"], + "莿" => &["ci"], + "菀" => &["yu","wan"], + "菁" => &["jing"], + "菂" => &["di"], + "菃" => &["qu"], + "菄" => &["dong"], + "菅" => &["jian"], + "菆" => &["zou"], + "菇" => &["gu"], + "菈" => &["la"], + "菉" => &["lu","lu:"], + "菊" => &["ju"], + "菋" => &["wei"], + "菌" => &["jun"], + "菍" => &["nie"], + "菎" => &["kun"], + "菏" => &["he"], + "菐" => &["pu"], + "菑" => &["zai"], + "菒" => &["gao"], + "菓" => &["guo"], + "菔" => &["fu"], + "菕" => &["lun"], + "菖" => &["chang"], + "菗" => &["chou"], + "菘" => &["song"], + "菙" => &["chui"], + "菚" => &["zhan"], + "菛" => &["men"], + "菜" => &["cai"], + "菝" => &["ba"], + "菞" => &["li"], + "菟" => &["tu"], + "菠" => &["bo"], + "菡" => &["han"], + "菢" => &["bao"], + "菣" => &["qin"], + "菤" => &["juan"], + "菥" => &["xi"], + "菦" => &["qin"], + "菧" => &["di"], + "菨" => &["jie"], + "菩" => &["pu"], + "菪" => &["dang"], + "菫" => &["jin"], + "菬" => &["zhao"], + "菭" => &["tai"], + "菮" => &["geng"], + "華" => &["hua"], + "菰" => &["gu"], + "菱" => &["ling"], + "菲" => &["fei"], + "菳" => &["jin"], + "菴" => &["an"], + "菵" => &["wang"], + "菶" => &["beng"], + "菷" => &["zhou"], + "菸" => &["yan"], + "菹" => &["zu"], + "菺" => &["jian"], + "菻" => &["lin"], + "菼" => &["tan"], + "菽" => &["shu"], + "菾" => &["tian"], + "菿" => &["dao"], + "萀" => &["hu"], + "萁" => &["ji","qi"], + "萂" => &["he"], + "萃" => &["cui"], + "萄" => &["tao"], + "萅" => &["chun"], + "萆" => &["bei","bi"], + "萇" => &["chang"], + "萈" => &["huan"], + "萉" => &["fei"], + "萊" => &["lai"], + "萋" => &["qi"], + "萌" => &["meng"], + "萍" => &["ping"], + "萎" => &["wei"], + "萏" => &["dan"], + "萐" => &["sha"], + "萑" => &["huan"], + "萒" => &["yan"], + "萓" => &["yi"], + "萔" => &["tiao"], + "萕" => &["qi","ji"], + "萖" => &["wan"], + "萗" => &["ce"], + "萘" => &["nai"], + "萚" => &["tuo"], + "萛" => &["jiu"], + "萜" => &["tie"], + "萝" => &["luo"], + "萠" => &["meng"], + "萣" => &["ding"], + "萤" => &["ying"], + "营" => &["ying"], + "萦" => &["ying"], + "萧" => &["xiao"], + "萨" => &["sa"], + "萩" => &["qiu"], + "萪" => &["ke"], + "萫" => &["xiang"], + "萬" => &["wan","mo"], + "萭" => &["yu"], + "萮" => &["yu"], + "萯" => &["fu"], + "萰" => &["lian"], + "萱" => &["xuan"], + "萲" => &["xuan"], + "萳" => &["nan"], + "萴" => &["ze"], + "萵" => &["wo"], + "萶" => &["chun"], + "萷" => &["xiao"], + "萸" => &["yu"], + "萹" => &["pian","bian"], + "萺" => &["mao"], + "萻" => &["an"], + "萼" => &["e"], + "落" => &["luo","la","lao"], + "萾" => &["ying"], + "萿" => &["huo"], + "葀" => &["gua"], + "葁" => &["jiang"], + "葂" => &["wan"], + "葃" => &["zuo"], + "葄" => &["zuo"], + "葅" => &["ju"], + "葆" => &["bao"], + "葇" => &["rou"], + "葈" => &["xi"], + "葉" => &["xie","ye","she"], + "葊" => &["an"], + "葋" => &["qu"], + "葌" => &["jian"], + "葍" => &["fu"], + "葎" => &["lu:"], + "葏" => &["lu:"], + "葐" => &["pen"], + "葑" => &["feng"], + "葒" => &["hong"], + "葓" => &["hong"], + "葔" => &["hou"], + "葕" => &["yan"], + "葖" => &["tu"], + "著" => &["zhu","zhe","zhao","zi","zhuo"], + "葘" => &["zi"], + "葙" => &["xiang"], + "葚" => &["shen","ren"], + "葛" => &["ge"], + "葜" => &["qia"], + "葝" => &["jing"], + "葞" => &["mi"], + "葟" => &["huang"], + "葠" => &["shen"], + "葡" => &["pu"], + "葢" => &["ge"], + "董" => &["dong"], + "葤" => &["zhou"], + "葥" => &["qian"], + "葦" => &["wei"], + "葧" => &["bo"], + "葨" => &["wei"], + "葩" => &["pa"], + "葪" => &["ji"], + "葫" => &["hu"], + "葬" => &["zang"], + "葭" => &["jia"], + "葮" => &["duan"], + "葯" => &["yao"], + "葰" => &["jun"], + "葱" => &["cong"], + "葲" => &["quan"], + "葳" => &["wei"], + "葴" => &["xian"], + "葵" => &["kui"], + "葶" => &["ting"], + "葷" => &["hun","xun"], + "葸" => &["xi"], + "葹" => &["shi"], + "葺" => &["qi"], + "葻" => &["lan"], + "葼" => &["zong"], + "葽" => &["yao"], + "葾" => &["yuan"], + "葿" => &["mei"], + "蒀" => &["yun"], + "蒁" => &["shu"], + "蒂" => &["di"], + "蒃" => &["zhuan"], + "蒄" => &["guan"], + "蒆" => &["qiong"], + "蒇" => &["chan"], + "蒈" => &["kai"], + "蒉" => &["kui"], + "蒋" => &["jiang"], + "蒌" => &["lou"], + "蒍" => &["wei"], + "蒎" => &["pai"], + "蒐" => &["sou"], + "蒑" => &["yin"], + "蒒" => &["shi"], + "蒓" => &["chun"], + "蒔" => &["shi"], + "蒕" => &["yun"], + "蒖" => &["zhen"], + "蒗" => &["lang"], + "蒘" => &["nu"], + "蒙" => &["meng"], + "蒚" => &["he"], + "蒛" => &["que"], + "蒜" => &["suan"], + "蒝" => &["yuan"], + "蒞" => &["li"], + "蒟" => &["ju"], + "蒠" => &["xi"], + "蒡" => &["bang","pang"], + "蒢" => &["chu"], + "蒣" => &["xu"], + "蒤" => &["tu"], + "蒥" => &["liu"], + "蒦" => &["huo"], + "蒧" => &["zhen"], + "蒨" => &["qian"], + "蒩" => &["zu"], + "蒪" => &["po"], + "蒫" => &["cuo"], + "蒬" => &["yuan"], + "蒭" => &["chu"], + "蒮" => &["yu"], + "蒯" => &["kuai"], + "蒰" => &["pan"], + "蒱" => &["pu"], + "蒲" => &["pu"], + "蒳" => &["na"], + "蒴" => &["shuo"], + "蒵" => &["xi"], + "蒶" => &["fen"], + "蒷" => &["yun"], + "蒸" => &["zheng"], + "蒹" => &["jian"], + "蒺" => &["ji"], + "蒻" => &["ruo"], + "蒼" => &["cang"], + "蒽" => &["en"], + "蒾" => &["mi"], + "蒿" => &["hao"], + "蓀" => &["sun"], + "蓁" => &["zhen"], + "蓂" => &["ming"], + "蓄" => &["xu"], + "蓅" => &["liu"], + "蓆" => &["xi"], + "蓇" => &["gu"], + "蓈" => &["lang"], + "蓉" => &["rong"], + "蓊" => &["weng"], + "蓋" => &["gai","ge","he"], + "蓌" => &["cuo"], + "蓍" => &["shi"], + "蓎" => &["tang"], + "蓏" => &["luo"], + "蓐" => &["ru"], + "蓑" => &["suo"], + "蓒" => &["xian"], + "蓓" => &["bei"], + "蓔" => &["yao"], + "蓕" => &["gui"], + "蓖" => &["bi"], + "蓗" => &["zong"], + "蓘" => &["gun"], + "蓚" => &["xiu"], + "蓛" => &["ce"], + "蓝" => &["lan","la"], + "蓟" => &["ji"], + "蓠" => &["li"], + "蓡" => &["can"], + "蓢" => &["lang"], + "蓣" => &["yu"], + "蓥" => &["ying"], + "蓦" => &["mo"], + "蓧" => &["diao"], + "蓨" => &["xiu"], + "蓩" => &["wu"], + "蓪" => &["tong"], + "蓫" => &["zhu"], + "蓬" => &["peng"], + "蓭" => &["an"], + "蓮" => &["lian"], + "蓯" => &["cong"], + "蓰" => &["xi"], + "蓱" => &["ping"], + "蓲" => &["qiu","ou"], + "蓳" => &["jin"], + "蓴" => &["chun"], + "蓵" => &["jie"], + "蓶" => &["wei"], + "蓷" => &["tui"], + "蓸" => &["cao"], + "蓹" => &["yu"], + "蓺" => &["yi"], + "蓻" => &["ji"], + "蓼" => &["liao","lu"], + "蓽" => &["bi"], + "蓾" => &["lu"], + "蓿" => &["xu","su"], + "蔀" => &["bu"], + "蔁" => &["zhang"], + "蔂" => &["luo"], + "蔃" => &["qiang"], + "蔄" => &["man"], + "蔅" => &["yan"], + "蔆" => &["leng"], + "蔇" => &["ji"], + "蔈" => &["biao","piao"], + "蔉" => &["gun"], + "蔊" => &["han"], + "蔋" => &["di"], + "蔌" => &["su"], + "蔍" => &["lu"], + "蔎" => &["she"], + "蔏" => &["shang"], + "蔐" => &["di"], + "蔑" => &["mie"], + "蔒" => &["xun"], + "蔓" => &["man","wan"], + "蔔" => &["bo","bu"], + "蔕" => &["di"], + "蔖" => &["cuo"], + "蔗" => &["zhe"], + "蔘" => &["sen"], + "蔙" => &["xuan"], + "蔚" => &["yu","wei"], + "蔛" => &["hu"], + "蔜" => &["ao"], + "蔝" => &["mi"], + "蔞" => &["lou"], + "蔟" => &["cu"], + "蔠" => &["zhong"], + "蔡" => &["cai"], + "蔢" => &["po"], + "蔣" => &["jiang"], + "蔤" => &["mi"], + "蔥" => &["cong"], + "蔦" => &["niao"], + "蔧" => &["hui"], + "蔨" => &["jun"], + "蔩" => &["yin"], + "蔪" => &["jian"], + "蔫" => &["nian"], + "蔬" => &["shu"], + "蔭" => &["yin"], + "蔮" => &["kui"], + "蔯" => &["chen"], + "蔰" => &["hu"], + "蔱" => &["sha"], + "蔲" => &["kou"], + "蔳" => &["qian"], + "蔴" => &["ma"], + "蔵" => &["cang","zang"], + "蔶" => &["ze"], + "蔷" => &["qiang"], + "蔸" => &["dou"], + "蔹" => &["lian"], + "蔺" => &["lin"], + "蔻" => &["kou"], + "蔼" => &["ai"], + "蔽" => &["bi"], + "蔾" => &["li"], + "蔿" => &["wei"], + "蕀" => &["ji"], + "蕁" => &["qian","xun"], + "蕂" => &["sheng"], + "蕃" => &["fan","bo"], + "蕄" => &["meng"], + "蕅" => &["ou"], + "蕆" => &["chan"], + "蕇" => &["dian"], + "蕈" => &["xun","jun"], + "蕉" => &["jiao","qiao"], + "蕊" => &["rui"], + "蕋" => &["rui"], + "蕌" => &["lei"], + "蕍" => &["yu"], + "蕎" => &["qiao"], + "蕏" => &["chu"], + "蕐" => &["hua"], + "蕑" => &["jian"], + "蕒" => &["mai"], + "蕓" => &["yun"], + "蕔" => &["bao"], + "蕕" => &["you"], + "蕖" => &["qu"], + "蕗" => &["lu"], + "蕘" => &["rao"], + "蕙" => &["hui"], + "蕚" => &["e"], + "蕛" => &["ti"], + "蕜" => &["fei"], + "蕝" => &["jue"], + "蕞" => &["zui"], + "蕟" => &["fa"], + "蕠" => &["ru"], + "蕡" => &["fen"], + "蕢" => &["kui"], + "蕣" => &["shun"], + "蕤" => &["rui"], + "蕥" => &["ya"], + "蕦" => &["xu"], + "蕧" => &["fu"], + "蕨" => &["jue"], + "蕩" => &["dang"], + "蕪" => &["wu"], + "蕫" => &["tong"], + "蕬" => &["si"], + "蕭" => &["xiao"], + "蕮" => &["xi"], + "蕯" => &["yong"], + "蕰" => &["wen"], + "蕱" => &["shao"], + "蕲" => &["qi"], + "蕳" => &["jian"], + "蕴" => &["yun"], + "蕵" => &["sun"], + "蕶" => &["ling"], + "蕷" => &["yu"], + "蕸" => &["xia"], + "蕹" => &["weng"], + "蕺" => &["ji"], + "蕻" => &["hong"], + "蕼" => &["si"], + "蕽" => &["deng"], + "蕾" => &["lei"], + "蕿" => &["xuan"], + "薀" => &["yun"], + "薁" => &["yu"], + "薂" => &["xi"], + "薃" => &["hao"], + "薄" => &["bo","bao"], + "薅" => &["hao"], + "薆" => &["ai"], + "薇" => &["wei"], + "薈" => &["hui"], + "薉" => &["wei"], + "薊" => &["ji"], + "薋" => &["ci"], + "薌" => &["xiang"], + "薍" => &["luan"], + "薎" => &["mie"], + "薏" => &["yi"], + "薐" => &["leng"], + "薑" => &["jiang"], + "薒" => &["can"], + "薓" => &["shen","can","cen"], + "薔" => &["qiang"], + "薕" => &["lian"], + "薖" => &["ke"], + "薗" => &["yuan"], + "薘" => &["da"], + "薙" => &["ti"], + "薚" => &["tang"], + "薛" => &["xue"], + "薜" => &["bi","bo"], + "薝" => &["zhan"], + "薞" => &["sun"], + "薟" => &["lian","xian"], + "薠" => &["fan"], + "薡" => &["ding"], + "薢" => &["xiao"], + "薣" => &["gu"], + "薤" => &["xie"], + "薥" => &["shu"], + "薦" => &["jian"], + "薧" => &["kao"], + "薨" => &["hong"], + "薩" => &["sa"], + "薪" => &["xin"], + "薫" => &["xun"], + "薬" => &["yao"], + "薭" => &["bai"], + "薮" => &["sou"], + "薯" => &["shu"], + "薰" => &["xun"], + "薱" => &["dui"], + "薲" => &["pin"], + "薳" => &["wei"], + "薴" => &["neng"], + "薵" => &["chou"], + "薶" => &["mai"], + "薷" => &["ru"], + "薸" => &["piao"], + "薹" => &["tai"], + "薺" => &["qi","ji"], + "薻" => &["zao"], + "薼" => &["chen"], + "薽" => &["zhen"], + "薾" => &["er"], + "薿" => &["ni"], + "藀" => &["ying"], + "藁" => &["gao"], + "藂" => &["cong"], + "藃" => &["xiao"], + "藄" => &["qi"], + "藅" => &["fa"], + "藆" => &["jian"], + "藇" => &["xu"], + "藈" => &["kui"], + "藉" => &["jie","ji"], + "藊" => &["bian"], + "藋" => &["di"], + "藌" => &["mi"], + "藍" => &["lan","la"], + "藎" => &["jin"], + "藏" => &["cang","zang"], + "藐" => &["miao"], + "藑" => &["qiong"], + "藒" => &["qie"], + "藓" => &["xian","li"], + "藕" => &["ou"], + "藖" => &["xian"], + "藗" => &["su"], + "藘" => &["lu:"], + "藙" => &["yi"], + "藚" => &["xu"], + "藛" => &["xie"], + "藜" => &["li"], + "藝" => &["yi"], + "藞" => &["la"], + "藟" => &["lei"], + "藠" => &["jiao"], + "藡" => &["di"], + "藢" => &["zhi"], + "藣" => &["pi"], + "藤" => &["teng"], + "藥" => &["yao","yue"], + "藦" => &["mo"], + "藧" => &["huan"], + "藨" => &["biao"], + "藩" => &["fan"], + "藪" => &["sou"], + "藫" => &["tan"], + "藬" => &["tui"], + "藭" => &["qiong"], + "藮" => &["qiao"], + "藯" => &["wei"], + "藰" => &["liu"], + "藱" => &["hui"], + "藲" => &["shu"], + "藳" => &["gao"], + "藴" => &["yun"], + "藶" => &["li"], + "藷" => &["zhu","shu"], + "藸" => &["zhu"], + "藹" => &["ai"], + "藺" => &["lin"], + "藻" => &["zao"], + "藼" => &["xuan"], + "藽" => &["chen"], + "藾" => &["lai"], + "藿" => &["huo"], + "蘀" => &["tuo"], + "蘁" => &["wu","e"], + "蘂" => &["rui"], + "蘃" => &["rui"], + "蘄" => &["qi"], + "蘅" => &["heng"], + "蘆" => &["lu"], + "蘇" => &["su"], + "蘈" => &["tui"], + "蘉" => &["mang"], + "蘊" => &["yun"], + "蘋" => &["pin","ping"], + "蘌" => &["yu"], + "蘍" => &["xun"], + "蘎" => &["ji"], + "蘏" => &["jiong"], + "蘐" => &["xuan"], + "蘑" => &["mo"], + "蘓" => &["su"], + "蘔" => &["jiong"], + "蘖" => &["nie"], + "蘗" => &["bo","nie"], + "蘘" => &["rang"], + "蘙" => &["yi"], + "蘚" => &["xian","li"], + "蘛" => &["yu"], + "蘜" => &["ju"], + "蘝" => &["lian"], + "蘞" => &["lian"], + "蘟" => &["yin"], + "蘠" => &["qiang"], + "蘡" => &["ying"], + "蘢" => &["long"], + "蘣" => &["tou"], + "蘤" => &["wei"], + "蘥" => &["yue"], + "蘦" => &["ling"], + "蘧" => &["qu"], + "蘨" => &["yao"], + "蘩" => &["fan"], + "蘪" => &["mi"], + "蘫" => &["lan"], + "蘬" => &["kui"], + "蘭" => &["lan"], + "蘮" => &["ji"], + "蘯" => &["dang"], + "蘱" => &["lei"], + "蘲" => &["lei"], + "蘳" => &["tong"], + "蘴" => &["feng"], + "蘵" => &["zhi"], + "蘶" => &["wei"], + "蘷" => &["kui"], + "蘸" => &["zhan"], + "蘹" => &["huai"], + "蘺" => &["li"], + "蘻" => &["ji"], + "蘼" => &["mi"], + "蘽" => &["lei"], + "蘾" => &["huai"], + "蘿" => &["luo"], + "虀" => &["ji"], + "虁" => &["nao"], + "虂" => &["lu"], + "虃" => &["jian"], + "虆" => &["lei"], + "虇" => &["quan"], + "虈" => &["xiao"], + "虉" => &["yi"], + "虊" => &["luan"], + "虋" => &["men"], + "虌" => &["bie"], + "虍" => &["hu"], + "虎" => &["hu"], + "虏" => &["lu"], + "虐" => &["nu:e"], + "虑" => &["lu:"], + "虒" => &["zhi"], + "虓" => &["xiao"], + "虔" => &["qian"], + "處" => &["chu"], + "虖" => &["hu"], + "虗" => &["xu"], + "虘" => &["cuo"], + "虙" => &["fu"], + "虚" => &["xu"], + "虛" => &["xu"], + "虜" => &["lu"], + "虝" => &["hu"], + "虞" => &["yu"], + "號" => &["hao"], + "虠" => &["jiao"], + "虡" => &["ju"], + "虢" => &["guo"], + "虣" => &["bao"], + "虤" => &["yan"], + "虥" => &["zhan"], + "虦" => &["zhan"], + "虧" => &["kui"], + "虨" => &["ban"], + "虩" => &["xi"], + "虪" => &["shu"], + "虫" => &["chong","hui"], + "虬" => &["qiu"], + "虭" => &["diao"], + "虮" => &["ji"], + "虯" => &["qiu"], + "虰" => &["ding"], + "虱" => &["shi"], + "虳" => &["di"], + "虴" => &["zhe"], + "虵" => &["she","yi"], + "虶" => &["yu"], + "虷" => &["gan"], + "虸" => &["zi"], + "虹" => &["hong","jiang"], + "虺" => &["hui"], + "虻" => &["meng"], + "虼" => &["ge"], + "虽" => &["sui"], + "虾" => &["xia","ha"], + "虿" => &["chai"], + "蚀" => &["shi"], + "蚁" => &["yi"], + "蚂" => &["ma"], + "蚃" => &["xiang"], + "蚄" => &["fang"], + "蚅" => &["e"], + "蚆" => &["pa"], + "蚇" => &["chi"], + "蚈" => &["qian"], + "蚉" => &["wen"], + "蚊" => &["wen"], + "蚋" => &["rui"], + "蚌" => &["bang","beng"], + "蚍" => &["pi"], + "蚎" => &["yue"], + "蚏" => &["yue"], + "蚐" => &["jun"], + "蚑" => &["qi"], + "蚒" => &["ran"], + "蚓" => &["yin"], + "蚔" => &["qi","chi"], + "蚕" => &["can","tian"], + "蚖" => &["yuan"], + "蚗" => &["jue"], + "蚘" => &["hui"], + "蚙" => &["qian"], + "蚚" => &["qi"], + "蚛" => &["zhong"], + "蚜" => &["ya"], + "蚝" => &["hao"], + "蚞" => &["mu"], + "蚟" => &["wang"], + "蚠" => &["fen"], + "蚡" => &["fen"], + "蚢" => &["hang"], + "蚣" => &["gong"], + "蚤" => &["zao"], + "蚥" => &["fu"], + "蚦" => &["ran"], + "蚧" => &["jie"], + "蚨" => &["fu"], + "蚩" => &["chi"], + "蚪" => &["dou"], + "蚫" => &["bao"], + "蚬" => &["xian"], + "蚭" => &["ni"], + "蚮" => &["te"], + "蚯" => &["qiu"], + "蚰" => &["you"], + "蚱" => &["zha"], + "蚲" => &["ping"], + "蚳" => &["chi"], + "蚴" => &["you"], + "蚵" => &["he","ke"], + "蚶" => &["han"], + "蚷" => &["ju"], + "蚸" => &["li"], + "蚹" => &["fu"], + "蚺" => &["ran"], + "蚻" => &["zha"], + "蚼" => &["gou"], + "蚽" => &["pi"], + "蚾" => &["bo"], + "蚿" => &["xian"], + "蛀" => &["zhu"], + "蛁" => &["diao"], + "蛂" => &["bie"], + "蛃" => &["bing"], + "蛄" => &["gu"], + "蛅" => &["ran"], + "蛆" => &["qu","ju"], + "蛇" => &["she","yi"], + "蛈" => &["tie"], + "蛉" => &["ling"], + "蛊" => &["gu"], + "蛋" => &["dan"], + "蛌" => &["gu"], + "蛍" => &["ying"], + "蛎" => &["li"], + "蛏" => &["cheng"], + "蛐" => &["qu"], + "蛑" => &["mou"], + "蛒" => &["ge"], + "蛓" => &["ci"], + "蛔" => &["hui"], + "蛕" => &["hui"], + "蛖" => &["mang"], + "蛗" => &["fu"], + "蛘" => &["yang"], + "蛙" => &["wa"], + "蛚" => &["lie"], + "蛛" => &["zhu"], + "蛜" => &["yi"], + "蛝" => &["xian"], + "蛞" => &["kuo"], + "蛟" => &["jiao"], + "蛠" => &["li"], + "蛡" => &["yi"], + "蛢" => &["ping"], + "蛣" => &["ji"], + "蛤" => &["ha","ge"], + "蛥" => &["she"], + "蛦" => &["yi"], + "蛧" => &["wang"], + "蛨" => &["mo"], + "蛩" => &["qiong"], + "蛪" => &["qie"], + "蛫" => &["gui"], + "蛬" => &["gong"], + "蛭" => &["zhi"], + "蛮" => &["man"], + "蛰" => &["zhe"], + "蛱" => &["jia"], + "蛲" => &["nao"], + "蛳" => &["si"], + "蛴" => &["qi"], + "蛵" => &["xing"], + "蛶" => &["lie"], + "蛷" => &["qiu"], + "蛸" => &["shao","xiao"], + "蛹" => &["yong"], + "蛺" => &["jia"], + "蛻" => &["tui","shui"], + "蛼" => &["che"], + "蛽" => &["bai"], + "蛾" => &["e","yi"], + "蛿" => &["han"], + "蜀" => &["shu"], + "蜁" => &["xuan"], + "蜂" => &["feng"], + "蜃" => &["shen"], + "蜄" => &["zhen"], + "蜅" => &["fu"], + "蜆" => &["xian"], + "蜇" => &["zhe"], + "蜈" => &["wu"], + "蜉" => &["fu"], + "蜊" => &["li"], + "蜋" => &["lang"], + "蜌" => &["bi"], + "蜍" => &["chu"], + "蜎" => &["yuan"], + "蜏" => &["you"], + "蜐" => &["jie"], + "蜑" => &["dan"], + "蜒" => &["yan"], + "蜓" => &["ting"], + "蜔" => &["dian"], + "蜕" => &["tui"], + "蜖" => &["hui"], + "蜗" => &["wo"], + "蜘" => &["zhi"], + "蜙" => &["song"], + "蜚" => &["fei"], + "蜛" => &["ju"], + "蜜" => &["mi"], + "蜝" => &["qi"], + "蜞" => &["qi"], + "蜟" => &["yu"], + "蜠" => &["jun"], + "蜡" => &["la","zha"], + "蜢" => &["meng"], + "蜣" => &["qiang"], + "蜤" => &["si"], + "蜥" => &["xi"], + "蜦" => &["lun"], + "蜧" => &["li"], + "蜨" => &["die"], + "蜩" => &["tiao"], + "蜪" => &["tao"], + "蜫" => &["kun"], + "蜬" => &["gan"], + "蜭" => &["han"], + "蜮" => &["yu"], + "蜯" => &["bang","beng"], + "蜰" => &["fei"], + "蜱" => &["pi"], + "蜲" => &["wei"], + "蜳" => &["dun"], + "蜴" => &["yi"], + "蜵" => &["yuan"], + "蜶" => &["su"], + "蜷" => &["quan"], + "蜸" => &["qian"], + "蜹" => &["rui"], + "蜺" => &["ni"], + "蜻" => &["qing"], + "蜼" => &["wei"], + "蜽" => &["liang"], + "蜾" => &["guo"], + "蜿" => &["wan"], + "蝀" => &["dong"], + "蝁" => &["e"], + "蝂" => &["ban"], + "蝃" => &["zhuo"], + "蝄" => &["wang"], + "蝅" => &["can"], + "蝆" => &["yang"], + "蝇" => &["ying"], + "蝈" => &["guo"], + "蝉" => &["chan"], + "蝋" => &["la","zha"], + "蝌" => &["ke"], + "蝍" => &["ji"], + "蝎" => &["xie","he"], + "蝏" => &["ting"], + "蝐" => &["mai"], + "蝑" => &["xu"], + "蝒" => &["mian"], + "蝓" => &["yu"], + "蝔" => &["jie"], + "蝕" => &["shi"], + "蝖" => &["xuan"], + "蝗" => &["huang"], + "蝘" => &["yan"], + "蝙" => &["bian"], + "蝚" => &["rou"], + "蝛" => &["wei"], + "蝜" => &["fu"], + "蝝" => &["yuan"], + "蝞" => &["mei"], + "蝟" => &["wei"], + "蝠" => &["fu"], + "蝡" => &["ruan"], + "蝢" => &["xie"], + "蝣" => &["you"], + "蝤" => &["you","qiu"], + "蝥" => &["mao"], + "蝦" => &["xia","ha"], + "蝧" => &["ying"], + "蝨" => &["shi"], + "蝩" => &["chong"], + "蝪" => &["tang"], + "蝫" => &["zhu"], + "蝬" => &["zong"], + "蝭" => &["ti"], + "蝮" => &["fu"], + "蝯" => &["yuan"], + "蝰" => &["kui"], + "蝱" => &["meng"], + "蝲" => &["la"], + "蝳" => &["du"], + "蝴" => &["hu"], + "蝵" => &["qiu"], + "蝶" => &["die"], + "蝷" => &["li","xi"], + "蝸" => &["gua","wo"], + "蝹" => &["yun"], + "蝺" => &["ju"], + "蝻" => &["nan"], + "蝼" => &["lou"], + "蝽" => &["chun"], + "蝾" => &["rong"], + "蝿" => &["ying"], + "螀" => &["jiang"], + "螁" => &["tun"], + "螂" => &["lang"], + "螃" => &["pang"], + "螄" => &["si","shi"], + "螅" => &["xi"], + "螆" => &["xi"], + "螇" => &["xi"], + "螈" => &["yuan"], + "螉" => &["weng"], + "螊" => &["lian"], + "螋" => &["sou"], + "螌" => &["ban"], + "融" => &["rong"], + "螎" => &["rong"], + "螏" => &["ji"], + "螐" => &["wu"], + "螑" => &["xiu"], + "螒" => &["han"], + "螓" => &["qin"], + "螔" => &["yi"], + "螕" => &["bi"], + "螖" => &["hua"], + "螗" => &["tang"], + "螘" => &["yi"], + "螙" => &["du"], + "螚" => &["nai"], + "螛" => &["he"], + "螜" => &["hu"], + "螝" => &["xi"], + "螞" => &["ma"], + "螟" => &["ming"], + "螠" => &["yi"], + "螡" => &["wen"], + "螢" => &["ying"], + "螣" => &["teng","te"], + "螤" => &["yu"], + "螥" => &["cang"], + "螨" => &["man"], + "螪" => &["shang"], + "螫" => &["shi","zhe"], + "螬" => &["cao"], + "螭" => &["chi"], + "螮" => &["di"], + "螯" => &["ao"], + "螰" => &["lu"], + "螱" => &["wei"], + "螲" => &["zhi"], + "螳" => &["tang"], + "螴" => &["chen"], + "螵" => &["piao"], + "螶" => &["qu"], + "螷" => &["pi"], + "螸" => &["yu"], + "螹" => &["jian"], + "螺" => &["luo"], + "螻" => &["lou"], + "螼" => &["qin"], + "螽" => &["zhong"], + "螾" => &["yin"], + "螿" => &["jiang"], + "蟀" => &["shuai","shuo"], + "蟁" => &["wen"], + "蟂" => &["jiao"], + "蟃" => &["wan"], + "蟄" => &["zhe","zhi"], + "蟅" => &["zhe"], + "蟆" => &["ma"], + "蟇" => &["ma"], + "蟈" => &["guo"], + "蟉" => &["liao"], + "蟊" => &["mao"], + "蟋" => &["xi"], + "蟌" => &["cong"], + "蟍" => &["li"], + "蟎" => &["man"], + "蟏" => &["xiao"], + "蟑" => &["zhang"], + "蟒" => &["mang"], + "蟓" => &["xiang"], + "蟔" => &["mo"], + "蟕" => &["zi"], + "蟖" => &["si"], + "蟗" => &["qiu"], + "蟘" => &["te"], + "蟙" => &["zhi"], + "蟚" => &["peng"], + "蟛" => &["peng"], + "蟜" => &["jiao"], + "蟝" => &["qu"], + "蟞" => &["bie"], + "蟟" => &["liao"], + "蟠" => &["pan"], + "蟡" => &["gui"], + "蟢" => &["xi"], + "蟣" => &["ji"], + "蟤" => &["zhuan"], + "蟥" => &["huang"], + "蟦" => &["fei"], + "蟧" => &["lao"], + "蟨" => &["jue"], + "蟩" => &["jue"], + "蟪" => &["hui"], + "蟫" => &["yin"], + "蟬" => &["chan"], + "蟭" => &["jiao"], + "蟮" => &["shan"], + "蟯" => &["rao","nao"], + "蟰" => &["xiao"], + "蟱" => &["wu"], + "蟲" => &["chong"], + "蟳" => &["xun"], + "蟴" => &["si"], + "蟶" => &["cheng"], + "蟷" => &["dang"], + "蟸" => &["li"], + "蟹" => &["xie"], + "蟺" => &["shan"], + "蟻" => &["yi"], + "蟼" => &["jing"], + "蟽" => &["da"], + "蟾" => &["chan"], + "蟿" => &["qi"], + "蠀" => &["zi"], + "蠁" => &["xiang"], + "蠂" => &["she"], + "蠃" => &["luo"], + "蠄" => &["qin"], + "蠅" => &["ying"], + "蠆" => &["chai"], + "蠇" => &["li"], + "蠈" => &["ze"], + "蠉" => &["xuan"], + "蠊" => &["lian"], + "蠋" => &["zhu"], + "蠌" => &["ze"], + "蠍" => &["xie"], + "蠎" => &["mang"], + "蠏" => &["xie"], + "蠐" => &["qi"], + "蠑" => &["rong"], + "蠒" => &["jian"], + "蠓" => &["meng"], + "蠔" => &["hao"], + "蠕" => &["ru","ruan"], + "蠖" => &["huo"], + "蠗" => &["zhuo"], + "蠘" => &["jie"], + "蠙" => &["bin"], + "蠚" => &["he"], + "蠛" => &["mie"], + "蠜" => &["fan"], + "蠝" => &["lei"], + "蠞" => &["jie"], + "蠟" => &["la","zha"], + "蠠" => &["mi"], + "蠡" => &["li"], + "蠢" => &["chun"], + "蠣" => &["li"], + "蠤" => &["qiu"], + "蠥" => &["nie"], + "蠦" => &["lu"], + "蠧" => &["du"], + "蠨" => &["xiao"], + "蠩" => &["zhu"], + "蠪" => &["long"], + "蠫" => &["li"], + "蠬" => &["long"], + "蠭" => &["feng"], + "蠮" => &["ye"], + "蠯" => &["pi"], + "蠰" => &["rang"], + "蠱" => &["gu"], + "蠲" => &["juan"], + "蠳" => &["ying"], + "蠵" => &["xi"], + "蠶" => &["can"], + "蠷" => &["qu"], + "蠸" => &["quan"], + "蠹" => &["du"], + "蠺" => &["can"], + "蠻" => &["man"], + "蠼" => &["qu"], + "蠽" => &["jie"], + "蠾" => &["zhu"], + "蠿" => &["zha"], + "血" => &["xue","xie"], + "衁" => &["huang"], + "衂" => &["nu:"], + "衃" => &["pei"], + "衄" => &["nu:"], + "衅" => &["xin"], + "衆" => &["zhong"], + "衇" => &["mo"], + "衈" => &["er"], + "衉" => &["mie"], + "衊" => &["mie"], + "衋" => &["shi"], + "行" => &["xing","hang","heng"], + "衍" => &["yan"], + "衎" => &["kan"], + "衏" => &["yuan"], + "衑" => &["ling"], + "衒" => &["xuan"], + "術" => &["shu","zhu"], + "衔" => &["xian"], + "衕" => &["tong"], + "衖" => &["long"], + "街" => &["jie"], + "衘" => &["xian"], + "衙" => &["ya"], + "衚" => &["hu"], + "衛" => &["wei"], + "衜" => &["dao"], + "衝" => &["chong"], + "衞" => &["wei"], + "衟" => &["dao"], + "衠" => &["zhun"], + "衡" => &["heng"], + "衢" => &["qu"], + "衣" => &["yi"], + "衤" => &["yi"], + "补" => &["bu"], + "衦" => &["gan"], + "衧" => &["yu"], + "表" => &["biao"], + "衩" => &["cha"], + "衪" => &["yi"], + "衫" => &["shan"], + "衬" => &["chen"], + "衭" => &["fu"], + "衮" => &["gun"], + "衯" => &["fen"], + "衰" => &["shuai","cui"], + "衱" => &["jie","ji"], + "衲" => &["na"], + "衳" => &["zhong"], + "衴" => &["dan"], + "衵" => &["ri"], + "衶" => &["zhong"], + "衷" => &["zhong"], + "衸" => &["xie"], + "衹" => &["qi","zhi"], + "衺" => &["xie"], + "衻" => &["ran"], + "衼" => &["zhi"], + "衽" => &["ren"], + "衾" => &["qin"], + "衿" => &["jin"], + "袀" => &["jun"], + "袁" => &["yuan"], + "袂" => &["mei"], + "袃" => &["chai"], + "袄" => &["ao"], + "袅" => &["niao"], + "袆" => &["hui"], + "袇" => &["ran"], + "袈" => &["jia"], + "袉" => &["tuo"], + "袊" => &["ling"], + "袋" => &["dai"], + "袌" => &["bao"], + "袍" => &["pao"], + "袎" => &["yao"], + "袏" => &["zuo"], + "袐" => &["bi"], + "袑" => &["shao"], + "袒" => &["tan"], + "袓" => &["ju"], + "袔" => &["he"], + "袕" => &["xue"], + "袖" => &["xiu"], + "袗" => &["zhen"], + "袘" => &["yi"], + "袙" => &["pa"], + "袚" => &["bo","fu"], + "袛" => &["di"], + "袜" => &["wa"], + "袝" => &["fu"], + "袞" => &["gun"], + "袟" => &["zhi"], + "袠" => &["zhi"], + "袡" => &["ran"], + "袢" => &["pan"], + "袣" => &["yi"], + "袤" => &["mao"], + "袦" => &["na"], + "袧" => &["kou"], + "袨" => &["xuan"], + "袩" => &["chan"], + "袪" => &["qu"], + "被" => &["bei","pi"], + "袬" => &["yu"], + "袭" => &["xi"], + "袯" => &["bo"], + "袱" => &["fu"], + "袲" => &["yi"], + "袳" => &["chi"], + "袴" => &["ku"], + "袵" => &["ren"], + "袶" => &["jiang"], + "袷" => &["jia","qia"], + "袸" => &["cun"], + "袹" => &["mo"], + "袺" => &["jie"], + "袻" => &["er"], + "袼" => &["ge"], + "袽" => &["ru"], + "袾" => &["zhu"], + "袿" => &["gui"], + "裀" => &["yin"], + "裁" => &["cai"], + "裂" => &["lie"], + "装" => &["zhuang"], + "裆" => &["dang"], + "裈" => &["kun"], + "裉" => &["ken"], + "裊" => &["niao"], + "裋" => &["shu"], + "裌" => &["jia"], + "裍" => &["kun"], + "裎" => &["cheng"], + "裏" => &["li"], + "裐" => &["juan"], + "裑" => &["shen"], + "裒" => &["pou"], + "裓" => &["ge"], + "裔" => &["yi"], + "裕" => &["yu"], + "裖" => &["chen"], + "裗" => &["liu"], + "裘" => &["qiu"], + "裙" => &["qun"], + "裚" => &["ji"], + "裛" => &["yi"], + "補" => &["bu"], + "裝" => &["zhuang"], + "裞" => &["shui"], + "裟" => &["sha"], + "裠" => &["qun"], + "裡" => &["li"], + "裢" => &["lian"], + "裣" => &["lian"], + "裤" => &["ku"], + "裥" => &["jian"], + "裦" => &["fou"], + "裧" => &["tan"], + "裨" => &["bi","pi","bei"], + "裩" => &["gun"], + "裪" => &["tao"], + "裫" => &["yuan"], + "裬" => &["ling"], + "裭" => &["chi"], + "裮" => &["chang"], + "裯" => &["chou"], + "裰" => &["duo"], + "裱" => &["biao"], + "裲" => &["liang"], + "裳" => &["shang","chang"], + "裴" => &["pei"], + "裵" => &["pei"], + "裶" => &["fei"], + "裷" => &["yuan"], + "裸" => &["luo"], + "裹" => &["guo"], + "裺" => &["yan"], + "裻" => &["du"], + "裼" => &["ti","xi"], + "製" => &["zhi"], + "裾" => &["ju"], + "裿" => &["qi"], + "褀" => &["ji"], + "褁" => &["zhi"], + "褂" => &["gua"], + "褃" => &["ken"], + "褅" => &["ti"], + "褆" => &["shi"], + "複" => &["fu"], + "褈" => &["chong"], + "褉" => &["xie"], + "褊" => &["bian"], + "褋" => &["die"], + "褌" => &["kun","hui"], + "褍" => &["duan"], + "褎" => &["xiu"], + "褏" => &["xiu"], + "褐" => &["he"], + "褑" => &["yuan"], + "褒" => &["bao"], + "褓" => &["bao"], + "褔" => &["fu"], + "褕" => &["yu"], + "褖" => &["tuan"], + "褗" => &["yan"], + "褘" => &["hui"], + "褙" => &["bei"], + "褚" => &["chu","zhu"], + "褛" => &["lu:"], + "褞" => &["yun"], + "褟" => &["ta"], + "褠" => &["gou"], + "褡" => &["da"], + "褢" => &["huai"], + "褣" => &["rong"], + "褤" => &["yuan"], + "褥" => &["ru"], + "褦" => &["nai"], + "褧" => &["jiong"], + "褨" => &["suo"], + "褩" => &["ban"], + "褪" => &["tun","tui"], + "褫" => &["chi"], + "褬" => &["sang"], + "褭" => &["niao"], + "褮" => &["ying"], + "褯" => &["jie"], + "褰" => &["qian"], + "褱" => &["huai"], + "褲" => &["ku"], + "褳" => &["lian"], + "褴" => &["lan"], + "褵" => &["li"], + "褶" => &["zhe","xi"], + "褷" => &["shi"], + "褸" => &["lu:"], + "褹" => &["yi"], + "褺" => &["die"], + "褻" => &["xie"], + "褼" => &["xian"], + "褽" => &["wei"], + "褾" => &["biao"], + "褿" => &["cao"], + "襀" => &["ji"], + "襁" => &["qiang"], + "襂" => &["sen"], + "襃" => &["bao"], + "襄" => &["xiang"], + "襆" => &["pu"], + "襇" => &["jian"], + "襈" => &["zhuan"], + "襉" => &["jian"], + "襊" => &["zui"], + "襋" => &["ji"], + "襌" => &["dan"], + "襍" => &["za"], + "襎" => &["fan"], + "襏" => &["bo"], + "襐" => &["xiang"], + "襑" => &["xin"], + "襒" => &["bie","bi"], + "襓" => &["rao"], + "襔" => &["man"], + "襕" => &["lan"], + "襖" => &["ao"], + "襗" => &["duo"], + "襘" => &["hui"], + "襙" => &["cao"], + "襚" => &["sui"], + "襛" => &["nong"], + "襜" => &["chan"], + "襝" => &["lian"], + "襞" => &["bi"], + "襟" => &["jin"], + "襠" => &["dang"], + "襡" => &["shu"], + "襢" => &["tan"], + "襣" => &["bi"], + "襤" => &["lan"], + "襥" => &["pu"], + "襦" => &["ru"], + "襧" => &["zhi"], + "襩" => &["shu"], + "襪" => &["wa"], + "襫" => &["shi"], + "襬" => &["bai"], + "襭" => &["xie"], + "襮" => &["bo"], + "襯" => &["chen"], + "襰" => &["lai"], + "襱" => &["long"], + "襲" => &["xi"], + "襳" => &["xian"], + "襴" => &["lan"], + "襵" => &["zhe"], + "襶" => &["dai"], + "襸" => &["zan"], + "襹" => &["shi"], + "襺" => &["jian"], + "襻" => &["pan"], + "襼" => &["yi"], + "襾" => &["ya"], + "西" => &["xi"], + "覀" => &["xi"], + "要" => &["yao"], + "覂" => &["feng"], + "覃" => &["tan","qin"], + "覆" => &["fu"], + "覇" => &["ba"], + "覈" => &["he"], + "覉" => &["ji"], + "覊" => &["ji"], + "見" => &["jian","xian"], + "覌" => &["guan"], + "覍" => &["bian"], + "覎" => &["yan"], + "規" => &["gui"], + "覐" => &["jue","jiao"], + "覑" => &["pian"], + "覒" => &["mao"], + "覓" => &["mi"], + "覔" => &["mi"], + "覕" => &["mie"], + "視" => &["shi"], + "覗" => &["si"], + "覘" => &["zhan","chan"], + "覙" => &["luo"], + "覚" => &["jue","jiao"], + "覛" => &["mo"], + "覜" => &["tiao"], + "覝" => &["lian"], + "覞" => &["yao"], + "覟" => &["zhi"], + "覠" => &["jun"], + "覡" => &["xi"], + "覢" => &["shan"], + "覣" => &["wei"], + "覤" => &["xi"], + "覥" => &["tian"], + "覦" => &["yu"], + "覧" => &["lan"], + "覨" => &["e"], + "覩" => &["du"], + "親" => &["qin","qing"], + "覫" => &["pang"], + "覬" => &["ji"], + "覭" => &["ming"], + "覮" => &["ping"], + "覯" => &["gou"], + "覰" => &["qu"], + "覱" => &["zhan"], + "覲" => &["jin"], + "観" => &["guan"], + "覴" => &["deng"], + "覵" => &["jian"], + "覶" => &["luo"], + "覷" => &["qu"], + "覸" => &["jian"], + "覹" => &["wei"], + "覺" => &["jue","jiao"], + "覻" => &["qu"], + "覼" => &["luo"], + "覽" => &["lan"], + "覾" => &["shen"], + "覿" => &["di"], + "觀" => &["guan"], + "见" => &["jian","xian"], + "观" => &["guan"], + "觃" => &["yan"], + "规" => &["gui"], + "觅" => &["mi"], + "视" => &["shi"], + "觇" => &["chan"], + "览" => &["lan"], + "觉" => &["jue","jiao"], + "觊" => &["ji"], + "觋" => &["xi"], + "觌" => &["di"], + "觍" => &["tian"], + "觎" => &["yu"], + "觏" => &["gou"], + "觐" => &["jin"], + "觑" => &["qu"], + "角" => &["jiao","jue","jia"], + "觓" => &["jiu"], + "觔" => &["jin"], + "觕" => &["cu"], + "觖" => &["jue"], + "觗" => &["zhi"], + "觘" => &["chao"], + "觙" => &["ji"], + "觚" => &["gu"], + "觛" => &["dan"], + "觜" => &["zui","zi"], + "觝" => &["di"], + "觞" => &["shang"], + "觟" => &["hua"], + "觠" => &["quan"], + "觡" => &["ge"], + "觢" => &["zhi"], + "解" => &["jie","xie"], + "觤" => &["gui"], + "觥" => &["gong"], + "触" => &["chu"], + "觧" => &["jie","xie"], + "觨" => &["huan"], + "觩" => &["qiu"], + "觪" => &["xing"], + "觫" => &["su"], + "觬" => &["ni"], + "觭" => &["ji"], + "觮" => &["lu"], + "觯" => &["zhi"], + "觰" => &["zhu"], + "觱" => &["bi"], + "觲" => &["xing"], + "觳" => &["hu"], + "觴" => &["shang"], + "觵" => &["gong"], + "觶" => &["zhi"], + "觷" => &["xue"], + "觸" => &["chu"], + "觹" => &["xi"], + "觺" => &["yi"], + "觻" => &["li"], + "觼" => &["jue"], + "觽" => &["xi"], + "觾" => &["yan"], + "觿" => &["xi"], + "言" => &["yan"], + "訁" => &["yan"], + "訂" => &["ding"], + "訃" => &["fu"], + "訄" => &["qiu"], + "訅" => &["qiu"], + "訆" => &["jiao"], + "訇" => &["hong"], + "計" => &["ji"], + "訉" => &["fan"], + "訊" => &["xun"], + "訋" => &["diao"], + "訌" => &["hong"], + "訍" => &["cha"], + "討" => &["tao"], + "訏" => &["xu"], + "訐" => &["jie"], + "訑" => &["yi"], + "訒" => &["ren"], + "訓" => &["xun"], + "訔" => &["yin"], + "訕" => &["shan"], + "訖" => &["qi"], + "託" => &["tuo"], + "記" => &["ji"], + "訙" => &["xun"], + "訚" => &["yin"], + "訛" => &["e"], + "訜" => &["fen"], + "訝" => &["ya"], + "訞" => &["yao"], + "訟" => &["song"], + "訠" => &["shen"], + "訡" => &["yin"], + "訢" => &["xin"], + "訣" => &["jue"], + "訤" => &["xiao"], + "訥" => &["ne","na"], + "訦" => &["chen"], + "訧" => &["you"], + "訨" => &["zhi"], + "訩" => &["xiong"], + "訪" => &["fang"], + "訫" => &["xin"], + "訬" => &["chao"], + "設" => &["she"], + "訮" => &["xian"], + "訯" => &["sa"], + "訰" => &["zhun"], + "許" => &["xu","hu"], + "訲" => &["yi"], + "訳" => &["yi"], + "訴" => &["su"], + "訵" => &["chi"], + "訶" => &["he"], + "訷" => &["shen"], + "訸" => &["he"], + "訹" => &["xu"], + "診" => &["zhen"], + "註" => &["zhu"], + "証" => &["zheng"], + "訽" => &["gou"], + "訾" => &["zi"], + "訿" => &["zi"], + "詀" => &["zhan"], + "詁" => &["gu"], + "詂" => &["fu"], + "詃" => &["jian"], + "詄" => &["die"], + "詅" => &["ling"], + "詆" => &["di"], + "詇" => &["yang"], + "詈" => &["li"], + "詉" => &["nao"], + "詊" => &["pan"], + "詋" => &["zhou"], + "詌" => &["gan"], + "詍" => &["shi"], + "詎" => &["ju"], + "詏" => &["ao"], + "詐" => &["zha"], + "詑" => &["tuo"], + "詒" => &["yi"], + "詓" => &["qu"], + "詔" => &["zhao"], + "評" => &["ping"], + "詖" => &["bi"], + "詗" => &["xiong"], + "詘" => &["chu","qu"], + "詙" => &["ba"], + "詚" => &["da"], + "詛" => &["zu"], + "詜" => &["tao"], + "詝" => &["zhu"], + "詞" => &["ci"], + "詟" => &["zhe"], + "詠" => &["yong"], + "詡" => &["xu"], + "詢" => &["xun"], + "詣" => &["yi"], + "詤" => &["huang"], + "詥" => &["he"], + "試" => &["shi"], + "詧" => &["cha"], + "詨" => &["jiao"], + "詩" => &["shi"], + "詪" => &["hen"], + "詫" => &["cha"], + "詬" => &["gou"], + "詭" => &["gui"], + "詮" => &["quan"], + "詯" => &["hui"], + "詰" => &["jie"], + "話" => &["hua"], + "該" => &["gai"], + "詳" => &["xiang"], + "詴" => &["hui"], + "詵" => &["shen"], + "詶" => &["chou"], + "詷" => &["tong"], + "詸" => &["mi"], + "詹" => &["zhan"], + "詺" => &["ming"], + "詻" => &["e"], + "詼" => &["hui"], + "詽" => &["yan"], + "詾" => &["xiong"], + "詿" => &["gua"], + "誀" => &["er"], + "誁" => &["beng"], + "誂" => &["tiao","diao"], + "誃" => &["chi"], + "誄" => &["lei"], + "誅" => &["zhu"], + "誆" => &["kuang"], + "誇" => &["kua"], + "誈" => &["wu"], + "誉" => &["yu"], + "誊" => &["teng"], + "誋" => &["ji"], + "誌" => &["zhi"], + "認" => &["ren"], + "誎" => &["su"], + "誏" => &["lang"], + "誐" => &["e"], + "誑" => &["kuang"], + "誒" => &["e^"], + "誓" => &["shi"], + "誔" => &["ting"], + "誕" => &["dan"], + "誖" => &["bei"], + "誗" => &["chan"], + "誘" => &["you"], + "誙" => &["heng"], + "誚" => &["qiao"], + "誛" => &["qin"], + "誜" => &["shua"], + "誝" => &["an"], + "語" => &["yu"], + "誟" => &["xiao"], + "誠" => &["cheng"], + "誡" => &["jie"], + "誢" => &["xian"], + "誣" => &["wu"], + "誤" => &["wu"], + "誥" => &["gao"], + "誦" => &["song"], + "誧" => &["pu"], + "誨" => &["hui"], + "誩" => &["jing"], + "說" => &["shuo","shui","yue"], + "誫" => &["zhen"], + "説" => &["shuo","shui","yue"], + "読" => &["du","dou"], + "誮" => &["hua"], + "誯" => &["chang"], + "誰" => &["shui","shei"], + "誱" => &["jie"], + "課" => &["ke"], + "誳" => &["qu"], + "誴" => &["cong"], + "誵" => &["xiao"], + "誶" => &["sui"], + "誷" => &["wang"], + "誸" => &["xuan"], + "誹" => &["fei"], + "誺" => &["chi"], + "誻" => &["ta"], + "誼" => &["yi"], + "誽" => &["na"], + "誾" => &["yin"], + "調" => &["diao","tiao"], + "諀" => &["pi"], + "諁" => &["chuo"], + "諂" => &["chan"], + "諃" => &["chen"], + "諄" => &["zhun"], + "諅" => &["ji"], + "諆" => &["qi"], + "談" => &["tan"], + "諈" => &["chui"], + "諉" => &["wei"], + "諊" => &["ju"], + "請" => &["qing"], + "諌" => &["jian"], + "諍" => &["zheng"], + "諎" => &["ze"], + "諏" => &["zou"], + "諐" => &["qian"], + "諑" => &["zhuo"], + "諒" => &["liang"], + "諓" => &["jian"], + "諔" => &["zhu"], + "諕" => &["hao"], + "論" => &["lun"], + "諗" => &["shen"], + "諘" => &["biao"], + "諙" => &["huai"], + "諚" => &["pian"], + "諛" => &["yu"], + "諜" => &["die"], + "諝" => &["xu"], + "諞" => &["pian"], + "諟" => &["shi"], + "諠" => &["xuan"], + "諡" => &["shi"], + "諢" => &["hun"], + "諣" => &["hua"], + "諤" => &["e"], + "諥" => &["zhong"], + "諦" => &["di"], + "諧" => &["xie"], + "諨" => &["fu"], + "諩" => &["pu"], + "諪" => &["ting"], + "諫" => &["jian"], + "諬" => &["qi"], + "諭" => &["yu"], + "諮" => &["zi"], + "諯" => &["chuan"], + "諰" => &["xi"], + "諱" => &["hui"], + "諲" => &["yin"], + "諳" => &["an"], + "諴" => &["xian"], + "諵" => &["nan"], + "諶" => &["chen"], + "諷" => &["feng"], + "諸" => &["zhu"], + "諹" => &["yang"], + "諺" => &["yan"], + "諻" => &["heng"], + "諼" => &["xuan"], + "諽" => &["ge"], + "諾" => &["nuo"], + "諿" => &["qi"], + "謀" => &["mou"], + "謁" => &["ye"], + "謂" => &["wei"], + "謄" => &["teng"], + "謅" => &["zou","zhou"], + "謆" => &["shan"], + "謇" => &["jian"], + "謈" => &["bo"], + "謊" => &["huang"], + "謋" => &["huo"], + "謌" => &["ge"], + "謍" => &["ying"], + "謎" => &["mi","mei"], + "謏" => &["xiao","sou"], + "謐" => &["mi"], + "謑" => &["xi"], + "謒" => &["qiang"], + "謓" => &["chen"], + "謔" => &["nu:e","xue"], + "謕" => &["si"], + "謖" => &["su"], + "謗" => &["bang"], + "謘" => &["chi"], + "謙" => &["qian"], + "謚" => &["shi","yi"], + "講" => &["jiang"], + "謜" => &["yuan"], + "謝" => &["xie"], + "謞" => &["xue"], + "謟" => &["tao"], + "謠" => &["yao"], + "謡" => &["yao"], + "謢" => &["hu"], + "謣" => &["yu"], + "謤" => &["biao"], + "謥" => &["cong"], + "謦" => &["qing"], + "謧" => &["li"], + "謨" => &["mo"], + "謩" => &["mo"], + "謪" => &["shang"], + "謫" => &["zhe"], + "謬" => &["miu"], + "謭" => &["jian"], + "謮" => &["ze"], + "謯" => &["zha"], + "謰" => &["lian"], + "謱" => &["lou"], + "謲" => &["can"], + "謳" => &["ou"], + "謴" => &["guan"], + "謵" => &["xi"], + "謶" => &["zhuo"], + "謷" => &["ao"], + "謸" => &["ao"], + "謹" => &["jin"], + "謺" => &["zhe"], + "謻" => &["yi"], + "謼" => &["hu"], + "謽" => &["jiang"], + "謾" => &["man"], + "謿" => &["chao"], + "譀" => &["han"], + "譁" => &["hua"], + "譂" => &["chan"], + "譃" => &["xu"], + "譄" => &["zeng"], + "譅" => &["se"], + "譆" => &["xi"], + "譇" => &["she"], + "譈" => &["dui"], + "證" => &["zheng"], + "譊" => &["nao"], + "譋" => &["lan"], + "譌" => &["e"], + "譍" => &["ying"], + "譎" => &["jue"], + "譏" => &["ji"], + "譐" => &["zun"], + "譑" => &["jiao"], + "譒" => &["bo"], + "譓" => &["hui"], + "譔" => &["zhuan"], + "譕" => &["wu"], + "譖" => &["jian","zen"], + "譗" => &["zha"], + "識" => &["shi","zhi"], + "譙" => &["qiao"], + "譚" => &["tan"], + "譛" => &["zen"], + "譜" => &["pu"], + "譝" => &["sheng"], + "譞" => &["xuan"], + "譟" => &["zao"], + "譠" => &["zhan"], + "譡" => &["dang"], + "譢" => &["sui"], + "譣" => &["qian"], + "譤" => &["ji"], + "譥" => &["jiao"], + "警" => &["jing"], + "譧" => &["lian"], + "譨" => &["nou"], + "譩" => &["yi"], + "譪" => &["ai"], + "譫" => &["zhan"], + "譬" => &["pi"], + "譭" => &["hui"], + "譮" => &["hua"], + "譯" => &["yi"], + "議" => &["yi"], + "譱" => &["shan"], + "譲" => &["rang"], + "譳" => &["nou"], + "譴" => &["qian"], + "譵" => &["zhui"], + "譶" => &["ta"], + "護" => &["hu"], + "譸" => &["zhou"], + "譹" => &["hao"], + "譺" => &["ni"], + "譻" => &["ying"], + "譼" => &["jian"], + "譽" => &["yu"], + "譾" => &["jian"], + "譿" => &["hui"], + "讀" => &["du","dou"], + "讁" => &["zhe"], + "讂" => &["xuan"], + "讃" => &["zan"], + "讄" => &["lei"], + "讅" => &["shen"], + "讆" => &["wei"], + "讇" => &["chan"], + "讈" => &["li"], + "讉" => &["yi"], + "變" => &["bian"], + "讋" => &["zhe"], + "讌" => &["yan"], + "讍" => &["e"], + "讎" => &["chou"], + "讏" => &["wei"], + "讐" => &["chou"], + "讑" => &["yao"], + "讒" => &["chan"], + "讓" => &["rang"], + "讔" => &["yin"], + "讕" => &["lan"], + "讖" => &["chen"], + "讗" => &["huo"], + "讘" => &["zhe"], + "讙" => &["huan"], + "讚" => &["zan"], + "讛" => &["yi"], + "讜" => &["dang"], + "讝" => &["zhan"], + "讞" => &["yan"], + "讟" => &["du"], + "讠" => &["yan"], + "计" => &["ji"], + "订" => &["ding"], + "讣" => &["fu"], + "认" => &["ren"], + "讥" => &["ji"], + "讦" => &["jie"], + "讧" => &["hong"], + "讨" => &["tao"], + "让" => &["rang"], + "讪" => &["shan"], + "讫" => &["qi"], + "讬" => &["tuo"], + "训" => &["xun"], + "议" => &["yi"], + "讯" => &["xun"], + "记" => &["ji"], + "讱" => &["ren"], + "讲" => &["jiang"], + "讳" => &["hui"], + "讴" => &["ou"], + "讵" => &["ju"], + "讶" => &["ya"], + "讷" => &["ne"], + "许" => &["xu"], + "讹" => &["e"], + "论" => &["lun"], + "讻" => &["xiong"], + "讼" => &["song"], + "讽" => &["feng"], + "设" => &["she"], + "访" => &["fang"], + "诀" => &["jue"], + "证" => &["zheng"], + "诂" => &["gu"], + "诃" => &["he"], + "评" => &["ping"], + "诅" => &["zu"], + "识" => &["shi","zhi"], + "诇" => &["xiong"], + "诈" => &["zha"], + "诉" => &["su"], + "诊" => &["zhen"], + "诋" => &["di"], + "诌" => &["zhou"], + "词" => &["ci"], + "诎" => &["qu"], + "诏" => &["zhao"], + "诐" => &["bi"], + "译" => &["yi"], + "诒" => &["yi"], + "诓" => &["kuang"], + "诔" => &["lei"], + "试" => &["shi"], + "诖" => &["gua"], + "诗" => &["shi"], + "诘" => &["jie","ji"], + "诙" => &["hui"], + "诚" => &["cheng"], + "诛" => &["zhu"], + "诜" => &["shen"], + "话" => &["hua"], + "诞" => &["dan"], + "诟" => &["gou"], + "诠" => &["quan"], + "诡" => &["gui"], + "询" => &["xun"], + "诣" => &["yi"], + "诤" => &["zheng"], + "该" => &["gai"], + "详" => &["xiang"], + "诧" => &["cha"], + "诨" => &["hun"], + "诩" => &["xu"], + "诪" => &["zhou"], + "诫" => &["jie"], + "诬" => &["wu"], + "语" => &["yu"], + "诮" => &["qiao"], + "误" => &["wu"], + "诰" => &["gao"], + "诱" => &["you"], + "诲" => &["hui"], + "诳" => &["kuang"], + "说" => &["shuo","shui","yue"], + "诵" => &["song"], + "诶" => &["ei","ai","e^"], + "请" => &["qing"], + "诸" => &["zhu"], + "诹" => &["zou"], + "诺" => &["nuo"], + "读" => &["du","dou"], + "诼" => &["zhuo"], + "诽" => &["fei"], + "课" => &["ke"], + "诿" => &["wei"], + "谀" => &["yu"], + "谁" => &["shui","shei"], + "谂" => &["shen"], + "调" => &["diao","tiao"], + "谄" => &["chan"], + "谅" => &["liang"], + "谆" => &["zhun"], + "谇" => &["sui"], + "谈" => &["tan"], + "谉" => &["shen"], + "谊" => &["yi"], + "谋" => &["mou"], + "谌" => &["chen"], + "谍" => &["die"], + "谎" => &["huang"], + "谏" => &["jian"], + "谐" => &["xie"], + "谑" => &["xue"], + "谒" => &["ye"], + "谓" => &["wei"], + "谔" => &["e"], + "谕" => &["yu"], + "谖" => &["xuan"], + "谗" => &["chan"], + "谘" => &["zi"], + "谙" => &["an"], + "谚" => &["yan"], + "谛" => &["di"], + "谜" => &["mi","mei"], + "谝" => &["pian"], + "谞" => &["xu"], + "谟" => &["mo"], + "谠" => &["dang"], + "谡" => &["su"], + "谢" => &["xie"], + "谣" => &["yao"], + "谤" => &["bang"], + "谥" => &["shi"], + "谦" => &["qian"], + "谧" => &["mi"], + "谨" => &["jin"], + "谩" => &["man"], + "谪" => &["zhe"], + "谫" => &["jian"], + "谬" => &["miu"], + "谭" => &["tan"], + "谮" => &["jian","zen"], + "谯" => &["qiao"], + "谰" => &["lan"], + "谱" => &["pu"], + "谲" => &["jue"], + "谳" => &["yan"], + "谴" => &["qian"], + "谵" => &["zhan"], + "谶" => &["chen"], + "谷" => &["gu","yu"], + "谸" => &["qian"], + "谹" => &["hong"], + "谺" => &["ya"], + "谻" => &["jue"], + "谼" => &["hong"], + "谽" => &["han"], + "谾" => &["hong"], + "谿" => &["qi","xi"], + "豀" => &["xi"], + "豁" => &["huo","hua"], + "豂" => &["liao"], + "豃" => &["han"], + "豄" => &["du"], + "豅" => &["long"], + "豆" => &["dou"], + "豇" => &["jiang"], + "豈" => &["qi","kai"], + "豉" => &["chi"], + "豊" => &["feng","li"], + "豋" => &["deng"], + "豌" => &["wan"], + "豍" => &["bi"], + "豎" => &["shu"], + "豏" => &["xian"], + "豐" => &["feng"], + "豑" => &["zhi"], + "豒" => &["zhi"], + "豓" => &["yan"], + "豔" => &["yan"], + "豕" => &["shi"], + "豖" => &["chu"], + "豗" => &["hui"], + "豘" => &["tun"], + "豙" => &["yi"], + "豚" => &["tun"], + "豛" => &["yi"], + "豜" => &["jian"], + "豝" => &["ba"], + "豞" => &["hou"], + "豟" => &["e"], + "豠" => &["cu"], + "象" => &["xiang"], + "豢" => &["huan"], + "豣" => &["jian"], + "豤" => &["ken"], + "豥" => &["gai"], + "豦" => &["qu"], + "豧" => &["fu"], + "豨" => &["xi"], + "豩" => &["bin"], + "豪" => &["hao"], + "豫" => &["yu"], + "豬" => &["zhu"], + "豭" => &["jia"], + "豮" => &["fen"], + "豯" => &["xi"], + "豰" => &["hu"], + "豱" => &["wen"], + "豲" => &["huan"], + "豳" => &["bin"], + "豴" => &["di"], + "豵" => &["zong"], + "豶" => &["fen"], + "豷" => &["yi"], + "豸" => &["zhi"], + "豹" => &["bao"], + "豺" => &["chai"], + "豻" => &["han","an"], + "豼" => &["pi"], + "豽" => &["na"], + "豾" => &["pi"], + "豿" => &["gou"], + "貀" => &["duo"], + "貁" => &["you"], + "貂" => &["diao"], + "貃" => &["mo"], + "貄" => &["si"], + "貅" => &["xiu"], + "貆" => &["huan"], + "貇" => &["kun"], + "貈" => &["he"], + "貉" => &["he","hao","mo"], + "貊" => &["mo"], + "貋" => &["an"], + "貌" => &["mao"], + "貍" => &["li"], + "貎" => &["ni"], + "貏" => &["bi"], + "貐" => &["yu"], + "貑" => &["jia"], + "貒" => &["tuan"], + "貓" => &["mao"], + "貔" => &["pi"], + "貕" => &["xi"], + "貖" => &["e"], + "貗" => &["ju"], + "貘" => &["mo"], + "貙" => &["chu"], + "貚" => &["tan"], + "貛" => &["huan"], + "貜" => &["qu"], + "貝" => &["bei"], + "貞" => &["zhen"], + "貟" => &["yuan","yun"], + "負" => &["fu"], + "財" => &["cai"], + "貢" => &["gong"], + "貣" => &["te"], + "貤" => &["yi"], + "貥" => &["hang"], + "貦" => &["wan"], + "貧" => &["pin"], + "貨" => &["huo"], + "販" => &["fan"], + "貪" => &["tan"], + "貫" => &["guan"], + "責" => &["ze","zhai"], + "貭" => &["zhi"], + "貮" => &["er"], + "貯" => &["zhu"], + "貰" => &["shi"], + "貱" => &["bi"], + "貲" => &["zi"], + "貳" => &["er"], + "貴" => &["gui"], + "貵" => &["pian"], + "貶" => &["bian"], + "買" => &["mai"], + "貸" => &["dai"], + "貹" => &["sheng"], + "貺" => &["kuang"], + "費" => &["fei"], + "貼" => &["tie"], + "貽" => &["yi"], + "貾" => &["chi"], + "貿" => &["mao"], + "賀" => &["he"], + "賁" => &["bi","ben"], + "賂" => &["lu"], + "賃" => &["lin","ren"], + "賄" => &["hui"], + "賅" => &["gai"], + "賆" => &["pian"], + "資" => &["zi"], + "賈" => &["jia","gu"], + "賉" => &["xu"], + "賊" => &["zei","ze"], + "賋" => &["jiao"], + "賌" => &["gai"], + "賍" => &["zang"], + "賎" => &["jian"], + "賏" => &["ying"], + "賐" => &["xun"], + "賑" => &["zhen"], + "賒" => &["she"], + "賓" => &["bin"], + "賔" => &["bin"], + "賕" => &["qiu"], + "賖" => &["she"], + "賗" => &["chuan"], + "賘" => &["zang"], + "賙" => &["zhou"], + "賚" => &["lai"], + "賛" => &["zan"], + "賜" => &["si","ci"], + "賝" => &["chen"], + "賞" => &["shang"], + "賟" => &["tian"], + "賠" => &["pei"], + "賡" => &["geng"], + "賢" => &["xian"], + "賣" => &["mai"], + "賤" => &["jian"], + "賥" => &["sui"], + "賦" => &["fu"], + "賧" => &["dan"], + "賨" => &["cong"], + "賩" => &["cong"], + "質" => &["zhi"], + "賫" => &["ji"], + "賬" => &["zhang"], + "賭" => &["du"], + "賮" => &["jin"], + "賯" => &["xiong"], + "賰" => &["shun"], + "賱" => &["yun"], + "賲" => &["bao"], + "賳" => &["zai"], + "賴" => &["lai"], + "賵" => &["feng"], + "賶" => &["cang"], + "賷" => &["ji"], + "賸" => &["sheng"], + "賹" => &["ai"], + "賺" => &["zhuan","zuan"], + "賻" => &["fu"], + "購" => &["gou"], + "賽" => &["sai"], + "賾" => &["ze"], + "賿" => &["liao"], + "贀" => &["wei"], + "贁" => &["bai"], + "贂" => &["chen"], + "贃" => &["zhuan"], + "贄" => &["zhi"], + "贅" => &["zhui"], + "贆" => &["biao"], + "贇" => &["yun"], + "贈" => &["zeng"], + "贉" => &["tan"], + "贊" => &["zan"], + "贋" => &["yan"], + "贍" => &["shan"], + "贎" => &["wan"], + "贏" => &["ying"], + "贐" => &["jin"], + "贑" => &["gan"], + "贒" => &["xian"], + "贓" => &["zang"], + "贔" => &["bi"], + "贕" => &["du"], + "贖" => &["shu"], + "贗" => &["yan"], + "贙" => &["xuan"], + "贚" => &["long"], + "贛" => &["gan"], + "贜" => &["zang"], + "贝" => &["bei"], + "贞" => &["zhen"], + "负" => &["fu"], + "贠" => &["yuan","yun"], + "贡" => &["gong"], + "财" => &["cai"], + "责" => &["ze"], + "贤" => &["xian"], + "败" => &["bai"], + "账" => &["zhang"], + "货" => &["huo"], + "质" => &["zhi"], + "贩" => &["fan"], + "贪" => &["tan"], + "贫" => &["pin"], + "贬" => &["bian"], + "购" => &["gou"], + "贮" => &["zhu"], + "贯" => &["guan"], + "贰" => &["er"], + "贱" => &["jian"], + "贲" => &["bi","ben"], + "贳" => &["shi"], + "贴" => &["tie"], + "贵" => &["gui"], + "贶" => &["kuang"], + "贷" => &["dai"], + "贸" => &["mao"], + "费" => &["fei"], + "贺" => &["he"], + "贻" => &["yi"], + "贼" => &["zei"], + "贽" => &["zhi"], + "贾" => &["jia","gu"], + "贿" => &["hui"], + "赀" => &["zi"], + "赁" => &["lin"], + "赂" => &["lu"], + "赃" => &["zang"], + "资" => &["zi"], + "赅" => &["gai"], + "赆" => &["jin"], + "赇" => &["qiu"], + "赈" => &["zhen"], + "赉" => &["lai"], + "赊" => &["she"], + "赋" => &["fu"], + "赌" => &["du"], + "赍" => &["ji"], + "赎" => &["shu"], + "赏" => &["shang"], + "赐" => &["ci"], + "赑" => &["bi"], + "赒" => &["zhou"], + "赓" => &["geng"], + "赔" => &["pei"], + "赕" => &["dan"], + "赖" => &["lai"], + "赗" => &["feng"], + "赘" => &["zhui"], + "赙" => &["fu"], + "赚" => &["zhuan","zuan"], + "赛" => &["sai"], + "赜" => &["ze"], + "赝" => &["yan"], + "赞" => &["zan"], + "赟" => &["yun"], + "赠" => &["zeng"], + "赡" => &["shan"], + "赢" => &["ying"], + "赣" => &["gan"], + "赤" => &["chi"], + "赥" => &["xi"], + "赦" => &["she"], + "赧" => &["nan"], + "赨" => &["xiong"], + "赩" => &["xi"], + "赪" => &["cheng"], + "赫" => &["he"], + "赬" => &["cheng"], + "赭" => &["zhe"], + "赮" => &["xia"], + "赯" => &["tang"], + "走" => &["zou"], + "赱" => &["zou"], + "赲" => &["li"], + "赳" => &["jiu"], + "赴" => &["fu"], + "赵" => &["zhao"], + "赶" => &["gan"], + "起" => &["qi"], + "赸" => &["shan"], + "赹" => &["qiong"], + "赺" => &["qin"], + "赻" => &["xian"], + "赼" => &["ci"], + "赽" => &["jue"], + "赾" => &["qin"], + "赿" => &["chi"], + "趀" => &["ci"], + "趁" => &["chen"], + "趂" => &["chen"], + "趃" => &["die"], + "趄" => &["ju","qie"], + "超" => &["chao"], + "趆" => &["di"], + "趇" => &["se"], + "趈" => &["zhan"], + "趉" => &["zhu"], + "越" => &["yue"], + "趋" => &["qu"], + "趌" => &["jie"], + "趍" => &["chi"], + "趎" => &["chu"], + "趏" => &["gua"], + "趐" => &["xue"], + "趑" => &["zi"], + "趒" => &["tiao"], + "趓" => &["duo"], + "趔" => &["lie"], + "趕" => &["gan"], + "趖" => &["suo"], + "趗" => &["cu"], + "趘" => &["xi"], + "趙" => &["zhao"], + "趚" => &["su"], + "趛" => &["yin"], + "趜" => &["ju"], + "趝" => &["jian"], + "趞" => &["que"], + "趟" => &["tang"], + "趠" => &["chuo"], + "趡" => &["cui"], + "趢" => &["lu"], + "趣" => &["qu","cu"], + "趤" => &["dang"], + "趥" => &["qiu"], + "趦" => &["zi"], + "趧" => &["ti"], + "趨" => &["qu","cu"], + "趩" => &["chi"], + "趪" => &["huang"], + "趫" => &["qiao"], + "趬" => &["qiao"], + "趭" => &["yao"], + "趮" => &["zao"], + "趯" => &["yue"], + "趱" => &["zan"], + "趲" => &["zan"], + "足" => &["zu","ju"], + "趴" => &["pa"], + "趵" => &["bao","bo"], + "趶" => &["ku"], + "趷" => &["he"], + "趸" => &["dun"], + "趹" => &["jue"], + "趺" => &["fu"], + "趻" => &["chen"], + "趼" => &["jian"], + "趽" => &["fang"], + "趾" => &["zhi"], + "趿" => &["ta"], + "跀" => &["yue"], + "跁" => &["pa"], + "跂" => &["qi"], + "跃" => &["yue"], + "跄" => &["qiang"], + "跅" => &["tuo"], + "跆" => &["tai"], + "跇" => &["yi"], + "跈" => &["nian"], + "跉" => &["ling"], + "跊" => &["mei"], + "跋" => &["ba"], + "跌" => &["die"], + "跍" => &["ku"], + "跎" => &["tuo"], + "跏" => &["jia"], + "跐" => &["ci"], + "跑" => &["pao"], + "跒" => &["qia"], + "跓" => &["zhu"], + "跔" => &["ju"], + "跕" => &["die"], + "跖" => &["zhi"], + "跗" => &["fu"], + "跘" => &["pan"], + "跙" => &["ju"], + "跚" => &["shan"], + "跛" => &["bo"], + "跜" => &["ni"], + "距" => &["ju"], + "跞" => &["li","luo"], + "跟" => &["gen"], + "跠" => &["yi"], + "跡" => &["ji"], + "跢" => &["dai"], + "跣" => &["xian"], + "跤" => &["jiao"], + "跥" => &["duo"], + "跦" => &["chu"], + "跧" => &["quan"], + "跨" => &["kua"], + "跩" => &["zhuai","shi"], + "跪" => &["gui"], + "跫" => &["qiong"], + "跬" => &["kui"], + "跭" => &["xiang"], + "跮" => &["chi"], + "路" => &["lu"], + "跰" => &["beng"], + "跱" => &["zhi"], + "跲" => &["jia"], + "跳" => &["tiao"], + "跴" => &["cai"], + "践" => &["jian"], + "跶" => &["da"], + "跷" => &["qiao"], + "跸" => &["bi"], + "跹" => &["xian"], + "跺" => &["duo"], + "跻" => &["ji"], + "跼" => &["ju"], + "跽" => &["ji"], + "跾" => &["shu"], + "跿" => &["tu"], + "踀" => &["chu"], + "踁" => &["xing"], + "踂" => &["nie"], + "踃" => &["xiao"], + "踄" => &["bo"], + "踅" => &["xue"], + "踆" => &["qun"], + "踇" => &["mou"], + "踈" => &["shu"], + "踉" => &["liang"], + "踊" => &["yong"], + "踋" => &["jiao","jia","jue"], + "踌" => &["chou"], + "踍" => &["xiao"], + "踏" => &["ta"], + "踐" => &["jian"], + "踑" => &["qi"], + "踒" => &["wo"], + "踓" => &["wei"], + "踔" => &["chuo"], + "踕" => &["jie"], + "踖" => &["ji"], + "踗" => &["nie"], + "踘" => &["ju"], + "踙" => &["ju"], + "踚" => &["lun"], + "踛" => &["lu"], + "踜" => &["leng"], + "踝" => &["huai"], + "踞" => &["ju"], + "踟" => &["chi"], + "踠" => &["wan"], + "踡" => &["quan"], + "踢" => &["ti"], + "踣" => &["bo"], + "踤" => &["zu"], + "踥" => &["qie"], + "踦" => &["qi"], + "踧" => &["cu"], + "踨" => &["zong"], + "踩" => &["cai"], + "踪" => &["zong"], + "踫" => &["pan"], + "踬" => &["zhi"], + "踭" => &["zheng"], + "踮" => &["dian","die"], + "踯" => &["zhi"], + "踰" => &["yu"], + "踱" => &["duo"], + "踲" => &["dun"], + "踳" => &["chun"], + "踴" => &["yong"], + "踵" => &["zhong"], + "踶" => &["di"], + "踷" => &["zha"], + "踸" => &["chen"], + "踹" => &["chuai"], + "踺" => &["jian"], + "踻" => &["gua"], + "踼" => &["tang"], + "踽" => &["ju"], + "踾" => &["fu"], + "踿" => &["zu"], + "蹀" => &["die"], + "蹁" => &["pian"], + "蹂" => &["rou"], + "蹃" => &["nuo"], + "蹄" => &["ti"], + "蹅" => &["cha"], + "蹆" => &["tui"], + "蹇" => &["jian"], + "蹈" => &["dao"], + "蹉" => &["cuo"], + "蹊" => &["xi","qi"], + "蹋" => &["ta"], + "蹌" => &["qiang"], + "蹍" => &["zhan"], + "蹎" => &["dian"], + "蹏" => &["ti"], + "蹐" => &["ji"], + "蹑" => &["nie"], + "蹒" => &["pan"], + "蹓" => &["liu"], + "蹔" => &["zhan"], + "蹕" => &["bi"], + "蹖" => &["chong"], + "蹗" => &["lu"], + "蹘" => &["liao"], + "蹙" => &["cu"], + "蹚" => &["tang"], + "蹛" => &["dai"], + "蹜" => &["su"], + "蹝" => &["xi"], + "蹞" => &["kui"], + "蹟" => &["ji"], + "蹠" => &["zhi"], + "蹡" => &["qiang"], + "蹢" => &["di","zhi"], + "蹣" => &["man","pan"], + "蹤" => &["zong"], + "蹥" => &["lian"], + "蹦" => &["beng"], + "蹧" => &["zao"], + "蹨" => &["nian"], + "蹩" => &["bie"], + "蹪" => &["tui"], + "蹫" => &["ju"], + "蹬" => &["deng"], + "蹭" => &["ceng"], + "蹮" => &["xian"], + "蹯" => &["fan"], + "蹰" => &["chu"], + "蹱" => &["zhong"], + "蹲" => &["dun","cun"], + "蹳" => &["bo"], + "蹴" => &["cu"], + "蹵" => &["zu"], + "蹶" => &["jue"], + "蹷" => &["jue"], + "蹸" => &["lin"], + "蹹" => &["ta"], + "蹺" => &["qiao"], + "蹻" => &["qiao"], + "蹼" => &["pu"], + "蹽" => &["liao"], + "蹾" => &["dun"], + "蹿" => &["cuan"], + "躀" => &["kuang"], + "躁" => &["zao"], + "躂" => &["ta"], + "躃" => &["bi"], + "躄" => &["bi"], + "躅" => &["zhu"], + "躆" => &["ju"], + "躇" => &["chu"], + "躈" => &["qiao"], + "躉" => &["dun"], + "躊" => &["chou"], + "躋" => &["ji"], + "躌" => &["wu"], + "躍" => &["yue"], + "躎" => &["nian"], + "躏" => &["lin"], + "躐" => &["lie"], + "躑" => &["zhi"], + "躒" => &["li"], + "躓" => &["zhi"], + "躔" => &["chan"], + "躕" => &["chu"], + "躖" => &["duan"], + "躗" => &["wei"], + "躘" => &["long"], + "躙" => &["lin"], + "躚" => &["xian"], + "躛" => &["wei"], + "躜" => &["zuan"], + "躝" => &["lan"], + "躞" => &["xie"], + "躟" => &["rang"], + "躠" => &["xie"], + "躡" => &["nie"], + "躢" => &["ta"], + "躣" => &["qu"], + "躤" => &["jie"], + "躥" => &["cuan"], + "躦" => &["zuan"], + "躧" => &["xi"], + "躨" => &["kui"], + "躩" => &["jue"], + "躪" => &["lin"], + "身" => &["shen","juan"], + "躬" => &["gong"], + "躭" => &["dan"], + "躯" => &["qu"], + "躰" => &["ti"], + "躱" => &["duo"], + "躲" => &["duo"], + "躳" => &["gong"], + "躴" => &["lang"], + "躶" => &["luo"], + "躷" => &["ai"], + "躸" => &["ji"], + "躹" => &["ju"], + "躺" => &["tang"], + "躽" => &["yan"], + "躿" => &["kang"], + "軀" => &["qu"], + "軁" => &["lou"], + "軂" => &["lao"], + "軃" => &["duo"], + "軄" => &["zhi"], + "軆" => &["ti"], + "軇" => &["dao"], + "軉" => &["yu"], + "車" => &["che","ju"], + "軋" => &["ya","zha","ga"], + "軌" => &["gui"], + "軍" => &["jun"], + "軎" => &["wei"], + "軏" => &["yue"], + "軐" => &["xin"], + "軑" => &["di"], + "軒" => &["xuan"], + "軓" => &["fan"], + "軔" => &["ren"], + "軕" => &["shan"], + "軖" => &["qiang"], + "軗" => &["shu"], + "軘" => &["tun"], + "軙" => &["chen"], + "軚" => &["dai"], + "軛" => &["e"], + "軜" => &["na"], + "軝" => &["qi"], + "軞" => &["mao"], + "軟" => &["ruan"], + "軠" => &["ren"], + "軡" => &["qian"], + "転" => &["zhuan","zhuai"], + "軣" => &["hong"], + "軤" => &["hu"], + "軥" => &["qu"], + "軦" => &["huang"], + "軧" => &["di"], + "軨" => &["ling"], + "軩" => &["dai"], + "軪" => &["ao"], + "軫" => &["zhen"], + "軬" => &["fan"], + "軭" => &["kuang"], + "軮" => &["ang"], + "軯" => &["peng"], + "軰" => &["bei"], + "軱" => &["gu"], + "軲" => &["gu"], + "軳" => &["pao"], + "軴" => &["zhu"], + "軵" => &["rong","fu"], + "軶" => &["e"], + "軷" => &["ba"], + "軸" => &["zhou","zhu"], + "軹" => &["zhi"], + "軺" => &["yao"], + "軻" => &["ke"], + "軼" => &["yi"], + "軽" => &["qing"], + "軾" => &["shi"], + "軿" => &["ping"], + "輀" => &["er"], + "輁" => &["qiong"], + "輂" => &["ju"], + "較" => &["jiao"], + "輄" => &["guang"], + "輅" => &["lu"], + "輆" => &["kai"], + "輇" => &["quan"], + "輈" => &["zhou"], + "載" => &["zai"], + "輊" => &["zhi"], + "輋" => &["ju"], + "輌" => &["liang"], + "輍" => &["yu"], + "輎" => &["shao"], + "輏" => &["you"], + "輐" => &["huan"], + "輑" => &["yun"], + "輒" => &["zhe"], + "輓" => &["wan"], + "輔" => &["fu"], + "輕" => &["qing"], + "輖" => &["zhou"], + "輗" => &["ni"], + "輘" => &["ling"], + "輙" => &["zhe"], + "輚" => &["zhan"], + "輛" => &["liang"], + "輜" => &["zi"], + "輝" => &["hui"], + "輞" => &["wang"], + "輟" => &["chuo"], + "輠" => &["guo"], + "輡" => &["kan"], + "輢" => &["yi"], + "輣" => &["peng"], + "輤" => &["qian"], + "輥" => &["gun"], + "輦" => &["nian"], + "輧" => &["ping"], + "輨" => &["guan"], + "輩" => &["bei"], + "輪" => &["lun"], + "輫" => &["pai"], + "輬" => &["liang"], + "輭" => &["ruan"], + "輮" => &["rou"], + "輯" => &["ji"], + "輰" => &["yang"], + "輱" => &["xian"], + "輲" => &["chuan"], + "輳" => &["cou"], + "輴" => &["chun"], + "輵" => &["ge"], + "輶" => &["you"], + "輷" => &["hong"], + "輸" => &["shu"], + "輹" => &["fu"], + "輺" => &["zi"], + "輻" => &["fu"], + "輼" => &["wen"], + "輽" => &["ben"], + "輾" => &["zhan"], + "輿" => &["yu"], + "轀" => &["wen"], + "轁" => &["tao"], + "轂" => &["gu"], + "轃" => &["zhen"], + "轄" => &["xia"], + "轅" => &["yuan"], + "轆" => &["lu"], + "轇" => &["jiu"], + "轈" => &["chao"], + "轉" => &["zhuan","zhuai"], + "轊" => &["wei"], + "轋" => &["hun"], + "轍" => &["che","zhe"], + "轎" => &["jiao"], + "轏" => &["zhan"], + "轐" => &["pu"], + "轑" => &["lao"], + "轒" => &["fen"], + "轓" => &["fan"], + "轔" => &["lin"], + "轕" => &["ge"], + "轖" => &["se"], + "轗" => &["kan"], + "轘" => &["huan"], + "轙" => &["yi"], + "轚" => &["ji"], + "轛" => &["dui"], + "轜" => &["er"], + "轝" => &["yu"], + "轞" => &["xian"], + "轟" => &["hong"], + "轠" => &["lei"], + "轡" => &["pei"], + "轢" => &["li"], + "轣" => &["li"], + "轤" => &["lu"], + "轥" => &["lin"], + "车" => &["che","ju"], + "轧" => &["ya","zha","ga"], + "轨" => &["gui"], + "轩" => &["xuan"], + "轪" => &["dai"], + "轫" => &["ren"], + "转" => &["zhuan","zhuai"], + "轭" => &["e"], + "轮" => &["lun"], + "软" => &["ruan"], + "轰" => &["hong"], + "轱" => &["gu"], + "轲" => &["ke"], + "轳" => &["lu"], + "轴" => &["zhou"], + "轵" => &["zhi"], + "轶" => &["yi"], + "轷" => &["hu"], + "轸" => &["zhen"], + "轹" => &["li"], + "轺" => &["yao"], + "轻" => &["qing"], + "轼" => &["shi"], + "载" => &["zai"], + "轾" => &["zhi"], + "轿" => &["jiao"], + "辀" => &["zhou"], + "辁" => &["quan"], + "辂" => &["lu"], + "较" => &["jiao"], + "辄" => &["zhe"], + "辅" => &["fu"], + "辆" => &["liang"], + "辇" => &["nian"], + "辈" => &["bei"], + "辉" => &["hui"], + "辊" => &["gun"], + "辋" => &["wang"], + "辌" => &["liang"], + "辍" => &["chuo"], + "辎" => &["zi"], + "辏" => &["cou"], + "辐" => &["fu"], + "辑" => &["ji"], + "辒" => &["wen"], + "输" => &["shu"], + "辔" => &["pei"], + "辕" => &["yuan"], + "辖" => &["xia"], + "辗" => &["zhan"], + "辘" => &["lu"], + "辙" => &["zhe"], + "辚" => &["lin"], + "辛" => &["xin"], + "辜" => &["gu"], + "辝" => &["ci"], + "辞" => &["ci"], + "辟" => &["pi","bi"], + "辠" => &["zui"], + "辡" => &["bian"], + "辢" => &["la"], + "辣" => &["la"], + "辤" => &["ci"], + "辥" => &["xue"], + "辦" => &["ban"], + "辧" => &["bian"], + "辨" => &["bian"], + "辩" => &["bian"], + "辫" => &["bian"], + "辬" => &["ban"], + "辭" => &["ci"], + "辮" => &["bian"], + "辯" => &["bian"], + "辰" => &["chen"], + "辱" => &["ru"], + "農" => &["nong"], + "辳" => &["nong"], + "辴" => &["zhen"], + "辵" => &["chuo"], + "辶" => &["chuo"], + "辸" => &["reng"], + "边" => &["bian"], + "辺" => &["bian"], + "辽" => &["liao"], + "达" => &["da"], + "辿" => &["chan"], + "迀" => &["gan"], + "迁" => &["qian"], + "迂" => &["yu"], + "迃" => &["yu"], + "迄" => &["qi"], + "迅" => &["xun"], + "迆" => &["yi"], + "过" => &["guo"], + "迈" => &["mai"], + "迉" => &["qi"], + "迊" => &["za"], + "迋" => &["wang"], + "迍" => &["zhun"], + "迎" => &["ying"], + "迏" => &["ti"], + "运" => &["yun"], + "近" => &["jin"], + "迒" => &["hang"], + "迓" => &["ya"], + "返" => &["fan"], + "迕" => &["wu"], + "迖" => &["ta"], + "迗" => &["e"], + "还" => &["hai","huan"], + "这" => &["zhe","zhei"], + "进" => &["jin"], + "远" => &["yuan"], + "违" => &["wei"], + "连" => &["lian"], + "迟" => &["chi"], + "迠" => &["che"], + "迡" => &["ni"], + "迢" => &["tiao"], + "迣" => &["zhi"], + "迤" => &["yi"], + "迥" => &["jiong"], + "迦" => &["jia"], + "迧" => &["chen"], + "迨" => &["dai"], + "迩" => &["er"], + "迪" => &["di"], + "迫" => &["po","pai"], + "迬" => &["wang"], + "迭" => &["die"], + "迮" => &["ze"], + "迯" => &["tao"], + "述" => &["shu"], + "迱" => &["tuo"], + "迳" => &["jing"], + "迴" => &["hui"], + "迵" => &["tong"], + "迶" => &["you"], + "迷" => &["mi"], + "迸" => &["beng"], + "迹" => &["ji"], + "迺" => &["nai"], + "迻" => &["yi"], + "迼" => &["jie"], + "追" => &["zhui"], + "迾" => &["lie"], + "迿" => &["xun"], + "退" => &["tui"], + "送" => &["song"], + "适" => &["shi","kuo"], + "逃" => &["tao"], + "逄" => &["pang"], + "逅" => &["hou"], + "逆" => &["ni"], + "逇" => &["dun"], + "逈" => &["jiong"], + "选" => &["xuan"], + "逊" => &["xun"], + "逋" => &["bu"], + "逌" => &["you"], + "逍" => &["xiao"], + "逎" => &["qiu"], + "透" => &["tou"], + "逐" => &["zhu"], + "逑" => &["qiu"], + "递" => &["di"], + "逓" => &["di"], + "途" => &["tu"], + "逕" => &["jing"], + "逖" => &["ti"], + "逗" => &["dou"], + "逘" => &["yi"], + "這" => &["zhe","zhei"], + "通" => &["tong"], + "逛" => &["guang"], + "逜" => &["wu"], + "逝" => &["shi"], + "逞" => &["cheng"], + "速" => &["su"], + "造" => &["zao"], + "逡" => &["qun"], + "逢" => &["feng"], + "連" => &["lian"], + "逤" => &["suo"], + "逥" => &["hui"], + "逦" => &["li"], + "逨" => &["zui"], + "逩" => &["ben"], + "逪" => &["cuo"], + "逫" => &["jue"], + "逬" => &["beng"], + "逭" => &["huan"], + "逮" => &["dai"], + "逯" => &["lu"], + "逰" => &["you"], + "週" => &["zhou"], + "進" => &["jin"], + "逳" => &["yu"], + "逴" => &["chuo"], + "逵" => &["kui"], + "逶" => &["wei"], + "逷" => &["ti"], + "逸" => &["yi"], + "逹" => &["da"], + "逺" => &["yuan"], + "逻" => &["luo"], + "逼" => &["bi"], + "逽" => &["nuo"], + "逾" => &["yu"], + "逿" => &["dang"], + "遀" => &["sui"], + "遁" => &["dun"], + "遂" => &["sui"], + "遃" => &["yan"], + "遄" => &["chuan"], + "遅" => &["chi"], + "遆" => &["ti"], + "遇" => &["yu"], + "遈" => &["shi"], + "遉" => &["zhen"], + "遊" => &["you"], + "運" => &["yun"], + "遌" => &["e"], + "遍" => &["bian","pian"], + "過" => &["guo"], + "遏" => &["e"], + "遐" => &["xia"], + "遑" => &["huang"], + "遒" => &["qiu"], + "道" => &["dao"], + "達" => &["da"], + "違" => &["wei"], + "遗" => &["yi","wei"], + "遘" => &["gou"], + "遙" => &["yao"], + "遚" => &["chu"], + "遛" => &["liu"], + "遜" => &["xun"], + "遝" => &["ta"], + "遞" => &["di"], + "遟" => &["chi"], + "遠" => &["yuan"], + "遡" => &["su"], + "遢" => &["ta"], + "遣" => &["qian"], + "遥" => &["yao"], + "遦" => &["guan"], + "遧" => &["zhang"], + "遨" => &["ao"], + "適" => &["shi","kuo"], + "遪" => &["ce"], + "遫" => &["su"], + "遬" => &["su"], + "遭" => &["zao"], + "遮" => &["zhe"], + "遯" => &["dun"], + "遰" => &["zhi"], + "遱" => &["lou"], + "遲" => &["chi"], + "遳" => &["cuo"], + "遴" => &["lin"], + "遵" => &["zun"], + "遶" => &["rao"], + "遷" => &["qian"], + "選" => &["xuan"], + "遹" => &["yu"], + "遺" => &["yi","wei"], + "遻" => &["wu"], + "遼" => &["liao"], + "遽" => &["ju"], + "遾" => &["shi"], + "避" => &["bi"], + "邀" => &["yao"], + "邁" => &["mai"], + "邂" => &["xie"], + "邃" => &["sui"], + "還" => &["huan","hai","xuan"], + "邅" => &["zhan"], + "邆" => &["deng"], + "邇" => &["er"], + "邈" => &["miao"], + "邉" => &["bian"], + "邊" => &["bian"], + "邋" => &["la"], + "邌" => &["li"], + "邍" => &["yuan"], + "邎" => &["you"], + "邏" => &["luo"], + "邐" => &["li"], + "邑" => &["yi"], + "邒" => &["ting"], + "邓" => &["deng"], + "邔" => &["qi"], + "邕" => &["yong"], + "邖" => &["shan"], + "邗" => &["han"], + "邘" => &["yu"], + "邙" => &["mang"], + "邚" => &["ru"], + "邛" => &["qiong"], + "邝" => &["kuang"], + "邞" => &["fu"], + "邟" => &["kang"], + "邠" => &["bin"], + "邡" => &["fang"], + "邢" => &["xing"], + "那" => &["na","nei"], + "邥" => &["shen"], + "邦" => &["bang"], + "邧" => &["yuan"], + "邨" => &["cun"], + "邩" => &["huo"], + "邪" => &["xie","ye"], + "邫" => &["bang"], + "邬" => &["wu"], + "邭" => &["ju"], + "邮" => &["you"], + "邯" => &["han"], + "邰" => &["tai"], + "邱" => &["qiu"], + "邲" => &["bi"], + "邳" => &["pi"], + "邴" => &["bing"], + "邵" => &["shao"], + "邶" => &["bei"], + "邷" => &["wa"], + "邸" => &["di"], + "邹" => &["zou"], + "邺" => &["ye"], + "邻" => &["lin"], + "邼" => &["kuang"], + "邽" => &["gui"], + "邾" => &["zhu"], + "邿" => &["shi"], + "郀" => &["ku"], + "郁" => &["yu"], + "郂" => &["gai"], + "郃" => &["he"], + "郄" => &["qie"], + "郅" => &["zhi"], + "郆" => &["ji"], + "郇" => &["xun","huan"], + "郈" => &["hou"], + "郉" => &["xing"], + "郊" => &["jiao"], + "郋" => &["xi"], + "郌" => &["gui"], + "郍" => &["nuo"], + "郎" => &["lang"], + "郏" => &["jia"], + "郐" => &["kuai"], + "郑" => &["zheng"], + "郒" => &["lang"], + "郓" => &["yun"], + "郔" => &["yan"], + "郕" => &["cheng"], + "郖" => &["dou"], + "郗" => &["xi"], + "郘" => &["lu:"], + "郙" => &["fu"], + "郚" => &["wu"], + "郛" => &["fu"], + "郜" => &["gao"], + "郝" => &["hao"], + "郞" => &["lang"], + "郟" => &["jia"], + "郠" => &["geng"], + "郡" => &["jun"], + "郢" => &["ying"], + "郣" => &["bo"], + "郤" => &["xi"], + "郥" => &["bei"], + "郦" => &["li"], + "郧" => &["yun"], + "部" => &["bu"], + "郩" => &["xiao"], + "郪" => &["qi"], + "郫" => &["pi"], + "郬" => &["qing"], + "郭" => &["guo"], + "郯" => &["tan"], + "郰" => &["zou"], + "郱" => &["ping"], + "郲" => &["lai"], + "郳" => &["ni"], + "郴" => &["chen"], + "郵" => &["you"], + "郶" => &["bu"], + "郷" => &["xiang"], + "郸" => &["dan"], + "郹" => &["ju"], + "郺" => &["yong"], + "郻" => &["qiao"], + "郼" => &["yi"], + "都" => &["dou","du"], + "郾" => &["yan"], + "郿" => &["mei"], + "鄀" => &["ruo"], + "鄁" => &["bei"], + "鄂" => &["e"], + "鄃" => &["yu"], + "鄄" => &["juan"], + "鄅" => &["yu"], + "鄆" => &["yun"], + "鄇" => &["hou"], + "鄈" => &["kui"], + "鄉" => &["xiang"], + "鄊" => &["xiang"], + "鄋" => &["sou"], + "鄌" => &["tang"], + "鄍" => &["ming"], + "鄎" => &["xi"], + "鄏" => &["ru"], + "鄐" => &["chu"], + "鄑" => &["zi"], + "鄒" => &["zou"], + "鄓" => &["ju"], + "鄔" => &["wu"], + "鄕" => &["xiang"], + "鄖" => &["yun"], + "鄗" => &["hao"], + "鄘" => &["yong"], + "鄙" => &["bi"], + "鄚" => &["mao"], + "鄛" => &["chao"], + "鄜" => &["fu"], + "鄝" => &["liao"], + "鄞" => &["yin"], + "鄟" => &["zhuan"], + "鄠" => &["hu"], + "鄡" => &["qiao"], + "鄢" => &["yan"], + "鄣" => &["zhang"], + "鄤" => &["fan"], + "鄥" => &["wu"], + "鄦" => &["xu"], + "鄧" => &["deng"], + "鄨" => &["bi"], + "鄩" => &["xin"], + "鄪" => &["bi"], + "鄫" => &["ceng"], + "鄬" => &["wei"], + "鄭" => &["zheng"], + "鄮" => &["mao"], + "鄯" => &["shan"], + "鄰" => &["lin"], + "鄱" => &["po"], + "鄲" => &["dan"], + "鄳" => &["meng"], + "鄴" => &["ye"], + "鄵" => &["cao"], + "鄶" => &["kuai"], + "鄷" => &["feng"], + "鄸" => &["meng"], + "鄹" => &["zou"], + "鄺" => &["kuang"], + "鄻" => &["lian"], + "鄼" => &["zan"], + "鄽" => &["chan"], + "鄾" => &["you"], + "鄿" => &["qi"], + "酀" => &["yan"], + "酁" => &["chan"], + "酂" => &["cuo"], + "酃" => &["ling"], + "酄" => &["huan"], + "酅" => &["xi"], + "酆" => &["feng"], + "酇" => &["zan"], + "酈" => &["li"], + "酉" => &["you"], + "酊" => &["ding"], + "酋" => &["qiu"], + "酌" => &["zhuo"], + "配" => &["pei"], + "酎" => &["zhou"], + "酏" => &["yi"], + "酐" => &["gan"], + "酑" => &["yu"], + "酒" => &["jiu"], + "酓" => &["yan"], + "酔" => &["zui"], + "酕" => &["mao"], + "酖" => &["dan","zhen"], + "酗" => &["xu"], + "酘" => &["tou"], + "酙" => &["zhen"], + "酚" => &["fen"], + "酝" => &["yun"], + "酞" => &["tai"], + "酟" => &["tian"], + "酠" => &["qia"], + "酡" => &["tuo"], + "酢" => &["zuo","cu"], + "酣" => &["han"], + "酤" => &["gu"], + "酥" => &["su"], + "酦" => &["fa","po"], + "酧" => &["chou"], + "酨" => &["dai"], + "酩" => &["ming"], + "酪" => &["lao","luo"], + "酫" => &["chuo"], + "酬" => &["chou"], + "酭" => &["you"], + "酮" => &["tong"], + "酯" => &["zhi"], + "酰" => &["xian"], + "酱" => &["jiang"], + "酲" => &["cheng"], + "酳" => &["yin"], + "酴" => &["tu"], + "酵" => &["jiao","xiao"], + "酶" => &["mei"], + "酷" => &["ku"], + "酸" => &["suan"], + "酹" => &["lei"], + "酺" => &["pu"], + "酻" => &["zui"], + "酼" => &["hai"], + "酽" => &["yan"], + "酾" => &["shi","shai"], + "酿" => &["niang","nian"], + "醀" => &["wei"], + "醁" => &["lu"], + "醂" => &["lan"], + "醃" => &["yan"], + "醄" => &["tao"], + "醅" => &["pei"], + "醆" => &["zhan"], + "醇" => &["chun"], + "醈" => &["tan"], + "醉" => &["zui"], + "醊" => &["chuo"], + "醋" => &["cu"], + "醌" => &["kun"], + "醍" => &["ti"], + "醎" => &["xian"], + "醏" => &["du"], + "醐" => &["hu"], + "醑" => &["xu"], + "醒" => &["xing"], + "醓" => &["tan"], + "醔" => &["qiu"], + "醕" => &["chun"], + "醖" => &["yun"], + "醗" => &["fa","po"], + "醘" => &["ke"], + "醙" => &["sou"], + "醚" => &["mi"], + "醛" => &["quan"], + "醜" => &["chou"], + "醝" => &["cuo"], + "醞" => &["yun"], + "醟" => &["yong"], + "醠" => &["ang"], + "醡" => &["zha"], + "醢" => &["hai"], + "醣" => &["tang"], + "醤" => &["jiang"], + "醥" => &["piao"], + "醦" => &["lao"], + "醧" => &["yu"], + "醨" => &["li"], + "醩" => &["zao"], + "醪" => &["lao"], + "醫" => &["yi"], + "醬" => &["jiang"], + "醭" => &["bu"], + "醮" => &["jiao"], + "醯" => &["xi"], + "醰" => &["tan"], + "醱" => &["fa","po"], + "醲" => &["nong"], + "醳" => &["yi"], + "醴" => &["li"], + "醵" => &["ju"], + "醶" => &["yan"], + "醷" => &["yi"], + "醸" => &["niang"], + "醹" => &["ru"], + "醺" => &["xun"], + "醻" => &["chou"], + "醼" => &["yan"], + "醽" => &["ling"], + "醾" => &["mi"], + "醿" => &["mi"], + "釀" => &["niang"], + "釁" => &["xin"], + "釂" => &["jiao"], + "釃" => &["shi"], + "釄" => &["mi"], + "釅" => &["yan"], + "釆" => &["bian"], + "采" => &["cai"], + "釈" => &["shi"], + "釉" => &["you"], + "释" => &["shi"], + "釋" => &["shi"], + "里" => &["li"], + "重" => &["zhong","chong"], + "野" => &["ye"], + "量" => &["liang"], + "釐" => &["li","xi"], + "金" => &["jin"], + "釒" => &["jin"], + "釓" => &["ga"], + "釔" => &["yi"], + "釕" => &["liao"], + "釖" => &["dao"], + "釗" => &["zhao"], + "釘" => &["ding"], + "釙" => &["li"], + "釚" => &["qiu"], + "釛" => &["he"], + "釜" => &["fu"], + "針" => &["zhen"], + "釞" => &["zhi"], + "釟" => &["ba"], + "釠" => &["luan"], + "釡" => &["fu"], + "釢" => &["nai"], + "釣" => &["diao"], + "釤" => &["shan"], + "釥" => &["qiao"], + "釦" => &["kou"], + "釧" => &["chuan"], + "釨" => &["zi"], + "釩" => &["fan"], + "釪" => &["yu"], + "釫" => &["hua"], + "釬" => &["han"], + "釭" => &["gong","gang"], + "釮" => &["qi"], + "釯" => &["mang"], + "釰" => &["jian"], + "釱" => &["di"], + "釲" => &["si"], + "釳" => &["xi"], + "釴" => &["yi"], + "釵" => &["chai"], + "釶" => &["ta","tuo"], + "釷" => &["tu"], + "釸" => &["xi"], + "釹" => &["nu:"], + "釺" => &["qian"], + "釼" => &["jian"], + "釽" => &["pi"], + "釾" => &["ye"], + "釿" => &["yin"], + "鈀" => &["ba","pa"], + "鈁" => &["fang"], + "鈂" => &["chen"], + "鈃" => &["jian"], + "鈄" => &["tou"], + "鈅" => &["yue"], + "鈆" => &["yan"], + "鈇" => &["fu"], + "鈈" => &["bu"], + "鈉" => &["na"], + "鈊" => &["xin"], + "鈋" => &["e"], + "鈌" => &["jue"], + "鈍" => &["dun"], + "鈎" => &["gou"], + "鈏" => &["yin"], + "鈐" => &["qian"], + "鈑" => &["ban"], + "鈒" => &["ji"], + "鈓" => &["ren"], + "鈔" => &["chao"], + "鈕" => &["niu"], + "鈖" => &["fen"], + "鈗" => &["yun"], + "鈘" => &["yi"], + "鈙" => &["qin"], + "鈚" => &["pi"], + "鈛" => &["guo"], + "鈜" => &["hong"], + "鈝" => &["yin"], + "鈞" => &["jun"], + "鈟" => &["shi"], + "鈠" => &["yi"], + "鈡" => &["zhong"], + "鈢" => &["nie"], + "鈣" => &["gai"], + "鈤" => &["ri"], + "鈥" => &["huo"], + "鈦" => &["tai"], + "鈧" => &["kang"], + "鈩" => &["lu"], + "鈬" => &["duo"], + "鈭" => &["zi"], + "鈮" => &["ni"], + "鈯" => &["tu"], + "鈰" => &["shi"], + "鈱" => &["min"], + "鈲" => &["gu"], + "鈳" => &["ke"], + "鈴" => &["ling"], + "鈵" => &["bing"], + "鈶" => &["yi"], + "鈷" => &["gu"], + "鈸" => &["ba"], + "鈹" => &["pi"], + "鈺" => &["yu"], + "鈻" => &["si"], + "鈼" => &["zuo"], + "鈽" => &["bu"], + "鈾" => &["you"], + "鈿" => &["dian","tian"], + "鉀" => &["jia"], + "鉁" => &["zhen"], + "鉂" => &["shi"], + "鉃" => &["shi"], + "鉄" => &["tie"], + "鉅" => &["ju"], + "鉆" => &["zhan"], + "鉇" => &["ta","tuo"], + "鉈" => &["she","tuo","ta"], + "鉉" => &["xuan"], + "鉊" => &["zhao"], + "鉋" => &["bao"], + "鉌" => &["he"], + "鉍" => &["bi"], + "鉎" => &["sheng"], + "鉏" => &["chu"], + "鉐" => &["shi"], + "鉑" => &["bo"], + "鉒" => &["zhu"], + "鉓" => &["chi"], + "鉔" => &["za"], + "鉕" => &["po"], + "鉖" => &["tong"], + "鉗" => &["qian"], + "鉘" => &["fu"], + "鉙" => &["zhai"], + "鉚" => &["liu","mao"], + "鉛" => &["qian","yan"], + "鉜" => &["fu"], + "鉝" => &["li"], + "鉞" => &["yue"], + "鉟" => &["pi"], + "鉠" => &["yang"], + "鉡" => &["ban"], + "鉢" => &["bo"], + "鉣" => &["jie"], + "鉤" => &["gou"], + "鉥" => &["shu"], + "鉦" => &["zheng"], + "鉧" => &["mu"], + "鉨" => &["ni"], + "鉩" => &["xi"], + "鉪" => &["di"], + "鉫" => &["jia"], + "鉬" => &["mu"], + "鉭" => &["tan"], + "鉮" => &["shen"], + "鉯" => &["yi"], + "鉰" => &["si"], + "鉱" => &["kuang"], + "鉲" => &["ka"], + "鉳" => &["bei"], + "鉴" => &["jian"], + "鉵" => &["tong"], + "鉶" => &["xing"], + "鉷" => &["hong"], + "鉸" => &["jiao","jia"], + "鉹" => &["chi"], + "鉺" => &["er"], + "鉻" => &["ge"], + "鉼" => &["bing"], + "鉽" => &["shi"], + "鉾" => &["mou"], + "鉿" => &["jia","ha"], + "銀" => &["yin"], + "銁" => &["jun"], + "銂" => &["zhou"], + "銃" => &["chong"], + "銄" => &["shang"], + "銅" => &["tong"], + "銆" => &["mo"], + "銇" => &["lei"], + "銈" => &["ji"], + "銉" => &["yu"], + "銊" => &["xu"], + "銋" => &["ren"], + "銌" => &["cun"], + "銍" => &["zhi"], + "銎" => &["qiong"], + "銏" => &["shan"], + "銐" => &["chi"], + "銑" => &["xian","xi"], + "銒" => &["xing"], + "銓" => &["quan"], + "銔" => &["pi"], + "銕" => &["yi"], + "銖" => &["zhu"], + "銗" => &["hou"], + "銘" => &["ming"], + "銙" => &["kua"], + "銚" => &["yao","diao","tiao"], + "銛" => &["xian"], + "銜" => &["xian"], + "銝" => &["xiu"], + "銞" => &["jun"], + "銟" => &["cha"], + "銠" => &["lao"], + "銡" => &["ji"], + "銢" => &["yong"], + "銣" => &["ru"], + "銤" => &["mi"], + "銥" => &["yi"], + "銦" => &["yin"], + "銧" => &["guang"], + "銨" => &["an"], + "銩" => &["diu"], + "銪" => &["you"], + "銫" => &["se"], + "銬" => &["kao"], + "銭" => &["qian"], + "銮" => &["luan"], + "銰" => &["ai"], + "銱" => &["diao"], + "銲" => &["han"], + "銳" => &["rui"], + "銴" => &["shi"], + "銵" => &["keng"], + "銶" => &["qiu"], + "銷" => &["xiao"], + "銸" => &["zhe"], + "銹" => &["xiu"], + "銺" => &["zang"], + "銻" => &["ti"], + "銼" => &["cuo"], + "銽" => &["gua"], + "銾" => &["gong"], + "銿" => &["zhong"], + "鋀" => &["dou"], + "鋁" => &["lu:"], + "鋂" => &["mei"], + "鋃" => &["lang"], + "鋄" => &["wan"], + "鋅" => &["xin"], + "鋆" => &["yun"], + "鋇" => &["bei"], + "鋈" => &["wu"], + "鋉" => &["su"], + "鋊" => &["yu"], + "鋋" => &["chan"], + "鋌" => &["ting","ding"], + "鋍" => &["bo"], + "鋎" => &["han"], + "鋏" => &["jia"], + "鋐" => &["hong"], + "鋑" => &["cuan"], + "鋒" => &["feng"], + "鋓" => &["chan"], + "鋔" => &["wan"], + "鋕" => &["zhi"], + "鋖" => &["si"], + "鋗" => &["xuan"], + "鋘" => &["wu"], + "鋙" => &["wu"], + "鋚" => &["tiao"], + "鋛" => &["gong"], + "鋜" => &["zhuo"], + "鋝" => &["lu:e"], + "鋞" => &["xing"], + "鋟" => &["qin"], + "鋠" => &["shen"], + "鋡" => &["han"], + "鋣" => &["ye"], + "鋤" => &["chu"], + "鋥" => &["zeng"], + "鋦" => &["ju"], + "鋧" => &["xian"], + "鋨" => &["e"], + "鋩" => &["mang"], + "鋪" => &["pu"], + "鋫" => &["li"], + "鋬" => &["shi"], + "鋭" => &["rui"], + "鋮" => &["cheng"], + "鋯" => &["gao"], + "鋰" => &["li"], + "鋱" => &["te"], + "鋳" => &["zhu"], + "鋵" => &["tu"], + "鋶" => &["liu"], + "鋷" => &["zui"], + "鋸" => &["ju"], + "鋹" => &["chang"], + "鋺" => &["yuan"], + "鋻" => &["jian"], + "鋼" => &["gang"], + "鋽" => &["diao"], + "鋾" => &["tao"], + "鋿" => &["chang"], + "錀" => &["lun"], + "錁" => &["guo"], + "錂" => &["ling"], + "錃" => &["bei"], + "錄" => &["lu"], + "錅" => &["li"], + "錆" => &["qing","qiang"], + "錇" => &["pei"], + "錈" => &["juan"], + "錉" => &["min"], + "錊" => &["zui"], + "錋" => &["peng"], + "錌" => &["an"], + "錍" => &["pi"], + "錎" => &["xian"], + "錏" => &["ya"], + "錐" => &["zhui"], + "錑" => &["lei"], + "錒" => &["a","e"], + "錓" => &["kong"], + "錔" => &["ta"], + "錕" => &["kun"], + "錖" => &["du"], + "錗" => &["wei"], + "錘" => &["chui"], + "錙" => &["zi"], + "錚" => &["zheng"], + "錛" => &["ben"], + "錜" => &["nie"], + "錝" => &["cong"], + "錞" => &["chun"], + "錟" => &["tan"], + "錠" => &["ding"], + "錡" => &["qi"], + "錢" => &["qian"], + "錣" => &["zhuo"], + "錤" => &["qi"], + "錥" => &["yu"], + "錦" => &["jin"], + "錧" => &["guan"], + "錨" => &["mao"], + "錩" => &["chang"], + "錪" => &["dian"], + "錫" => &["xi"], + "錬" => &["lian"], + "錭" => &["tao"], + "錮" => &["gu"], + "錯" => &["cuo","cu"], + "錰" => &["shu"], + "錱" => &["zhen"], + "録" => &["lu"], + "錳" => &["meng"], + "錴" => &["lu"], + "錵" => &["hua"], + "錶" => &["biao"], + "錷" => &["ga"], + "錸" => &["lai"], + "錹" => &["ken"], + "錺" => &["zhui"], + "錼" => &["nai"], + "錽" => &["wan"], + "錾" => &["zan"], + "鍀" => &["de"], + "鍁" => &["xian"], + "鍃" => &["huo"], + "鍄" => &["liang"], + "鍆" => &["men"], + "鍇" => &["kai"], + "鍈" => &["ying"], + "鍉" => &["di"], + "鍊" => &["lian"], + "鍋" => &["guo"], + "鍌" => &["xian"], + "鍍" => &["du"], + "鍎" => &["tu"], + "鍏" => &["wei"], + "鍐" => &["cong"], + "鍑" => &["fu"], + "鍒" => &["rou"], + "鍓" => &["ji"], + "鍔" => &["e"], + "鍕" => &["rou"], + "鍖" => &["chen"], + "鍗" => &["ti"], + "鍘" => &["zha"], + "鍙" => &["hong"], + "鍚" => &["yang"], + "鍛" => &["duan"], + "鍜" => &["xia"], + "鍝" => &["yu"], + "鍞" => &["keng"], + "鍟" => &["xing"], + "鍠" => &["huang"], + "鍡" => &["wei"], + "鍢" => &["fu"], + "鍣" => &["zhao"], + "鍤" => &["cha"], + "鍥" => &["qie"], + "鍦" => &["she"], + "鍧" => &["hong"], + "鍨" => &["kui"], + "鍩" => &["nuo"], + "鍪" => &["mou"], + "鍫" => &["qiao"], + "鍬" => &["qiao"], + "鍭" => &["hou"], + "鍮" => &["zhen"], + "鍯" => &["huo"], + "鍰" => &["huan"], + "鍱" => &["ye"], + "鍲" => &["min"], + "鍳" => &["jian"], + "鍴" => &["duan"], + "鍵" => &["jian"], + "鍶" => &["si"], + "鍷" => &["kui"], + "鍸" => &["hu"], + "鍹" => &["xuan"], + "鍺" => &["zang","zhe"], + "鍻" => &["jie"], + "鍼" => &["zhen"], + "鍽" => &["bian"], + "鍾" => &["zhong"], + "鍿" => &["zi"], + "鎀" => &["xiu"], + "鎁" => &["ye"], + "鎂" => &["mei"], + "鎃" => &["pai"], + "鎄" => &["ai"], + "鎅" => &["jie"], + "鎇" => &["mei"], + "鎈" => &["cha"], + "鎉" => &["ta"], + "鎊" => &["bang"], + "鎋" => &["xia"], + "鎌" => &["lian"], + "鎍" => &["suo"], + "鎎" => &["xi"], + "鎏" => &["liu"], + "鎐" => &["zu"], + "鎑" => &["ye"], + "鎒" => &["nou"], + "鎓" => &["weng"], + "鎔" => &["rong"], + "鎕" => &["tang"], + "鎖" => &["suo"], + "鎗" => &["qiang"], + "鎘" => &["ge"], + "鎙" => &["shuo"], + "鎚" => &["chui"], + "鎛" => &["bo"], + "鎜" => &["pan"], + "鎝" => &["ta"], + "鎞" => &["bi"], + "鎟" => &["sang"], + "鎠" => &["gang"], + "鎡" => &["zi"], + "鎢" => &["wu"], + "鎣" => &["ying"], + "鎤" => &["huang"], + "鎥" => &["tiao"], + "鎦" => &["liu"], + "鎧" => &["kai"], + "鎨" => &["sun"], + "鎩" => &["sha"], + "鎪" => &["sou"], + "鎫" => &["wan"], + "鎬" => &["hao","gao"], + "鎭" => &["zhen"], + "鎮" => &["zhen"], + "鎯" => &["luo"], + "鎰" => &["yi"], + "鎱" => &["yuan"], + "鎲" => &["tang"], + "鎳" => &["nie"], + "鎴" => &["xi"], + "鎵" => &["jia"], + "鎶" => &["ge"], + "鎷" => &["ma"], + "鎸" => &["juan"], + "鎹" => &["rong"], + "鎻" => &["suo"], + "鎿" => &["na"], + "鏀" => &["lu"], + "鏁" => &["suo"], + "鏂" => &["kou"], + "鏃" => &["zu","cu"], + "鏄" => &["tuan"], + "鏅" => &["xiu"], + "鏆" => &["guan"], + "鏇" => &["xuan"], + "鏈" => &["lian"], + "鏉" => &["shou"], + "鏊" => &["ao"], + "鏋" => &["man"], + "鏌" => &["mo"], + "鏍" => &["luo"], + "鏎" => &["bi"], + "鏏" => &["wei"], + "鏐" => &["liu"], + "鏑" => &["di"], + "鏒" => &["qiao"], + "鏓" => &["huo"], + "鏔" => &["yin"], + "鏕" => &["lu"], + "鏖" => &["ao"], + "鏗" => &["keng"], + "鏘" => &["qiang"], + "鏙" => &["cui"], + "鏚" => &["qi"], + "鏛" => &["chang"], + "鏜" => &["tang"], + "鏝" => &["man"], + "鏞" => &["yong"], + "鏟" => &["chan"], + "鏠" => &["feng"], + "鏡" => &["jing"], + "鏢" => &["biao"], + "鏣" => &["shu"], + "鏤" => &["lou"], + "鏥" => &["xiu"], + "鏦" => &["cong"], + "鏧" => &["long"], + "鏨" => &["zan"], + "鏩" => &["jian"], + "鏪" => &["cao"], + "鏫" => &["li"], + "鏬" => &["xia"], + "鏭" => &["xi"], + "鏮" => &["kang"], + "鏰" => &["beng"], + "鏳" => &["zheng"], + "鏴" => &["lu"], + "鏵" => &["hua"], + "鏶" => &["ji"], + "鏷" => &["pu"], + "鏸" => &["hui"], + "鏹" => &["qiang"], + "鏺" => &["po"], + "鏻" => &["lin"], + "鏼" => &["suo"], + "鏽" => &["xiu"], + "鏾" => &["san"], + "鏿" => &["cheng"], + "鐀" => &["kui"], + "鐁" => &["san"], + "鐂" => &["liu"], + "鐃" => &["nao"], + "鐄" => &["huang"], + "鐅" => &["pie"], + "鐆" => &["sui"], + "鐇" => &["fan"], + "鐈" => &["qiao"], + "鐉" => &["chuan"], + "鐊" => &["yang"], + "鐋" => &["tang"], + "鐌" => &["xiang"], + "鐍" => &["jue"], + "鐎" => &["jiao"], + "鐏" => &["zun"], + "鐐" => &["liao"], + "鐑" => &["jie"], + "鐒" => &["lao"], + "鐓" => &["dui","dun"], + "鐔" => &["tan","chan","xin"], + "鐕" => &["zan"], + "鐖" => &["ji"], + "鐗" => &["jian"], + "鐘" => &["zhong"], + "鐙" => &["deng"], + "鐚" => &["lou","lue"], + "鐛" => &["ying"], + "鐜" => &["dui"], + "鐝" => &["jue"], + "鐞" => &["nou"], + "鐟" => &["ti"], + "鐠" => &["pu"], + "鐡" => &["tie"], + "鐤" => &["ding"], + "鐥" => &["shan"], + "鐦" => &["kai"], + "鐧" => &["jian"], + "鐨" => &["fei"], + "鐩" => &["sui"], + "鐪" => &["lu"], + "鐫" => &["juan"], + "鐬" => &["hui"], + "鐭" => &["yu"], + "鐮" => &["lian"], + "鐯" => &["zhuo"], + "鐰" => &["qiao"], + "鐱" => &["qian"], + "鐲" => &["zhuo"], + "鐳" => &["lei"], + "鐴" => &["bi"], + "鐵" => &["tie"], + "鐶" => &["huan"], + "鐷" => &["ye"], + "鐸" => &["duo"], + "鐹" => &["guo"], + "鐺" => &["dang","cheng"], + "鐻" => &["ju"], + "鐼" => &["fen"], + "鐽" => &["da"], + "鐾" => &["bei"], + "鐿" => &["yi"], + "鑀" => &["ai"], + "鑁" => &["dang","zheng"], + "鑂" => &["xun"], + "鑃" => &["diao","yao"], + "鑄" => &["zhu"], + "鑅" => &["heng"], + "鑆" => &["zhui"], + "鑇" => &["ji"], + "鑈" => &["nie"], + "鑉" => &["ta"], + "鑊" => &["huo"], + "鑋" => &["qing"], + "鑌" => &["bin"], + "鑍" => &["ying"], + "鑎" => &["kui"], + "鑏" => &["ning"], + "鑐" => &["xu"], + "鑑" => &["jian"], + "鑒" => &["jian"], + "鑓" => &["qiang"], + "鑔" => &["cha"], + "鑕" => &["zhi"], + "鑖" => &["mie"], + "鑗" => &["li"], + "鑘" => &["lei"], + "鑙" => &["ji"], + "鑚" => &["zuan"], + "鑛" => &["kuang"], + "鑜" => &["shang"], + "鑝" => &["peng"], + "鑞" => &["la"], + "鑟" => &["du"], + "鑠" => &["shuo"], + "鑡" => &["chuo"], + "鑢" => &["lu:"], + "鑣" => &["biao"], + "鑤" => &["bao"], + "鑥" => &["lu"], + "鑨" => &["long"], + "鑩" => &["e"], + "鑪" => &["lu"], + "鑫" => &["xin"], + "鑬" => &["jian"], + "鑭" => &["lan"], + "鑮" => &["bo"], + "鑯" => &["jian"], + "鑰" => &["yao","yue"], + "鑱" => &["chan"], + "鑲" => &["xiang"], + "鑳" => &["jian"], + "鑴" => &["xi"], + "鑵" => &["guan"], + "鑶" => &["cang"], + "鑷" => &["nie"], + "鑸" => &["lei"], + "鑹" => &["cuan"], + "鑺" => &["qu"], + "鑻" => &["pan"], + "鑼" => &["luo"], + "鑽" => &["zuan"], + "鑾" => &["luan"], + "鑿" => &["zao","zuo"], + "钀" => &["nie"], + "钁" => &["jue"], + "钂" => &["tang"], + "钃" => &["shu"], + "钄" => &["lan"], + "钅" => &["jin"], + "钆" => &["ga"], + "钇" => &["yi"], + "针" => &["zhen"], + "钉" => &["ding"], + "钊" => &["zhao"], + "钋" => &["po"], + "钌" => &["liao"], + "钍" => &["tu"], + "钎" => &["qian"], + "钏" => &["chuan"], + "钐" => &["shan"], + "钑" => &["sa"], + "钒" => &["fan"], + "钓" => &["diao"], + "钔" => &["men"], + "钕" => &["nu:"], + "钖" => &["yang"], + "钗" => &["chai"], + "钘" => &["xing"], + "钙" => &["gai"], + "钚" => &["bu"], + "钛" => &["tai"], + "钜" => &["ju"], + "钝" => &["dun"], + "钞" => &["chao"], + "钟" => &["zhong"], + "钠" => &["na"], + "钡" => &["bei"], + "钢" => &["gang"], + "钣" => &["ban"], + "钤" => &["qian"], + "钥" => &["yao","yue"], + "钦" => &["qin"], + "钧" => &["jun"], + "钨" => &["wu"], + "钩" => &["gou"], + "钪" => &["kang"], + "钫" => &["fang"], + "钬" => &["huo"], + "钭" => &["tou"], + "钮" => &["niu"], + "钯" => &["ba","pa"], + "钰" => &["yu"], + "钱" => &["qian"], + "钲" => &["zheng"], + "钳" => &["qian"], + "钴" => &["gu"], + "钵" => &["bo"], + "钶" => &["ke"], + "钷" => &["po"], + "钸" => &["bu"], + "钹" => &["bo"], + "钺" => &["yue"], + "钻" => &["zuan"], + "钼" => &["mu"], + "钽" => &["tan"], + "钾" => &["jia"], + "钿" => &["dian","tian"], + "铀" => &["you"], + "铁" => &["tie"], + "铂" => &["bo"], + "铃" => &["ling"], + "铄" => &["shuo"], + "铅" => &["qian","yan"], + "铆" => &["mao"], + "铇" => &["bao"], + "铈" => &["shi"], + "铉" => &["xuan"], + "铊" => &["tuo","she","ta"], + "铋" => &["bi"], + "铌" => &["ni"], + "铍" => &["pi"], + "铎" => &["duo"], + "铏" => &["xing"], + "铐" => &["kao"], + "铑" => &["lao"], + "铒" => &["er"], + "铓" => &["mang"], + "铔" => &["ya"], + "铕" => &["you"], + "铖" => &["cheng"], + "铗" => &["jia"], + "铘" => &["ye"], + "铙" => &["nao"], + "铚" => &["zhi"], + "铛" => &["dang","cheng"], + "铜" => &["tong"], + "铝" => &["lu:"], + "铞" => &["diao"], + "铟" => &["yin"], + "铠" => &["kai"], + "铡" => &["zha"], + "铢" => &["zhu"], + "铣" => &["xian","xi"], + "铤" => &["ting","ding"], + "铥" => &["diu"], + "铦" => &["xian"], + "铧" => &["hua"], + "铨" => &["quan"], + "铩" => &["sha"], + "铪" => &["ha"], + "铫" => &["yao","diao","tiao"], + "铬" => &["ge"], + "铭" => &["ming"], + "铮" => &["zheng"], + "铯" => &["se"], + "铰" => &["jiao","jia"], + "铱" => &["yi"], + "铲" => &["chan"], + "铳" => &["chong"], + "铴" => &["tang"], + "铵" => &["an"], + "银" => &["yin"], + "铷" => &["ru"], + "铸" => &["zhu"], + "铹" => &["lao"], + "铺" => &["pu"], + "铻" => &["wu"], + "铼" => &["lai"], + "铽" => &["te"], + "链" => &["lian"], + "铿" => &["keng"], + "销" => &["xiao"], + "锁" => &["suo"], + "锂" => &["li"], + "锃" => &["zeng"], + "锄" => &["chu"], + "锅" => &["guo"], + "锆" => &["gao"], + "锇" => &["e"], + "锈" => &["xiu"], + "锉" => &["cuo"], + "锊" => &["lu:e"], + "锋" => &["feng"], + "锌" => &["xin"], + "锍" => &["liu"], + "锎" => &["kai"], + "锏" => &["jian"], + "锐" => &["rui"], + "锑" => &["ti"], + "锒" => &["lang"], + "锓" => &["qin"], + "锔" => &["ju"], + "锕" => &["a"], + "锖" => &["qing","qiang"], + "锗" => &["zhe","zang"], + "锘" => &["nuo"], + "错" => &["cuo"], + "锚" => &["mao"], + "锛" => &["ben"], + "锜" => &["qi"], + "锝" => &["de"], + "锞" => &["ke"], + "锟" => &["kun"], + "锠" => &["chang"], + "锡" => &["xi"], + "锢" => &["gu"], + "锣" => &["luo"], + "锤" => &["chui"], + "锥" => &["zhui"], + "锦" => &["jin"], + "锧" => &["zhi"], + "锨" => &["xian"], + "锩" => &["juan"], + "锪" => &["huo"], + "锫" => &["pei"], + "锬" => &["tan"], + "锭" => &["ding"], + "键" => &["jian"], + "锯" => &["ju"], + "锰" => &["meng"], + "锱" => &["zi"], + "锲" => &["qie"], + "锳" => &["ying"], + "锴" => &["kai"], + "锵" => &["qiang"], + "锶" => &["si"], + "锷" => &["e"], + "锸" => &["cha"], + "锹" => &["qiao"], + "锺" => &["zhong"], + "锻" => &["duan"], + "锼" => &["sou"], + "锽" => &["huang"], + "锾" => &["huan"], + "锿" => &["ai"], + "镀" => &["du"], + "镁" => &["mei"], + "镂" => &["lou"], + "镃" => &["zi"], + "镄" => &["fei"], + "镅" => &["mei"], + "镆" => &["mo"], + "镇" => &["zhen"], + "镈" => &["bo"], + "镉" => &["ge"], + "镊" => &["nie"], + "镋" => &["tang"], + "镌" => &["juan"], + "镍" => &["nie"], + "镎" => &["na"], + "镏" => &["liu"], + "镐" => &["hao","gao"], + "镑" => &["bang"], + "镒" => &["yi"], + "镓" => &["jia"], + "镔" => &["bin"], + "镕" => &["rong"], + "镖" => &["biao"], + "镗" => &["tang"], + "镘" => &["man"], + "镙" => &["luo"], + "镚" => &["beng"], + "镛" => &["yong"], + "镜" => &["jing"], + "镝" => &["di"], + "镞" => &["zu"], + "镟" => &["xuan"], + "镠" => &["liu"], + "镡" => &["chan","xin","tan"], + "镢" => &["jue"], + "镣" => &["liao"], + "镤" => &["pu"], + "镥" => &["lu"], + "镦" => &["dun","dui"], + "镧" => &["lan"], + "镨" => &["pu"], + "镩" => &["cuan"], + "镪" => &["qiang"], + "镫" => &["deng"], + "镬" => &["huo"], + "镭" => &["lei"], + "镮" => &["huan"], + "镯" => &["zhuo"], + "镰" => &["lian"], + "镱" => &["yi"], + "镲" => &["cha"], + "镳" => &["biao"], + "镴" => &["la"], + "镵" => &["chan"], + "镶" => &["xiang"], + "長" => &["chang","zhang"], + "镸" => &["chang","zhang"], + "镹" => &["jiu"], + "镺" => &["ao"], + "镻" => &["die"], + "镼" => &["qu"], + "镽" => &["liao"], + "镾" => &["mi"], + "长" => &["chang","zhang"], + "門" => &["men"], + "閁" => &["ma"], + "閂" => &["shuan"], + "閃" => &["shan"], + "閄" => &["huo"], + "閅" => &["men"], + "閆" => &["yan"], + "閇" => &["bi"], + "閈" => &["han"], + "閉" => &["bi"], + "開" => &["kai"], + "閌" => &["kang"], + "閍" => &["beng"], + "閎" => &["hong"], + "閏" => &["run"], + "閐" => &["san"], + "閑" => &["xian"], + "閒" => &["xian"], + "間" => &["jian"], + "閔" => &["min"], + "閕" => &["xia"], + "閖" => &["min"], + "閗" => &["dou"], + "閘" => &["zha"], + "閙" => &["nao"], + "閛" => &["peng"], + "閜" => &["ke"], + "閝" => &["ling"], + "閞" => &["bian"], + "閟" => &["bi"], + "閠" => &["run"], + "閡" => &["he"], + "関" => &["guan"], + "閣" => &["ge"], + "閤" => &["he","ge"], + "閥" => &["fa"], + "閦" => &["chu"], + "閧" => &["hong"], + "閨" => &["gui"], + "閩" => &["min"], + "閫" => &["kun"], + "閬" => &["lang"], + "閭" => &["lu:"], + "閮" => &["ting"], + "閯" => &["sha"], + "閰" => &["yan"], + "閱" => &["yue"], + "閲" => &["yue"], + "閳" => &["chan"], + "閴" => &["qu"], + "閵" => &["lin"], + "閶" => &["chang"], + "閷" => &["shai"], + "閸" => &["kun"], + "閹" => &["yan"], + "閺" => &["min","wen"], + "閻" => &["yan"], + "閼" => &["e","yan"], + "閽" => &["hun"], + "閾" => &["yu"], + "閿" => &["wen"], + "闀" => &["xiang"], + "闂" => &["xiang"], + "闃" => &["qu"], + "闄" => &["yao"], + "闅" => &["wen"], + "闆" => &["ban"], + "闇" => &["an"], + "闈" => &["wei"], + "闉" => &["yin"], + "闊" => &["kuo"], + "闋" => &["que"], + "闌" => &["lan"], + "闍" => &["du","she"], + "闐" => &["tian"], + "闑" => &["nie"], + "闒" => &["da","ta"], + "闓" => &["kai"], + "闔" => &["he"], + "闕" => &["que"], + "闖" => &["chuang"], + "闗" => &["guan"], + "闘" => &["dou"], + "闙" => &["qi"], + "闚" => &["kui"], + "闛" => &["tang"], + "關" => &["guan"], + "闝" => &["piao"], + "闞" => &["kan","han"], + "闟" => &["xi"], + "闠" => &["hui"], + "闡" => &["chan"], + "闢" => &["pi","bi"], + "闣" => &["dang"], + "闤" => &["huan"], + "闥" => &["ta"], + "闦" => &["wen"], + "门" => &["men"], + "闩" => &["shuan"], + "闪" => &["shan"], + "闫" => &["yan"], + "闬" => &["han"], + "闭" => &["bi"], + "问" => &["wen"], + "闯" => &["chuang"], + "闰" => &["run"], + "闱" => &["wei"], + "闲" => &["xian"], + "闳" => &["hong"], + "间" => &["jian"], + "闵" => &["min"], + "闶" => &["kang"], + "闷" => &["men"], + "闸" => &["zha"], + "闹" => &["nao"], + "闺" => &["gui"], + "闻" => &["wen"], + "闼" => &["ta"], + "闽" => &["min"], + "闾" => &["lu:"], + "闿" => &["kai"], + "阀" => &["fa"], + "阁" => &["ge"], + "阂" => &["he"], + "阃" => &["kun"], + "阄" => &["jiu"], + "阅" => &["yue"], + "阆" => &["lang"], + "阇" => &["du","she"], + "阈" => &["yu"], + "阉" => &["yan"], + "阊" => &["chang"], + "阋" => &["xi"], + "阌" => &["wen"], + "阍" => &["hun"], + "阎" => &["yan"], + "阏" => &["yan","e"], + "阐" => &["chan"], + "阑" => &["lan"], + "阒" => &["qu"], + "阓" => &["hui"], + "阔" => &["kuo"], + "阕" => &["que"], + "阖" => &["he"], + "阗" => &["tian"], + "阘" => &["da","ta"], + "阙" => &["que"], + "阚" => &["kan","han"], + "阛" => &["huan"], + "阜" => &["fu"], + "阝" => &["fu","yi"], + "阞" => &["le"], + "队" => &["dui"], + "阠" => &["xin","shen"], + "阡" => &["qian"], + "阢" => &["wu"], + "阣" => &["yi"], + "阤" => &["tuo"], + "阥" => &["yin"], + "阦" => &["yang"], + "阧" => &["dou"], + "阨" => &["e"], + "阩" => &["sheng"], + "阪" => &["ban"], + "阫" => &["pei"], + "阬" => &["keng"], + "阭" => &["yun"], + "阮" => &["ruan"], + "阯" => &["zhi"], + "阰" => &["pi"], + "阱" => &["jing"], + "防" => &["fang"], + "阳" => &["yang"], + "阴" => &["yin"], + "阵" => &["zhen"], + "阶" => &["jie"], + "阷" => &["cheng"], + "阸" => &["e"], + "阹" => &["qu"], + "阺" => &["di"], + "阻" => &["zu"], + "阼" => &["zuo"], + "阽" => &["dian","yan"], + "阾" => &["ling"], + "阿" => &["a","e"], + "陀" => &["tuo"], + "陁" => &["tuo"], + "陂" => &["po","bei","pi"], + "陃" => &["bing"], + "附" => &["fu"], + "际" => &["ji"], + "陆" => &["lu","liu"], + "陇" => &["long"], + "陈" => &["chen"], + "陉" => &["xing"], + "陊" => &["duo"], + "陋" => &["lou"], + "陌" => &["mo"], + "降" => &["jiang","xiang"], + "陎" => &["shu"], + "陏" => &["duo"], + "限" => &["xian"], + "陑" => &["er"], + "陒" => &["gui"], + "陓" => &["wu"], + "陔" => &["gai"], + "陕" => &["shan"], + "陖" => &["jun"], + "陗" => &["qiao"], + "陘" => &["xing"], + "陙" => &["chun"], + "陚" => &["fu"], + "陛" => &["bi"], + "陜" => &["shan"], + "陝" => &["shan","xia"], + "陞" => &["sheng"], + "陟" => &["zhi"], + "陠" => &["pu"], + "陡" => &["dou"], + "院" => &["yuan"], + "陣" => &["zhen"], + "除" => &["chu"], + "陥" => &["xian"], + "陦" => &["zhi"], + "陧" => &["nie"], + "陨" => &["yun"], + "险" => &["xian"], + "陪" => &["pei"], + "陫" => &["pei"], + "陬" => &["zou"], + "陭" => &["yi"], + "陮" => &["dui"], + "陯" => &["lun"], + "陰" => &["yin"], + "陱" => &["ju"], + "陲" => &["chui"], + "陳" => &["chen"], + "陴" => &["pi"], + "陵" => &["ling"], + "陶" => &["tao","yao"], + "陷" => &["xian"], + "陸" => &["lu","liu"], + "険" => &["xian"], + "陻" => &["yin"], + "陼" => &["zhu"], + "陽" => &["yang"], + "陾" => &["reng"], + "陿" => &["shan"], + "隀" => &["chong"], + "隁" => &["yan"], + "隂" => &["yin"], + "隃" => &["yu"], + "隄" => &["ti"], + "隅" => &["yu"], + "隆" => &["long"], + "隇" => &["wei"], + "隈" => &["wei"], + "隉" => &["nie"], + "隊" => &["dui"], + "隋" => &["sui"], + "隌" => &["an"], + "隍" => &["huang"], + "階" => &["jie"], + "随" => &["sui"], + "隐" => &["yin"], + "隑" => &["gai"], + "隒" => &["yan"], + "隓" => &["hui"], + "隔" => &["ge"], + "隕" => &["yun"], + "隖" => &["wu"], + "隗" => &["wei","kui"], + "隘" => &["ai"], + "隙" => &["xi"], + "隚" => &["tang"], + "際" => &["ji"], + "障" => &["zhang"], + "隝" => &["dao"], + "隞" => &["ao"], + "隟" => &["xi"], + "隠" => &["yin"], + "隡" => &["sa"], + "隢" => &["rao"], + "隣" => &["lin"], + "隤" => &["tui"], + "隥" => &["deng"], + "隦" => &["pi"], + "隧" => &["sui"], + "隨" => &["sui"], + "隩" => &["yu"], + "險" => &["xian"], + "隫" => &["fen"], + "隬" => &["ni"], + "隭" => &["er"], + "隮" => &["ji"], + "隯" => &["dao"], + "隰" => &["xi"], + "隱" => &["yin"], + "隲" => &["zhi"], + "隳" => &["hui"], + "隴" => &["long"], + "隵" => &["xi"], + "隶" => &["li"], + "隷" => &["li"], + "隸" => &["li"], + "隹" => &["zhui","cui"], + "隺" => &["he"], + "隻" => &["zhi"], + "隼" => &["sun","zhun"], + "隽" => &["juan","jun"], + "难" => &["nan"], + "隿" => &["yi"], + "雀" => &["que","qiao"], + "雁" => &["yan"], + "雂" => &["qin"], + "雃" => &["ya"], + "雄" => &["xiong"], + "雅" => &["ya"], + "集" => &["ji"], + "雇" => &["gu"], + "雈" => &["huan"], + "雉" => &["zhi"], + "雊" => &["gou"], + "雋" => &["jun","juan"], + "雌" => &["ci"], + "雍" => &["yong"], + "雎" => &["ju"], + "雏" => &["chu"], + "雐" => &["hu"], + "雑" => &["za"], + "雒" => &["luo"], + "雓" => &["yu"], + "雔" => &["chou"], + "雕" => &["diao"], + "雖" => &["sui"], + "雗" => &["han"], + "雘" => &["huo"], + "雙" => &["shuang"], + "雚" => &["guan"], + "雛" => &["chu"], + "雜" => &["za"], + "雝" => &["yong"], + "雞" => &["ji"], + "雟" => &["sui"], + "雠" => &["chou"], + "雡" => &["liu"], + "離" => &["li"], + "難" => &["nan"], + "雤" => &["xue"], + "雥" => &["za"], + "雦" => &["ji"], + "雧" => &["ji"], + "雨" => &["yu"], + "雩" => &["yu"], + "雪" => &["xue"], + "雫" => &["na"], + "雬" => &["fou"], + "雭" => &["se"], + "雮" => &["mu"], + "雯" => &["wen"], + "雰" => &["fen"], + "雱" => &["pang"], + "雲" => &["yun"], + "雳" => &["li"], + "雴" => &["li"], + "雵" => &["yang"], + "零" => &["ling"], + "雷" => &["lei"], + "雸" => &["an"], + "雹" => &["bao"], + "雺" => &["meng"], + "電" => &["dian"], + "雼" => &["dang"], + "雽" => &["hang","yu"], + "雾" => &["wu"], + "雿" => &["zhao"], + "需" => &["xu"], + "霁" => &["ji"], + "霂" => &["mu"], + "霃" => &["chen"], + "霄" => &["xiao"], + "霅" => &["zha"], + "霆" => &["ting"], + "震" => &["zhen"], + "霈" => &["pei"], + "霉" => &["mei"], + "霊" => &["ling"], + "霋" => &["qi"], + "霌" => &["chou"], + "霍" => &["huo"], + "霎" => &["sha"], + "霏" => &["fei"], + "霐" => &["weng"], + "霑" => &["zhan"], + "霒" => &["ying"], + "霓" => &["ni"], + "霔" => &["chou"], + "霕" => &["tun"], + "霖" => &["lin"], + "霘" => &["dong"], + "霙" => &["ying","ji"], + "霚" => &["wu"], + "霛" => &["ling"], + "霜" => &["shuang"], + "霝" => &["ling"], + "霞" => &["xia"], + "霟" => &["hong"], + "霠" => &["yin"], + "霡" => &["mai"], + "霢" => &["mo"], + "霣" => &["yun"], + "霤" => &["liu"], + "霥" => &["meng"], + "霦" => &["bin"], + "霧" => &["wu"], + "霨" => &["wei"], + "霩" => &["kuo"], + "霪" => &["yin"], + "霫" => &["xi"], + "霬" => &["yi"], + "霭" => &["ai"], + "霮" => &["dan"], + "霯" => &["deng"], + "霰" => &["xian","san"], + "霱" => &["yu"], + "露" => &["lu","lou"], + "霳" => &["long"], + "霴" => &["dai"], + "霵" => &["ji"], + "霶" => &["pang"], + "霷" => &["yang"], + "霸" => &["ba"], + "霹" => &["pi"], + "霺" => &["wei"], + "霼" => &["xi"], + "霽" => &["ji"], + "霾" => &["mai"], + "霿" => &["meng"], + "靀" => &["meng"], + "靁" => &["lei"], + "靂" => &["li"], + "靃" => &["huo","sui"], + "靄" => &["ai"], + "靅" => &["fei"], + "靆" => &["dai"], + "靇" => &["long"], + "靈" => &["ling"], + "靉" => &["ai"], + "靊" => &["feng"], + "靋" => &["li"], + "靌" => &["bao"], + "靎" => &["he"], + "靏" => &["he"], + "靐" => &["bing"], + "靑" => &["qing"], + "青" => &["qing"], + "靓" => &["jing","liang"], + "靔" => &["qi"], + "靕" => &["zhen"], + "靖" => &["jing"], + "靗" => &["cheng"], + "靘" => &["qing"], + "静" => &["jing"], + "靚" => &["jing","liang"], + "靛" => &["dian"], + "靜" => &["jing"], + "靝" => &["tian"], + "非" => &["fei"], + "靟" => &["fei"], + "靠" => &["kao"], + "靡" => &["mi"], + "面" => &["mian"], + "靣" => &["mian"], + "靤" => &["pao"], + "靥" => &["ye"], + "靦" => &["tian","mian"], + "靧" => &["hui"], + "靨" => &["ye"], + "革" => &["ge","ji"], + "靪" => &["ding"], + "靫" => &["ren"], + "靬" => &["jian"], + "靭" => &["ren"], + "靮" => &["di"], + "靯" => &["du"], + "靰" => &["wu"], + "靱" => &["ren"], + "靲" => &["qin"], + "靳" => &["jin"], + "靴" => &["xue"], + "靵" => &["niu"], + "靶" => &["ba"], + "靷" => &["yin"], + "靸" => &["sa"], + "靹" => &["ren"], + "靺" => &["mo"], + "靻" => &["zu"], + "靼" => &["da"], + "靽" => &["ban"], + "靾" => &["yi"], + "靿" => &["yao"], + "鞀" => &["tao"], + "鞁" => &["bei","tuo"], + "鞂" => &["jia"], + "鞃" => &["hong"], + "鞄" => &["pao"], + "鞅" => &["yang"], + "鞆" => &["mo"], + "鞇" => &["yin"], + "鞈" => &["jia"], + "鞉" => &["tao"], + "鞊" => &["ji"], + "鞋" => &["xie"], + "鞌" => &["an"], + "鞍" => &["an"], + "鞎" => &["hen"], + "鞏" => &["gong"], + "鞐" => &["gong"], + "鞑" => &["da"], + "鞒" => &["qiao"], + "鞓" => &["ting"], + "鞔" => &["man","wan"], + "鞕" => &["ying"], + "鞖" => &["sui"], + "鞗" => &["tiao"], + "鞘" => &["qiao","shao"], + "鞙" => &["xuan"], + "鞚" => &["kong"], + "鞛" => &["beng"], + "鞜" => &["ta"], + "鞝" => &["zhang"], + "鞞" => &["bing"], + "鞟" => &["kuo"], + "鞠" => &["ju"], + "鞡" => &["la"], + "鞢" => &["xie"], + "鞣" => &["rou"], + "鞤" => &["bang"], + "鞥" => &["yi","eng"], + "鞦" => &["qiu"], + "鞧" => &["qiu"], + "鞨" => &["he"], + "鞩" => &["xiao"], + "鞪" => &["mu"], + "鞫" => &["ju"], + "鞬" => &["jian"], + "鞭" => &["bian"], + "鞮" => &["di"], + "鞯" => &["jian"], + "鞱" => &["tao"], + "鞲" => &["gou"], + "鞳" => &["ta"], + "鞴" => &["bei"], + "鞵" => &["xie"], + "鞶" => &["pan"], + "鞷" => &["ge"], + "鞸" => &["bi"], + "鞹" => &["kuo","kui"], + "鞺" => &["tang"], + "鞻" => &["lou"], + "鞼" => &["gui"], + "鞽" => &["qiao"], + "鞾" => &["xue"], + "鞿" => &["ji"], + "韀" => &["jian"], + "韁" => &["jiang"], + "韂" => &["chan"], + "韃" => &["da"], + "韄" => &["huo"], + "韅" => &["xian"], + "韆" => &["qian"], + "韇" => &["du"], + "韈" => &["wa"], + "韉" => &["jian"], + "韊" => &["lan"], + "韋" => &["wei"], + "韌" => &["ren"], + "韍" => &["fu"], + "韎" => &["mei"], + "韏" => &["juan"], + "韐" => &["ge"], + "韑" => &["wei"], + "韒" => &["qiao"], + "韓" => &["han"], + "韔" => &["chang"], + "韖" => &["rou"], + "韗" => &["xun"], + "韘" => &["she"], + "韙" => &["wei"], + "韚" => &["ge"], + "韛" => &["bei"], + "韜" => &["tao"], + "韝" => &["gou"], + "韞" => &["yun"], + "韟" => &["gao"], + "韠" => &["bi"], + "韡" => &["wei"], + "韢" => &["hui"], + "韣" => &["shu"], + "韤" => &["wa"], + "韥" => &["du"], + "韦" => &["wei"], + "韧" => &["ren"], + "韨" => &["fu"], + "韩" => &["han"], + "韪" => &["wei"], + "韫" => &["yun"], + "韬" => &["tao"], + "韭" => &["jiu"], + "韮" => &["jiu"], + "韯" => &["xian"], + "韰" => &["xie"], + "韱" => &["xian"], + "韲" => &["ji"], + "音" => &["yin"], + "韴" => &["za"], + "韵" => &["yun"], + "韶" => &["shao"], + "韷" => &["luo"], + "韸" => &["peng"], + "韹" => &["huang"], + "韺" => &["ying"], + "韻" => &["yun"], + "韼" => &["peng"], + "韽" => &["yin","an"], + "韾" => &["yin"], + "響" => &["xiang"], + "頀" => &["hu"], + "頁" => &["ye"], + "頂" => &["ding"], + "頃" => &["qing"], + "頄" => &["pan"], + "項" => &["xiang"], + "順" => &["shun"], + "頇" => &["han"], + "須" => &["xu"], + "頉" => &["yi"], + "頊" => &["xu"], + "頋" => &["gu"], + "頌" => &["song"], + "頍" => &["kui"], + "頎" => &["qi"], + "頏" => &["hang"], + "預" => &["yu"], + "頑" => &["wan"], + "頒" => &["ban"], + "頓" => &["dun","du"], + "頔" => &["di"], + "頕" => &["dan"], + "頖" => &["pan"], + "頗" => &["po"], + "領" => &["ling"], + "頙" => &["cheng"], + "頚" => &["jing","geng"], + "頛" => &["lei"], + "頜" => &["he","han"], + "頝" => &["qiao"], + "頞" => &["e"], + "頟" => &["e"], + "頠" => &["wei"], + "頡" => &["jie","xie"], + "頢" => &["gua"], + "頣" => &["shen"], + "頤" => &["yi"], + "頥" => &["yi"], + "頦" => &["ke"], + "頧" => &["dui"], + "頨" => &["pian"], + "頩" => &["ping"], + "頪" => &["lei"], + "頫" => &["fu"], + "頬" => &["jia"], + "頭" => &["tou"], + "頮" => &["hui"], + "頯" => &["kui"], + "頰" => &["jia"], + "頱" => &["le"], + "頲" => &["ting"], + "頳" => &["cheng"], + "頴" => &["ying"], + "頵" => &["jun"], + "頶" => &["hu"], + "頷" => &["han"], + "頸" => &["jing","geng"], + "頹" => &["tui"], + "頺" => &["tui"], + "頻" => &["pin"], + "頼" => &["lai"], + "頽" => &["tui"], + "頾" => &["zi"], + "頿" => &["zi"], + "顀" => &["chui"], + "顁" => &["ding"], + "顂" => &["lai"], + "顃" => &["yan"], + "顄" => &["han"], + "顅" => &["qian"], + "顆" => &["ke"], + "顇" => &["cui"], + "顈" => &["jiong"], + "顉" => &["qin"], + "顊" => &["yi"], + "顋" => &["sai"], + "題" => &["ti"], + "額" => &["e"], + "顎" => &["e"], + "顏" => &["yan"], + "顐" => &["hun"], + "顑" => &["kan"], + "顒" => &["yong"], + "顓" => &["zhuan"], + "顔" => &["yan"], + "顕" => &["xian"], + "顖" => &["xin"], + "顗" => &["yi"], + "願" => &["yuan"], + "顙" => &["sang"], + "顚" => &["dian"], + "顛" => &["dian"], + "顜" => &["jiang"], + "顝" => &["ku"], + "類" => &["lei"], + "顟" => &["liao"], + "顠" => &["piao"], + "顡" => &["yi"], + "顢" => &["man"], + "顣" => &["qi"], + "顤" => &["yao"], + "顥" => &["hao"], + "顦" => &["qiao"], + "顧" => &["gu"], + "顨" => &["xun"], + "顩" => &["qian"], + "顪" => &["hui"], + "顫" => &["zhan","chan"], + "顬" => &["ru"], + "顭" => &["hong"], + "顮" => &["bin"], + "顯" => &["xian"], + "顰" => &["pin"], + "顱" => &["lu"], + "顲" => &["lan"], + "顳" => &["nie"], + "顴" => &["quan"], + "页" => &["ye"], + "顶" => &["ding"], + "顷" => &["qing"], + "顸" => &["han"], + "项" => &["xiang"], + "顺" => &["shun"], + "须" => &["xu"], + "顼" => &["xu"], + "顽" => &["wan"], + "顾" => &["gu"], + "顿" => &["dun","du"], + "颀" => &["qi"], + "颁" => &["ban"], + "颂" => &["song"], + "颃" => &["hang"], + "预" => &["yu"], + "颅" => &["lu"], + "领" => &["ling"], + "颇" => &["po"], + "颈" => &["jing","geng"], + "颉" => &["jie","xie"], + "颊" => &["jia"], + "颋" => &["ting"], + "颌" => &["he","ge"], + "颍" => &["ying"], + "颎" => &["jiong"], + "颏" => &["ke"], + "颐" => &["yi"], + "频" => &["pin"], + "颒" => &["hui"], + "颓" => &["tui"], + "颔" => &["han"], + "颕" => &["ying"], + "颖" => &["ying"], + "颗" => &["ke"], + "题" => &["ti"], + "颙" => &["yong"], + "颚" => &["e"], + "颛" => &["zhuan"], + "颜" => &["yan"], + "额" => &["e"], + "颞" => &["nie"], + "颟" => &["man"], + "颠" => &["dian"], + "颡" => &["sang"], + "颢" => &["hao"], + "颣" => &["lei"], + "颤" => &["zhan","chan"], + "颥" => &["ru"], + "颦" => &["pin"], + "颧" => &["quan"], + "風" => &["feng"], + "颩" => &["biao"], + "颫" => &["fu"], + "颬" => &["xia"], + "颭" => &["zhan"], + "颮" => &["biao"], + "颯" => &["sa"], + "颰" => &["fa"], + "颱" => &["tai"], + "颲" => &["lie"], + "颳" => &["gua"], + "颴" => &["xuan"], + "颵" => &["shao"], + "颶" => &["ju"], + "颷" => &["biao"], + "颸" => &["si"], + "颹" => &["wei"], + "颺" => &["yang"], + "颻" => &["yao"], + "颼" => &["sou"], + "颽" => &["kai"], + "颾" => &["sao"], + "颿" => &["fan"], + "飀" => &["liu"], + "飁" => &["xi"], + "飂" => &["liao"], + "飃" => &["piao"], + "飄" => &["piao"], + "飅" => &["liu"], + "飆" => &["biao"], + "飇" => &["biao"], + "飈" => &["biao"], + "飉" => &["liao"], + "飋" => &["se"], + "飌" => &["feng"], + "飍" => &["biao"], + "风" => &["feng"], + "飏" => &["yang"], + "飐" => &["zhan"], + "飑" => &["biao"], + "飒" => &["sa"], + "飓" => &["ju"], + "飔" => &["si"], + "飕" => &["sou"], + "飖" => &["yao"], + "飗" => &["liu"], + "飘" => &["piao"], + "飙" => &["biao"], + "飚" => &["biao"], + "飛" => &["fei"], + "飜" => &["fan"], + "飝" => &["fei"], + "飞" => &["fei"], + "食" => &["shi","si","yi"], + "飠" => &["shi","si"], + "飡" => &["can"], + "飢" => &["ji"], + "飣" => &["ding"], + "飤" => &["si"], + "飥" => &["tuo"], + "飦" => &["jian"], + "飧" => &["sun"], + "飨" => &["xiang"], + "飩" => &["tun"], + "飪" => &["ren"], + "飫" => &["yu"], + "飬" => &["juan"], + "飭" => &["chi"], + "飮" => &["yin"], + "飯" => &["fan"], + "飰" => &["fan"], + "飱" => &["sun"], + "飲" => &["yin"], + "飳" => &["zhu"], + "飴" => &["yi"], + "飵" => &["zhai"], + "飶" => &["bi"], + "飷" => &["jie"], + "飸" => &["tao"], + "飹" => &["liu"], + "飺" => &["ci"], + "飻" => &["tie"], + "飼" => &["si"], + "飽" => &["bao"], + "飾" => &["shi"], + "飿" => &["duo"], + "餀" => &["hai"], + "餁" => &["ren"], + "餂" => &["tian"], + "餃" => &["jiao","jia"], + "餄" => &["jia"], + "餅" => &["bing"], + "餆" => &["yao"], + "餇" => &["tong"], + "餈" => &["ci"], + "餉" => &["xiang"], + "養" => &["yang"], + "餋" => &["yang"], + "餌" => &["er"], + "餍" => &["yan"], + "餎" => &["le"], + "餏" => &["yi"], + "餐" => &["can"], + "餑" => &["bo"], + "餒" => &["nei"], + "餓" => &["e"], + "餔" => &["bu"], + "餕" => &["jun"], + "餖" => &["dou"], + "餗" => &["su"], + "餘" => &["yu"], + "餙" => &["shi"], + "餚" => &["yao"], + "餛" => &["hun"], + "餜" => &["guo"], + "餝" => &["shi"], + "餞" => &["jian"], + "餟" => &["zhui"], + "餠" => &["bing"], + "餡" => &["xian"], + "餢" => &["bu"], + "餣" => &["ye"], + "餤" => &["tan"], + "餥" => &["fei"], + "餦" => &["zhang"], + "餧" => &["wei"], + "館" => &["guan"], + "餩" => &["e"], + "餪" => &["nuan"], + "餫" => &["hun"], + "餬" => &["hu"], + "餭" => &["huang"], + "餮" => &["tie"], + "餯" => &["hui"], + "餰" => &["jian"], + "餱" => &["hou"], + "餲" => &["he"], + "餳" => &["xing","tang"], + "餴" => &["fen"], + "餵" => &["wei"], + "餶" => &["gu"], + "餷" => &["cha"], + "餸" => &["song"], + "餹" => &["tang","xing"], + "餺" => &["bo"], + "餻" => &["gao"], + "餼" => &["xi"], + "餽" => &["kui"], + "餾" => &["liu"], + "餿" => &["sou"], + "饀" => &["tao"], + "饁" => &["ye"], + "饂" => &["yun"], + "饃" => &["mo"], + "饄" => &["tang"], + "饅" => &["man"], + "饆" => &["bi"], + "饇" => &["yu"], + "饈" => &["xiu"], + "饉" => &["jin"], + "饊" => &["san"], + "饋" => &["kui"], + "饌" => &["zhuan"], + "饍" => &["shan"], + "饎" => &["chi"], + "饏" => &["dan"], + "饐" => &["yi"], + "饑" => &["ji"], + "饒" => &["rao"], + "饓" => &["cheng"], + "饔" => &["yong"], + "饕" => &["tao"], + "饖" => &["hui"], + "饗" => &["xiang"], + "饘" => &["zhan"], + "饙" => &["fen"], + "饚" => &["hai"], + "饛" => &["meng"], + "饜" => &["yan"], + "饝" => &["mo"], + "饞" => &["chan"], + "饟" => &["xiang"], + "饠" => &["luo"], + "饡" => &["zuan","zan"], + "饢" => &["nang"], + "饣" => &["shi","si"], + "饤" => &["ding"], + "饥" => &["ji"], + "饦" => &["tuo"], + "饧" => &["xing","tang"], + "饨" => &["tun"], + "饩" => &["xi"], + "饪" => &["ren"], + "饫" => &["yu"], + "饬" => &["chi"], + "饭" => &["fan"], + "饮" => &["yin"], + "饯" => &["jian"], + "饰" => &["shi"], + "饱" => &["bao"], + "饲" => &["si"], + "饳" => &["duo"], + "饴" => &["yi"], + "饵" => &["er"], + "饶" => &["rao"], + "饷" => &["xiang"], + "饸" => &["he"], + "饹" => &["le"], + "饺" => &["jiao","jia"], + "饻" => &["xi"], + "饼" => &["bing"], + "饽" => &["bo"], + "饾" => &["dou"], + "饿" => &["e"], + "馀" => &["yu"], + "馁" => &["nei"], + "馂" => &["jun"], + "馃" => &["guo"], + "馄" => &["hun"], + "馅" => &["xian"], + "馆" => &["guan"], + "馇" => &["cha"], + "馈" => &["kui"], + "馉" => &["gu"], + "馊" => &["sou"], + "馋" => &["chan"], + "馌" => &["ye"], + "馍" => &["mo"], + "馎" => &["bo"], + "馏" => &["liu"], + "馐" => &["xiu"], + "馑" => &["jin"], + "馒" => &["man"], + "馓" => &["san"], + "馔" => &["zhuan"], + "馕" => &["nang"], + "首" => &["shou"], + "馗" => &["kui"], + "馘" => &["guo"], + "香" => &["xiang"], + "馚" => &["fen"], + "馛" => &["ba"], + "馜" => &["ni"], + "馝" => &["bi"], + "馞" => &["bo"], + "馟" => &["tu"], + "馠" => &["han"], + "馡" => &["fei"], + "馢" => &["jian"], + "馣" => &["yan"], + "馤" => &["ai"], + "馥" => &["fu"], + "馦" => &["xian"], + "馧" => &["wen"], + "馨" => &["xin","xing"], + "馩" => &["fen"], + "馪" => &["bin"], + "馫" => &["xing"], + "馬" => &["ma"], + "馭" => &["yu"], + "馮" => &["feng","ping"], + "馯" => &["han"], + "馰" => &["di"], + "馱" => &["tuo","duo"], + "馲" => &["tuo"], + "馳" => &["chi"], + "馴" => &["xun"], + "馵" => &["zhu"], + "馶" => &["zhi"], + "馷" => &["pei"], + "馸" => &["xin"], + "馹" => &["ri"], + "馺" => &["sa"], + "馻" => &["yin"], + "馼" => &["wen"], + "馽" => &["zhi"], + "馾" => &["dan"], + "馿" => &["lu:"], + "駀" => &["you"], + "駁" => &["bo"], + "駂" => &["bao"], + "駃" => &["kuai"], + "駄" => &["tuo","duo"], + "駅" => &["yi"], + "駆" => &["qu"], + "駇" => &["wen"], + "駈" => &["qu"], + "駉" => &["jiong"], + "駊" => &["bo"], + "駋" => &["zhao"], + "駌" => &["yuan"], + "駍" => &["peng"], + "駎" => &["zhou"], + "駏" => &["ju"], + "駐" => &["zhu"], + "駑" => &["nu"], + "駒" => &["ju"], + "駓" => &["pi"], + "駔" => &["zang"], + "駕" => &["jia"], + "駖" => &["ling"], + "駗" => &["zhen"], + "駘" => &["tai"], + "駙" => &["fu"], + "駚" => &["yang"], + "駛" => &["shi"], + "駜" => &["bi"], + "駝" => &["tuo"], + "駞" => &["tuo"], + "駟" => &["si"], + "駠" => &["liu"], + "駡" => &["ma"], + "駢" => &["pian"], + "駣" => &["tao"], + "駤" => &["zhi"], + "駥" => &["rong"], + "駦" => &["teng"], + "駧" => &["dong"], + "駨" => &["xun"], + "駩" => &["quan"], + "駪" => &["shen"], + "駫" => &["jiong"], + "駬" => &["er"], + "駭" => &["hai","xie"], + "駮" => &["bo"], + "駰" => &["yin"], + "駱" => &["luo"], + "駳" => &["dan"], + "駴" => &["xie"], + "駵" => &["liu"], + "駶" => &["ju"], + "駷" => &["song"], + "駸" => &["qin"], + "駹" => &["mang"], + "駺" => &["liang"], + "駻" => &["han"], + "駼" => &["tu"], + "駽" => &["xuan"], + "駾" => &["tui"], + "駿" => &["jun"], + "騀" => &["e"], + "騁" => &["cheng"], + "騂" => &["xing"], + "騃" => &["ai"], + "騄" => &["lu"], + "騅" => &["zhui"], + "騆" => &["zhou"], + "騇" => &["she"], + "騈" => &["pian"], + "騉" => &["kun"], + "騊" => &["tao"], + "騋" => &["lai"], + "騌" => &["zong"], + "騍" => &["ke"], + "騎" => &["qi","ji"], + "騏" => &["qi"], + "騐" => &["yan"], + "騑" => &["fei"], + "騒" => &["sao"], + "験" => &["yan"], + "騔" => &["jie","ge"], + "騕" => &["yao"], + "騖" => &["wu"], + "騗" => &["pian"], + "騘" => &["cong"], + "騙" => &["pian"], + "騚" => &["qian"], + "騛" => &["fei"], + "騜" => &["huang"], + "騝" => &["jian"], + "騞" => &["huo"], + "騟" => &["yu"], + "騠" => &["ti"], + "騡" => &["quan"], + "騢" => &["xia"], + "騣" => &["zong"], + "騤" => &["kui"], + "騥" => &["rou"], + "騦" => &["si"], + "騧" => &["gua"], + "騨" => &["tuo","tan"], + "騩" => &["kui"], + "騪" => &["sou"], + "騫" => &["qian"], + "騬" => &["cheng"], + "騭" => &["zhi"], + "騮" => &["liu"], + "騯" => &["pang"], + "騰" => &["teng"], + "騱" => &["xi"], + "騲" => &["cao"], + "騳" => &["du"], + "騴" => &["yan"], + "騵" => &["yuan"], + "騶" => &["zou"], + "騷" => &["sao"], + "騸" => &["shan"], + "騹" => &["li"], + "騺" => &["zhi"], + "騻" => &["shuang"], + "騼" => &["lu"], + "騽" => &["xi"], + "騾" => &["luo"], + "騿" => &["zhang"], + "驀" => &["mo"], + "驁" => &["ao"], + "驂" => &["can"], + "驃" => &["piao","biao"], + "驄" => &["cong"], + "驅" => &["qu"], + "驆" => &["bi"], + "驇" => &["zhi"], + "驈" => &["yu"], + "驉" => &["xu"], + "驊" => &["hua"], + "驋" => &["bo"], + "驌" => &["su"], + "驍" => &["xiao"], + "驎" => &["lin"], + "驏" => &["zhan"], + "驐" => &["dun"], + "驑" => &["liu"], + "驒" => &["tuo"], + "驓" => &["zeng"], + "驔" => &["tan"], + "驕" => &["jiao"], + "驖" => &["tie"], + "驗" => &["yan"], + "驘" => &["luo"], + "驙" => &["zhan"], + "驚" => &["jing"], + "驛" => &["yi"], + "驜" => &["ye"], + "驝" => &["tuo"], + "驞" => &["bin"], + "驟" => &["zou","zhou"], + "驠" => &["yan"], + "驡" => &["peng"], + "驢" => &["lu:"], + "驣" => &["teng"], + "驤" => &["xiang"], + "驥" => &["ji"], + "驦" => &["shuang"], + "驧" => &["ju"], + "驨" => &["xi"], + "驩" => &["huan"], + "驪" => &["li"], + "驫" => &["biao"], + "马" => &["ma"], + "驭" => &["yu"], + "驮" => &["tuo","duo"], + "驯" => &["xun"], + "驰" => &["chi"], + "驱" => &["qu"], + "驲" => &["ri"], + "驳" => &["bo"], + "驴" => &["lu:"], + "驵" => &["zang"], + "驶" => &["shi"], + "驷" => &["si"], + "驸" => &["fu"], + "驹" => &["ju"], + "驺" => &["zou"], + "驻" => &["zhu"], + "驼" => &["tuo"], + "驽" => &["nu"], + "驾" => &["jia"], + "驿" => &["yi"], + "骀" => &["tai","dai"], + "骁" => &["xiao"], + "骂" => &["ma"], + "骃" => &["yin"], + "骄" => &["jiao"], + "骅" => &["hua"], + "骆" => &["luo"], + "骇" => &["hai"], + "骈" => &["pian"], + "骉" => &["biao"], + "骊" => &["li"], + "骋" => &["cheng"], + "验" => &["yan"], + "骍" => &["xing"], + "骎" => &["qin"], + "骏" => &["jun"], + "骐" => &["qi"], + "骑" => &["qi"], + "骒" => &["ke"], + "骓" => &["zhui"], + "骔" => &["zong"], + "骕" => &["su"], + "骖" => &["can"], + "骗" => &["pian"], + "骘" => &["zhi"], + "骙" => &["kui"], + "骚" => &["sao"], + "骛" => &["wu"], + "骜" => &["ao"], + "骝" => &["liu"], + "骞" => &["qian"], + "骟" => &["shan"], + "骠" => &["piao","biao"], + "骡" => &["luo"], + "骢" => &["cong"], + "骣" => &["zhan","chan"], + "骤" => &["zhou"], + "骥" => &["ji"], + "骦" => &["shuang"], + "骧" => &["xiang"], + "骨" => &["gu"], + "骩" => &["wei"], + "骪" => &["wei"], + "骫" => &["wei"], + "骬" => &["yu"], + "骭" => &["gan"], + "骮" => &["yi"], + "骯" => &["ang"], + "骰" => &["tou","shai"], + "骱" => &["jie","xie"], + "骲" => &["bo"], + "骳" => &["bi"], + "骴" => &["ci"], + "骵" => &["ti"], + "骶" => &["di"], + "骷" => &["ku"], + "骸" => &["hai"], + "骹" => &["qiao"], + "骺" => &["hou"], + "骻" => &["kua"], + "骼" => &["ge"], + "骽" => &["tui"], + "骾" => &["geng"], + "骿" => &["pian"], + "髀" => &["bi"], + "髁" => &["ke"], + "髂" => &["qia"], + "髃" => &["yu"], + "髄" => &["sui"], + "髅" => &["lou"], + "髆" => &["bo"], + "髇" => &["xiao"], + "髈" => &["bang"], + "髉" => &["bo"], + "髊" => &["cuo"], + "髋" => &["kuan"], + "髌" => &["bin"], + "髍" => &["mo"], + "髎" => &["liao"], + "髏" => &["lou"], + "髐" => &["nao"], + "髑" => &["du"], + "髒" => &["zang"], + "髓" => &["sui"], + "體" => &["ti"], + "髕" => &["bin"], + "髖" => &["kuan"], + "髗" => &["lu"], + "高" => &["gao"], + "髙" => &["gao"], + "髚" => &["qiao"], + "髛" => &["kao"], + "髜" => &["qiao"], + "髝" => &["lao"], + "髞" => &["zao"], + "髟" => &["biao","shan"], + "髠" => &["kun"], + "髡" => &["kun"], + "髢" => &["ti"], + "髣" => &["fang"], + "髤" => &["xiu"], + "髥" => &["ran"], + "髦" => &["mao"], + "髧" => &["dan"], + "髨" => &["kun"], + "髩" => &["bin"], + "髪" => &["fa"], + "髫" => &["tiao"], + "髬" => &["pi"], + "髭" => &["zi"], + "髮" => &["fa"], + "髯" => &["ran"], + "髰" => &["ti"], + "髱" => &["pao"], + "髲" => &["pi"], + "髳" => &["mao"], + "髴" => &["fu","fo"], + "髵" => &["er"], + "髶" => &["rong"], + "髷" => &["qu"], + "髹" => &["xiu"], + "髺" => &["gua"], + "髻" => &["ji"], + "髼" => &["peng"], + "髽" => &["zhua"], + "髾" => &["shao"], + "髿" => &["sha"], + "鬀" => &["ti"], + "鬁" => &["li"], + "鬂" => &["bin"], + "鬃" => &["zong"], + "鬄" => &["ti"], + "鬅" => &["peng"], + "鬆" => &["song"], + "鬇" => &["zheng"], + "鬈" => &["quan","qian"], + "鬉" => &["zong"], + "鬊" => &["shun"], + "鬋" => &["jian"], + "鬌" => &["duo"], + "鬍" => &["hu"], + "鬎" => &["la"], + "鬏" => &["jiu"], + "鬐" => &["qi"], + "鬑" => &["lian"], + "鬒" => &["zhen"], + "鬓" => &["bin"], + "鬔" => &["peng"], + "鬕" => &["mo"], + "鬖" => &["san"], + "鬗" => &["man"], + "鬘" => &["man"], + "鬙" => &["seng"], + "鬚" => &["xu"], + "鬛" => &["lie"], + "鬜" => &["qian"], + "鬝" => &["qian"], + "鬞" => &["nong"], + "鬟" => &["huan"], + "鬠" => &["kuai"], + "鬡" => &["ning"], + "鬢" => &["bin"], + "鬣" => &["lie"], + "鬤" => &["rang"], + "鬥" => &["dou"], + "鬦" => &["dou"], + "鬧" => &["nao"], + "鬨" => &["hong"], + "鬩" => &["xi"], + "鬪" => &["dou"], + "鬫" => &["kan"], + "鬬" => &["dou"], + "鬭" => &["dou"], + "鬮" => &["jiu"], + "鬯" => &["chang"], + "鬰" => &["yu"], + "鬱" => &["yu"], + "鬲" => &["li","ge"], + "鬳" => &["juan"], + "鬴" => &["fu"], + "鬵" => &["qian"], + "鬶" => &["gui"], + "鬷" => &["zong"], + "鬸" => &["liu"], + "鬹" => &["gui"], + "鬺" => &["shang"], + "鬻" => &["yu"], + "鬼" => &["gui"], + "鬽" => &["mei"], + "鬾" => &["ji"], + "鬿" => &["qi"], + "魀" => &["jie"], + "魁" => &["kui"], + "魂" => &["hun"], + "魃" => &["ba"], + "魄" => &["po","tuo","bo"], + "魅" => &["mei"], + "魆" => &["xu"], + "魇" => &["yan"], + "魈" => &["xiao"], + "魉" => &["liang"], + "魊" => &["yu"], + "魋" => &["tui"], + "魌" => &["qi"], + "魍" => &["wang"], + "魎" => &["liang"], + "魏" => &["wei"], + "魐" => &["jian"], + "魑" => &["chi"], + "魒" => &["piao"], + "魓" => &["bi"], + "魔" => &["mo"], + "魕" => &["ji"], + "魖" => &["xu"], + "魗" => &["chou"], + "魘" => &["yan"], + "魙" => &["zhan"], + "魚" => &["yu"], + "魛" => &["dao"], + "魜" => &["ren"], + "魝" => &["ji"], + "魞" => &["ba"], + "魟" => &["hong"], + "魠" => &["tuo"], + "魡" => &["diao"], + "魢" => &["ji"], + "魣" => &["yu"], + "魤" => &["e"], + "魥" => &["que"], + "魦" => &["sha"], + "魧" => &["hang"], + "魨" => &["tun"], + "魩" => &["mo"], + "魪" => &["gai"], + "魫" => &["shen"], + "魬" => &["fan"], + "魭" => &["yuan"], + "魮" => &["pi"], + "魯" => &["lu"], + "魰" => &["wen"], + "魱" => &["hu"], + "魲" => &["lu"], + "魳" => &["za"], + "魴" => &["fang"], + "魵" => &["fen"], + "魶" => &["na"], + "魷" => &["you"], + "魺" => &["he","ge"], + "魻" => &["xia"], + "魼" => &["qu"], + "魽" => &["han"], + "魾" => &["pi"], + "魿" => &["ling"], + "鮀" => &["tuo"], + "鮁" => &["ba"], + "鮂" => &["qiu"], + "鮃" => &["ping"], + "鮄" => &["fu"], + "鮅" => &["bi"], + "鮆" => &["ji"], + "鮇" => &["wei"], + "鮈" => &["ju"], + "鮉" => &["diao"], + "鮊" => &["ba"], + "鮋" => &["you"], + "鮌" => &["gun"], + "鮍" => &["pi"], + "鮎" => &["nian"], + "鮏" => &["xing"], + "鮐" => &["tai"], + "鮑" => &["bao"], + "鮒" => &["fu"], + "鮓" => &["zha"], + "鮔" => &["ju"], + "鮕" => &["gu"], + "鮙" => &["ta"], + "鮚" => &["jie"], + "鮛" => &["shua"], + "鮜" => &["hou"], + "鮝" => &["xiang"], + "鮞" => &["er"], + "鮟" => &["an"], + "鮠" => &["wei"], + "鮡" => &["tiao"], + "鮢" => &["zhu"], + "鮣" => &["yin"], + "鮤" => &["lie"], + "鮥" => &["luo"], + "鮦" => &["tong"], + "鮧" => &["yi"], + "鮨" => &["qi"], + "鮩" => &["bing"], + "鮪" => &["wei"], + "鮫" => &["jiao"], + "鮬" => &["pu"], + "鮭" => &["gui","xie"], + "鮮" => &["xian"], + "鮯" => &["ge"], + "鮰" => &["hui"], + "鮳" => &["kao"], + "鮵" => &["duo"], + "鮶" => &["jun"], + "鮷" => &["ti"], + "鮸" => &["mian"], + "鮹" => &["shao"], + "鮺" => &["za"], + "鮻" => &["suo"], + "鮼" => &["qin"], + "鮽" => &["yu"], + "鮾" => &["nei"], + "鮿" => &["zhe"], + "鯀" => &["gun"], + "鯁" => &["geng"], + "鯃" => &["wu"], + "鯄" => &["qiu"], + "鯅" => &["ting"], + "鯆" => &["fu"], + "鯇" => &["huan"], + "鯈" => &["chou"], + "鯉" => &["li"], + "鯊" => &["sha"], + "鯋" => &["sha"], + "鯌" => &["gao"], + "鯍" => &["meng"], + "鯒" => &["yong"], + "鯓" => &["ni"], + "鯔" => &["zi"], + "鯕" => &["qi"], + "鯖" => &["qing","zheng"], + "鯗" => &["xiang"], + "鯘" => &["nei"], + "鯙" => &["chun"], + "鯚" => &["ji"], + "鯛" => &["diao"], + "鯜" => &["qie"], + "鯝" => &["gu"], + "鯞" => &["zhou"], + "鯟" => &["dong"], + "鯠" => &["lai"], + "鯡" => &["fei"], + "鯢" => &["ni"], + "鯣" => &["yi"], + "鯤" => &["kun"], + "鯥" => &["lu"], + "鯦" => &["jiu"], + "鯧" => &["chang"], + "鯨" => &["jing"], + "鯩" => &["lun"], + "鯪" => &["ling"], + "鯫" => &["zou"], + "鯬" => &["li"], + "鯭" => &["meng"], + "鯮" => &["zong"], + "鯯" => &["zhi"], + "鯰" => &["nian"], + "鯴" => &["shi"], + "鯵" => &["sao"], + "鯶" => &["hun"], + "鯷" => &["ti"], + "鯸" => &["hou"], + "鯹" => &["xing"], + "鯺" => &["ju"], + "鯻" => &["la"], + "鯼" => &["zong"], + "鯽" => &["ji"], + "鯾" => &["bian"], + "鯿" => &["bian"], + "鰀" => &["huan"], + "鰁" => &["quan"], + "鰂" => &["ji"], + "鰃" => &["wei"], + "鰄" => &["wei"], + "鰅" => &["yu"], + "鰆" => &["chun"], + "鰇" => &["rou"], + "鰈" => &["die"], + "鰉" => &["huang"], + "鰊" => &["lian"], + "鰋" => &["yan"], + "鰌" => &["qiu"], + "鰍" => &["qiu"], + "鰎" => &["jian"], + "鰏" => &["bi"], + "鰐" => &["e"], + "鰑" => &["yang"], + "鰒" => &["fu"], + "鰓" => &["sai","xi"], + "鰔" => &["jian"], + "鰕" => &["ha","xia"], + "鰖" => &["tuo"], + "鰗" => &["hu"], + "鰙" => &["ruo"], + "鰛" => &["wen"], + "鰜" => &["jian"], + "鰝" => &["hao"], + "鰞" => &["wu"], + "鰟" => &["pang"], + "鰠" => &["sao"], + "鰡" => &["liu"], + "鰢" => &["ma"], + "鰣" => &["shi"], + "鰤" => &["shi"], + "鰥" => &["guan"], + "鰦" => &["zi"], + "鰧" => &["teng"], + "鰨" => &["ta"], + "鰩" => &["yao"], + "鰪" => &["ge"], + "鰫" => &["rong"], + "鰬" => &["qian"], + "鰭" => &["qi"], + "鰮" => &["wen"], + "鰯" => &["ruo"], + "鰱" => &["lian"], + "鰲" => &["ao"], + "鰳" => &["le"], + "鰴" => &["hui"], + "鰵" => &["min"], + "鰶" => &["ji"], + "鰷" => &["tiao"], + "鰸" => &["qu"], + "鰹" => &["jian"], + "鰺" => &["sao"], + "鰻" => &["man"], + "鰼" => &["xi"], + "鰽" => &["qiu"], + "鰾" => &["biao"], + "鰿" => &["ji"], + "鱀" => &["ji"], + "鱁" => &["zhu"], + "鱂" => &["jiang"], + "鱃" => &["qiu"], + "鱄" => &["zhuan"], + "鱅" => &["yong"], + "鱆" => &["zhang"], + "鱇" => &["kang"], + "鱈" => &["xue"], + "鱉" => &["bie"], + "鱊" => &["jue"], + "鱋" => &["qu"], + "鱌" => &["xiang"], + "鱍" => &["bo"], + "鱎" => &["jiao"], + "鱏" => &["xun"], + "鱐" => &["su"], + "鱑" => &["huang"], + "鱒" => &["zun"], + "鱓" => &["shan"], + "鱔" => &["shan"], + "鱕" => &["fan"], + "鱖" => &["gui"], + "鱗" => &["lin"], + "鱘" => &["xun"], + "鱙" => &["miao"], + "鱚" => &["xi"], + "鱜" => &["xiang"], + "鱝" => &["fen"], + "鱞" => &["guan"], + "鱟" => &["hou"], + "鱠" => &["kuai"], + "鱡" => &["zei"], + "鱢" => &["sao"], + "鱣" => &["zhan"], + "鱤" => &["gan"], + "鱥" => &["gui"], + "鱦" => &["sheng"], + "鱧" => &["li"], + "鱨" => &["chang"], + "鱫" => &["ai"], + "鱬" => &["ru"], + "鱭" => &["ji"], + "鱮" => &["xu"], + "鱯" => &["huo"], + "鱱" => &["li"], + "鱲" => &["lie"], + "鱳" => &["li"], + "鱴" => &["mie"], + "鱵" => &["zhen"], + "鱶" => &["xiang"], + "鱷" => &["e"], + "鱸" => &["lu"], + "鱹" => &["guan"], + "鱺" => &["li"], + "鱻" => &["xian"], + "鱼" => &["yu"], + "鱽" => &["dao"], + "鱾" => &["ji"], + "鱿" => &["you"], + "鲀" => &["tun"], + "鲁" => &["lu"], + "鲂" => &["fang"], + "鲃" => &["ba"], + "鲄" => &["ke"], + "鲅" => &["ba"], + "鲆" => &["ping"], + "鲇" => &["nian"], + "鲈" => &["lu"], + "鲉" => &["you"], + "鲊" => &["zha"], + "鲋" => &["fu"], + "鲌" => &["ba","bo"], + "鲍" => &["bao"], + "鲎" => &["hou"], + "鲏" => &["pi"], + "鲐" => &["tai"], + "鲑" => &["gui","xie"], + "鲒" => &["jie"], + "鲓" => &["kao"], + "鲔" => &["wei"], + "鲕" => &["er"], + "鲖" => &["tong"], + "鲗" => &["zei"], + "鲘" => &["hou"], + "鲙" => &["kuai"], + "鲚" => &["ji"], + "鲛" => &["jiao"], + "鲜" => &["xian"], + "鲝" => &["zha"], + "鲞" => &["xiang"], + "鲟" => &["xun"], + "鲠" => &["geng"], + "鲡" => &["li"], + "鲢" => &["lian"], + "鲣" => &["jian"], + "鲤" => &["li"], + "鲥" => &["shi"], + "鲦" => &["tiao"], + "鲧" => &["gun"], + "鲨" => &["sha"], + "鲩" => &["huan"], + "鲪" => &["jun"], + "鲫" => &["ji"], + "鲬" => &["yong"], + "鲭" => &["qing","zheng"], + "鲮" => &["ling"], + "鲯" => &["qi"], + "鲰" => &["zou"], + "鲱" => &["fei"], + "鲲" => &["kun"], + "鲳" => &["chang"], + "鲴" => &["gu"], + "鲵" => &["ni"], + "鲶" => &["nian"], + "鲷" => &["diao"], + "鲸" => &["jing"], + "鲹" => &["shen"], + "鲺" => &["shi"], + "鲻" => &["zi"], + "鲼" => &["fen"], + "鲽" => &["die"], + "鲾" => &["bi"], + "鲿" => &["chang"], + "鳀" => &["ti"], + "鳁" => &["wen"], + "鳂" => &["wei"], + "鳃" => &["sai"], + "鳄" => &["e"], + "鳅" => &["qiu"], + "鳆" => &["fu"], + "鳇" => &["huang"], + "鳈" => &["quan"], + "鳉" => &["jiang"], + "鳊" => &["bian"], + "鳋" => &["sao"], + "鳌" => &["ao"], + "鳍" => &["qi"], + "鳎" => &["ta"], + "鳏" => &["guan"], + "鳐" => &["yao"], + "鳑" => &["pang"], + "鳒" => &["jian"], + "鳓" => &["le"], + "鳔" => &["biao"], + "鳕" => &["xue"], + "鳖" => &["bie"], + "鳗" => &["man"], + "鳘" => &["min"], + "鳙" => &["yong"], + "鳚" => &["wei"], + "鳛" => &["xi"], + "鳜" => &["gui"], + "鳝" => &["shan"], + "鳞" => &["lin"], + "鳟" => &["zun"], + "鳠" => &["hu"], + "鳡" => &["gan"], + "鳢" => &["li"], + "鳣" => &["shan"], + "鳤" => &["guan"], + "鳥" => &["niao"], + "鳦" => &["yi"], + "鳧" => &["fu"], + "鳨" => &["li"], + "鳩" => &["jiu"], + "鳪" => &["bu"], + "鳫" => &["yan"], + "鳬" => &["fu"], + "鳭" => &["diao"], + "鳮" => &["ji"], + "鳯" => &["feng"], + "鳱" => &["gan"], + "鳲" => &["shi"], + "鳳" => &["feng"], + "鳴" => &["ming"], + "鳵" => &["bao"], + "鳶" => &["yuan"], + "鳷" => &["zhi"], + "鳸" => &["hu"], + "鳹" => &["qian"], + "鳺" => &["fu"], + "鳻" => &["fen"], + "鳼" => &["wen"], + "鳽" => &["jian"], + "鳾" => &["shi"], + "鳿" => &["yu"], + "鴀" => &["fou"], + "鴁" => &["yiao"], + "鴂" => &["ju"], + "鴃" => &["jue"], + "鴄" => &["pi"], + "鴅" => &["huan"], + "鴆" => &["zhen"], + "鴇" => &["bao"], + "鴈" => &["yan"], + "鴉" => &["ya"], + "鴊" => &["zheng"], + "鴋" => &["fang"], + "鴌" => &["feng"], + "鴍" => &["wen"], + "鴎" => &["ou"], + "鴏" => &["te"], + "鴐" => &["jia"], + "鴑" => &["nu"], + "鴒" => &["ling"], + "鴓" => &["mie"], + "鴔" => &["fu"], + "鴕" => &["tuo"], + "鴖" => &["wen"], + "鴗" => &["li"], + "鴘" => &["bian"], + "鴙" => &["zhi"], + "鴚" => &["ge"], + "鴛" => &["yuan"], + "鴜" => &["zi"], + "鴝" => &["qu"], + "鴞" => &["xiao"], + "鴟" => &["chi","zhi"], + "鴠" => &["dan"], + "鴡" => &["ju"], + "鴢" => &["you"], + "鴣" => &["gu"], + "鴤" => &["zhong"], + "鴥" => &["yu"], + "鴦" => &["yang"], + "鴧" => &["rong"], + "鴨" => &["ya"], + "鴩" => &["zhi"], + "鴪" => &["yu"], + "鴬" => &["ying"], + "鴭" => &["zhui"], + "鴮" => &["wu"], + "鴯" => &["er"], + "鴰" => &["gua"], + "鴱" => &["ai"], + "鴲" => &["zhi"], + "鴳" => &["yan"], + "鴴" => &["heng"], + "鴵" => &["jiao"], + "鴶" => &["ji"], + "鴷" => &["lie"], + "鴸" => &["zhu"], + "鴹" => &["ren"], + "鴺" => &["ti"], + "鴻" => &["hong"], + "鴼" => &["luo"], + "鴽" => &["ru"], + "鴾" => &["mou"], + "鴿" => &["ge"], + "鵀" => &["ren"], + "鵁" => &["jiao"], + "鵂" => &["xiu"], + "鵃" => &["zhou"], + "鵄" => &["chi"], + "鵅" => &["luo"], + "鵉" => &["luan"], + "鵊" => &["jia"], + "鵋" => &["ji"], + "鵌" => &["yu"], + "鵍" => &["huan"], + "鵎" => &["tuo"], + "鵏" => &["bu"], + "鵐" => &["wu"], + "鵑" => &["juan"], + "鵒" => &["yu"], + "鵓" => &["bo"], + "鵔" => &["xun"], + "鵕" => &["xun"], + "鵖" => &["bi"], + "鵗" => &["xi"], + "鵘" => &["jun"], + "鵙" => &["ju"], + "鵚" => &["tu"], + "鵛" => &["jing"], + "鵜" => &["ti"], + "鵝" => &["e"], + "鵞" => &["e"], + "鵟" => &["kuang"], + "鵠" => &["hu","gu"], + "鵡" => &["wu"], + "鵢" => &["shen"], + "鵣" => &["la"], + "鵦" => &["lu"], + "鵧" => &["bing"], + "鵨" => &["shu"], + "鵩" => &["fu"], + "鵪" => &["an"], + "鵫" => &["zhao"], + "鵬" => &["peng"], + "鵭" => &["qin"], + "鵮" => &["qian"], + "鵯" => &["bei"], + "鵰" => &["diao"], + "鵱" => &["lu"], + "鵲" => &["que","qiao"], + "鵳" => &["jian"], + "鵴" => &["ju"], + "鵵" => &["tu"], + "鵶" => &["ya"], + "鵷" => &["yuan"], + "鵸" => &["qi"], + "鵹" => &["li"], + "鵺" => &["ye"], + "鵻" => &["zhui"], + "鵼" => &["kong"], + "鵽" => &["duo"], + "鵾" => &["kun"], + "鵿" => &["sheng"], + "鶀" => &["qi"], + "鶁" => &["jing"], + "鶂" => &["ni"], + "鶃" => &["e"], + "鶄" => &["jing"], + "鶅" => &["zi"], + "鶆" => &["lai"], + "鶇" => &["dong"], + "鶈" => &["qi"], + "鶉" => &["chun"], + "鶊" => &["geng"], + "鶋" => &["ju"], + "鶌" => &["qu"], + "鶏" => &["ji"], + "鶐" => &["shu"], + "鶒" => &["chi"], + "鶓" => &["miao"], + "鶔" => &["rou"], + "鶕" => &["fu"], + "鶖" => &["qiu"], + "鶗" => &["ti"], + "鶘" => &["hu"], + "鶙" => &["ti"], + "鶚" => &["e"], + "鶛" => &["jie"], + "鶜" => &["mao"], + "鶝" => &["fu"], + "鶞" => &["chun"], + "鶟" => &["tu"], + "鶠" => &["yan"], + "鶡" => &["he"], + "鶢" => &["yuan"], + "鶣" => &["pian","bin"], + "鶤" => &["yun"], + "鶥" => &["mei"], + "鶦" => &["hu"], + "鶧" => &["ying"], + "鶨" => &["dun"], + "鶩" => &["mu","wu"], + "鶪" => &["ju"], + "鶬" => &["cang"], + "鶭" => &["fang"], + "鶮" => &["ge"], + "鶯" => &["ying"], + "鶰" => &["yuan"], + "鶱" => &["xuan"], + "鶲" => &["weng"], + "鶳" => &["shi"], + "鶴" => &["he","hao"], + "鶵" => &["chu"], + "鶶" => &["tang"], + "鶷" => &["xia"], + "鶸" => &["ruo"], + "鶹" => &["liu"], + "鶺" => &["ji"], + "鶻" => &["gu","hu"], + "鶼" => &["jian"], + "鶽" => &["zhun"], + "鶾" => &["han"], + "鶿" => &["zi"], + "鷀" => &["ci"], + "鷁" => &["yi","ni"], + "鷂" => &["yao"], + "鷃" => &["yan"], + "鷄" => &["ji"], + "鷅" => &["li","piao"], + "鷆" => &["tian"], + "鷇" => &["kou"], + "鷈" => &["ti"], + "鷉" => &["ti"], + "鷊" => &["ni"], + "鷋" => &["tu"], + "鷌" => &["ma"], + "鷍" => &["jiao"], + "鷎" => &["liu"], + "鷏" => &["zhen"], + "鷐" => &["chen"], + "鷑" => &["li"], + "鷒" => &["zhuan"], + "鷓" => &["zhe"], + "鷔" => &["ao"], + "鷕" => &["yao"], + "鷖" => &["yi"], + "鷗" => &["ou"], + "鷘" => &["chi"], + "鷙" => &["zhi"], + "鷚" => &["liao","liu"], + "鷛" => &["rong"], + "鷜" => &["lou"], + "鷝" => &["bi"], + "鷞" => &["shuang"], + "鷟" => &["zhuo"], + "鷠" => &["yu"], + "鷡" => &["wu"], + "鷢" => &["jue"], + "鷣" => &["yin"], + "鷤" => &["tan"], + "鷥" => &["si"], + "鷦" => &["jiao"], + "鷧" => &["yi"], + "鷨" => &["hua"], + "鷩" => &["bi"], + "鷪" => &["ying"], + "鷫" => &["su"], + "鷬" => &["huang"], + "鷭" => &["fan"], + "鷮" => &["jiao"], + "鷯" => &["liao"], + "鷰" => &["yan"], + "鷱" => &["kao"], + "鷲" => &["jiu"], + "鷳" => &["xian"], + "鷴" => &["xian"], + "鷵" => &["tu"], + "鷶" => &["mai"], + "鷷" => &["zun"], + "鷸" => &["yu"], + "鷹" => &["ying"], + "鷺" => &["lu"], + "鷻" => &["tuan"], + "鷼" => &["xian"], + "鷽" => &["xue"], + "鷾" => &["yi"], + "鷿" => &["pi"], + "鸀" => &["shu"], + "鸁" => &["luo"], + "鸂" => &["qi"], + "鸃" => &["yi"], + "鸄" => &["ji"], + "鸅" => &["zhe"], + "鸆" => &["yu"], + "鸇" => &["zhan"], + "鸈" => &["ye"], + "鸉" => &["yang"], + "鸊" => &["pi"], + "鸋" => &["ning"], + "鸌" => &["hu"], + "鸍" => &["mi"], + "鸎" => &["ying"], + "鸏" => &["meng"], + "鸐" => &["di"], + "鸑" => &["yue"], + "鸒" => &["yu"], + "鸓" => &["lei"], + "鸔" => &["bo"], + "鸕" => &["lu"], + "鸖" => &["he"], + "鸗" => &["long"], + "鸘" => &["shuang"], + "鸙" => &["yue"], + "鸚" => &["ying"], + "鸛" => &["guan"], + "鸜" => &["qu"], + "鸝" => &["li"], + "鸞" => &["luan"], + "鸟" => &["niao","diao"], + "鸠" => &["jiu"], + "鸡" => &["ji"], + "鸢" => &["yuan"], + "鸣" => &["ming"], + "鸤" => &["shi"], + "鸥" => &["ou"], + "鸦" => &["ya"], + "鸧" => &["cang"], + "鸨" => &["bao"], + "鸩" => &["zhen"], + "鸪" => &["gu"], + "鸫" => &["dong"], + "鸬" => &["lu"], + "鸭" => &["ya"], + "鸮" => &["xiao"], + "鸯" => &["yang"], + "鸰" => &["ling"], + "鸱" => &["chi"], + "鸲" => &["qu"], + "鸳" => &["yuan"], + "鸴" => &["xue"], + "鸵" => &["tuo"], + "鸶" => &["si"], + "鸷" => &["zhi"], + "鸸" => &["er"], + "鸹" => &["gua"], + "鸺" => &["xiu"], + "鸻" => &["heng"], + "鸼" => &["zhou"], + "鸽" => &["ge"], + "鸾" => &["luan"], + "鸿" => &["hong"], + "鹀" => &["wu"], + "鹁" => &["bo"], + "鹂" => &["li"], + "鹃" => &["juan"], + "鹄" => &["hu","gu"], + "鹅" => &["e"], + "鹆" => &["yu"], + "鹇" => &["xian"], + "鹈" => &["ti"], + "鹉" => &["wu"], + "鹊" => &["que"], + "鹋" => &["miao"], + "鹌" => &["an"], + "鹍" => &["kun"], + "鹎" => &["bei"], + "鹏" => &["peng"], + "鹐" => &["qian"], + "鹑" => &["chun"], + "鹒" => &["geng"], + "鹓" => &["yuan"], + "鹔" => &["su"], + "鹕" => &["hu"], + "鹖" => &["he"], + "鹗" => &["e"], + "鹘" => &["gu","hu"], + "鹙" => &["qiu"], + "鹚" => &["ci"], + "鹛" => &["mei"], + "鹜" => &["wu"], + "鹝" => &["yi"], + "鹞" => &["yao"], + "鹟" => &["weng"], + "鹠" => &["liu"], + "鹡" => &["ji"], + "鹢" => &["yi"], + "鹣" => &["jian"], + "鹤" => &["he"], + "鹥" => &["yi"], + "鹦" => &["ying"], + "鹧" => &["zhe"], + "鹨" => &["liu"], + "鹩" => &["liao"], + "鹪" => &["jiao"], + "鹫" => &["jiu"], + "鹬" => &["yu"], + "鹭" => &["lu"], + "鹮" => &["huan"], + "鹯" => &["zhan"], + "鹰" => &["ying"], + "鹱" => &["hu"], + "鹲" => &["meng"], + "鹳" => &["guan"], + "鹴" => &["shuang"], + "鹵" => &["lu"], + "鹶" => &["jin"], + "鹷" => &["ling"], + "鹸" => &["jian"], + "鹹" => &["xian"], + "鹺" => &["cuo"], + "鹻" => &["jian"], + "鹼" => &["jian"], + "鹽" => &["yan"], + "鹾" => &["cuo"], + "鹿" => &["lu"], + "麀" => &["you"], + "麁" => &["cu"], + "麂" => &["ji"], + "麃" => &["biao"], + "麄" => &["cu"], + "麅" => &["pao"], + "麆" => &["zhu"], + "麇" => &["jun","qun"], + "麈" => &["zhu"], + "麉" => &["jian","qian"], + "麊" => &["mi"], + "麋" => &["mi"], + "麌" => &["wu"], + "麍" => &["liu"], + "麎" => &["chen"], + "麏" => &["jun","qun"], + "麐" => &["lin"], + "麑" => &["ni"], + "麒" => &["qi"], + "麓" => &["lu"], + "麔" => &["jiu"], + "麕" => &["jun","qun"], + "麖" => &["jing"], + "麗" => &["li"], + "麘" => &["xiang"], + "麙" => &["yan"], + "麚" => &["jia"], + "麛" => &["mi"], + "麜" => &["li"], + "麝" => &["she"], + "麞" => &["zhang"], + "麟" => &["lin"], + "麠" => &["jing"], + "麡" => &["qi"], + "麢" => &["ling"], + "麣" => &["yan"], + "麤" => &["cu"], + "麥" => &["mai"], + "麦" => &["mai"], + "麧" => &["ge"], + "麨" => &["chao"], + "麩" => &["fu"], + "麪" => &["mian"], + "麫" => &["mian"], + "麬" => &["fu"], + "麭" => &["pao"], + "麮" => &["qu"], + "麯" => &["qu"], + "麰" => &["mou"], + "麱" => &["fu"], + "麲" => &["xian"], + "麳" => &["lai"], + "麴" => &["qu"], + "麵" => &["mian"], + "麶" => &["chi","li"], + "麷" => &["feng"], + "麸" => &["fu"], + "麹" => &["qu"], + "麺" => &["mian"], + "麻" => &["ma"], + "麼" => &["ma","me","mo"], + "麽" => &["mo","me"], + "麾" => &["hui"], + "黀" => &["zou"], + "黁" => &["nen"], + "黂" => &["fen"], + "黃" => &["huang"], + "黄" => &["huang"], + "黅" => &["jin"], + "黆" => &["guang"], + "黇" => &["tian"], + "黈" => &["tou"], + "黉" => &["hong"], + "黊" => &["xi"], + "黋" => &["kuang"], + "黌" => &["hong"], + "黍" => &["shu"], + "黎" => &["li"], + "黏" => &["nian"], + "黐" => &["chi","li"], + "黑" => &["hei"], + "黒" => &["hei"], + "黓" => &["yi"], + "黔" => &["qian"], + "黕" => &["zhen"], + "黖" => &["xi"], + "黗" => &["tuan"], + "默" => &["mo"], + "黙" => &["mo"], + "黚" => &["qian"], + "黛" => &["dai"], + "黜" => &["chu"], + "黝" => &["you"], + "點" => &["dian"], + "黟" => &["yi"], + "黠" => &["xia"], + "黡" => &["yan"], + "黢" => &["qu"], + "黣" => &["mei"], + "黤" => &["yan"], + "黥" => &["qing","jing"], + "黦" => &["yu"], + "黧" => &["li"], + "黨" => &["dang"], + "黩" => &["du"], + "黪" => &["can"], + "黫" => &["yin"], + "黬" => &["an"], + "黭" => &["yan"], + "黮" => &["tan"], + "黯" => &["an"], + "黰" => &["zhen"], + "黱" => &["dai"], + "黲" => &["can"], + "黳" => &["yi"], + "黴" => &["mei"], + "黵" => &["dan"], + "黶" => &["yan"], + "黷" => &["du"], + "黸" => &["lu"], + "黹" => &["zhi"], + "黺" => &["fen"], + "黻" => &["fu"], + "黼" => &["fu"], + "黽" => &["min","mian"], + "黾" => &["min","mian"], + "黿" => &["yuan"], + "鼀" => &["cu"], + "鼁" => &["qu"], + "鼂" => &["chao"], + "鼃" => &["wa"], + "鼄" => &["zhu"], + "鼅" => &["zhi"], + "鼆" => &["mang"], + "鼇" => &["ao"], + "鼈" => &["bie"], + "鼉" => &["tuo"], + "鼊" => &["bi"], + "鼋" => &["yuan"], + "鼌" => &["chao"], + "鼍" => &["tuo"], + "鼎" => &["ding"], + "鼏" => &["mi"], + "鼐" => &["nai"], + "鼑" => &["ding"], + "鼒" => &["zi"], + "鼓" => &["gu","hu"], + "鼔" => &["gu"], + "鼕" => &["dong"], + "鼖" => &["fen"], + "鼗" => &["tao"], + "鼘" => &["yuan"], + "鼙" => &["pi"], + "鼚" => &["chang"], + "鼛" => &["gao"], + "鼜" => &["qi"], + "鼝" => &["yuan"], + "鼞" => &["tang"], + "鼟" => &["teng"], + "鼠" => &["shu"], + "鼡" => &["shu"], + "鼢" => &["fen"], + "鼣" => &["fei"], + "鼤" => &["wen"], + "鼥" => &["ba"], + "鼦" => &["diao"], + "鼧" => &["tuo"], + "鼨" => &["tong"], + "鼩" => &["qu"], + "鼪" => &["sheng"], + "鼫" => &["shi"], + "鼬" => &["you"], + "鼭" => &["shi"], + "鼮" => &["ting"], + "鼯" => &["wu"], + "鼰" => &["nian"], + "鼱" => &["jing"], + "鼲" => &["hun"], + "鼳" => &["ju"], + "鼴" => &["yan"], + "鼵" => &["tu"], + "鼶" => &["si"], + "鼷" => &["xi"], + "鼸" => &["xian"], + "鼹" => &["yan"], + "鼺" => &["lei"], + "鼻" => &["bi"], + "鼼" => &["yao"], + "鼽" => &["yan","qui"], + "鼾" => &["han"], + "鼿" => &["hui"], + "齀" => &["wu"], + "齁" => &["hou"], + "齂" => &["xi"], + "齃" => &["ge"], + "齄" => &["zha"], + "齅" => &["xiu"], + "齆" => &["weng"], + "齇" => &["zha"], + "齈" => &["nong"], + "齉" => &["nang"], + "齊" => &["qi","zhai"], + "齋" => &["zhai"], + "齌" => &["ji"], + "齍" => &["zi","ji"], + "齎" => &["ji"], + "齏" => &["ji"], + "齐" => &["qi","ji"], + "齑" => &["ji"], + "齒" => &["chi"], + "齓" => &["chen"], + "齔" => &["chen"], + "齕" => &["he"], + "齖" => &["ya"], + "齗" => &["ken"], + "齘" => &["xie"], + "齙" => &["bao"], + "齚" => &["ze"], + "齛" => &["shi"], + "齜" => &["zi"], + "齝" => &["chi"], + "齞" => &["nian"], + "齟" => &["ju"], + "齠" => &["tiao"], + "齡" => &["ling"], + "齢" => &["ling"], + "齣" => &["chu"], + "齤" => &["quan"], + "齥" => &["xie"], + "齦" => &["yin","ken"], + "齧" => &["nie"], + "齨" => &["jiu"], + "齩" => &["nie"], + "齪" => &["chuo"], + "齫" => &["kun"], + "齬" => &["yu"], + "齭" => &["chu"], + "齮" => &["yi"], + "齯" => &["ni"], + "齰" => &["cuo"], + "齱" => &["chuo"], + "齲" => &["qu"], + "齳" => &["nian"], + "齴" => &["xian"], + "齵" => &["yu"], + "齶" => &["e"], + "齷" => &["wo"], + "齸" => &["yi"], + "齹" => &["chi"], + "齺" => &["zou"], + "齻" => &["dian"], + "齼" => &["chu"], + "齽" => &["jin"], + "齾" => &["ya"], + "齿" => &["chi"], + "龀" => &["chen"], + "龁" => &["he"], + "龂" => &["yin"], + "龃" => &["ju"], + "龄" => &["ling"], + "龅" => &["bao"], + "龆" => &["tiao"], + "龇" => &["zi"], + "龈" => &["yin","ken"], + "龉" => &["yu"], + "龊" => &["chuo"], + "龋" => &["qu"], + "龌" => &["wo"], + "龍" => &["long"], + "龎" => &["pang"], + "龏" => &["gong"], + "龐" => &["pang"], + "龑" => &["yan"], + "龒" => &["long"], + "龓" => &["long"], + "龔" => &["gong"], + "龕" => &["kan"], + "龖" => &["ta"], + "龗" => &["ling"], + "龘" => &["ta"], + "龙" => &["long"], + "龚" => &["gong"], + "龛" => &["kan"], + "龜" => &["gui","jun","qiu"], + "龝" => &["qiu"], + "龞" => &["bie"], + "龟" => &["gui","jun","qiu"], + "龠" => &["yue"], + "龡" => &["chui"], + "龢" => &["he"], + "龣" => &["jue"], + "龤" => &["xie"], + "龥" => &["yue"], +}; diff --git a/src/rust/examples/tests/crud/pinyin_dict/pinyin_set.rs b/src/rust/examples/tests/crud/pinyin_dict/pinyin_set.rs new file mode 100644 index 000000000..22c44c3a2 --- /dev/null +++ b/src/rust/examples/tests/crud/pinyin_dict/pinyin_set.rs @@ -0,0 +1,446 @@ +//! Auto-generated file. DO NOT EDIT MANUALLY. +// Generated at: 2025-12-12 02:07:53 UTC +// Source URL: https://raw.githubusercontent.com/infinilabs/analysis-pinyin/refs/heads/master/pinyin-core/src/main/resources/pinyin_alphabet.dict +// Total records: 437 + +use phf::phf_set; + +pub static PINYIN_SET: phf::Set<&'static str> = phf_set! { + "a", + "ai", + "an", + "ang", + "ao", + "b", + "ba", + "bai", + "ban", + "bang", + "bao", + "bei", + "ben", + "beng", + "bi", + "bian", + "biao", + "bie", + "bin", + "bing", + "bo", + "bu", + "c", + "ca", + "cai", + "can", + "cang", + "cao", + "ce", + "cen", + "ceng", + "ch", + "cha", + "chai", + "chan", + "chang", + "chao", + "che", + "chen", + "cheng", + "chi", + "chong", + "chou", + "chu", + "chua", + "chuai", + "chuan", + "chuang", + "chui", + "chun", + "chuo", + "ci", + "cong", + "cou", + "cu", + "cuan", + "cui", + "cun", + "cuo", + "d", + "da", + "dai", + "dan", + "dang", + "dao", + "de", + "dei", + "den", + "deng", + "di", + "dia", + "dian", + "diao", + "die", + "ding", + "diu", + "dong", + "dou", + "du", + "duan", + "dui", + "dun", + "duo", + "e", + "en", + "er", + "f", + "fa", + "fan", + "fang", + "fei", + "fen", + "feng", + "fiao", + "fo", + "fou", + "fu", + "g", + "ga", + "gai", + "gan", + "gang", + "gao", + "ge", + "gei", + "gen", + "geng", + "gong", + "gou", + "gu", + "gua", + "guai", + "guan", + "guang", + "gui", + "gun", + "guo", + "h", + "ha", + "hai", + "han", + "hang", + "hao", + "he", + "hei", + "hen", + "heng", + "hong", + "hou", + "hu", + "hua", + "huai", + "huan", + "huang", + "hui", + "hun", + "huo", + "i", + "j", + "ja", + "ji", + "jia", + "jian", + "jiang", + "jiao", + "jie", + "jin", + "jing", + "jiong", + "jiu", + "ju", + "juan", + "jue", + "jun", + "k", + "ka", + "kai", + "kan", + "kang", + "kao", + "ke", + "kei", + "ken", + "keng", + "kong", + "kou", + "ku", + "kua", + "kuai", + "kuan", + "kuang", + "kui", + "kun", + "kuo", + "l", + "la", + "lai", + "lan", + "lang", + "lao", + "le", + "lei", + "leng", + "li", + "lia", + "lian", + "liang", + "liao", + "lie", + "lin", + "ling", + "liu", + "lo", + "long", + "lou", + "lu", + "luan", + "lun", + "luo", + "lv", + "lve", + "m", + "ma", + "mai", + "man", + "mang", + "mao", + "me", + "mei", + "men", + "meng", + "mi", + "mian", + "miao", + "mie", + "min", + "ming", + "miu", + "mo", + "mou", + "mu", + "n", + "na", + "nai", + "nan", + "nang", + "nao", + "ne", + "nei", + "nen", + "neng", + "ni", + "nian", + "niang", + "niao", + "nie", + "nin", + "ning", + "niu", + "nong", + "nou", + "nu", + "nuan", + "nun", + "nuo", + "nv", + "nve", + "o", + "ou", + "p", + "pa", + "pai", + "pan", + "pang", + "pao", + "pei", + "pen", + "peng", + "pi", + "pian", + "piao", + "pie", + "pin", + "ping", + "po", + "pou", + "pu", + "q", + "qi", + "qia", + "qian", + "qiang", + "qiao", + "qie", + "qin", + "qing", + "qiong", + "qiu", + "qu", + "quan", + "que", + "qun", + "r", + "ran", + "rang", + "rao", + "re", + "ren", + "reng", + "ri", + "rong", + "rou", + "ru", + "ruan", + "rui", + "run", + "ruo", + "s", + "sa", + "sai", + "san", + "sang", + "sao", + "se", + "sen", + "seng", + "sh", + "sha", + "shai", + "shan", + "shang", + "shao", + "she", + "shei", + "shen", + "sheng", + "shi", + "shou", + "shu", + "shua", + "shuai", + "shuan", + "shuang", + "shui", + "shun", + "shuo", + "si", + "song", + "sou", + "su", + "suan", + "sui", + "sun", + "suo", + "t", + "ta", + "tai", + "tan", + "tang", + "tao", + "te", + "teng", + "ti", + "tian", + "tiao", + "tie", + "ting", + "tong", + "tou", + "tu", + "tuan", + "tui", + "tun", + "tuo", + "u", + "v", + "w", + "wa", + "wai", + "wan", + "wang", + "wei", + "wen", + "weng", + "wo", + "wu", + "x", + "xi", + "xia", + "xian", + "xiang", + "xiao", + "xie", + "xin", + "xing", + "xiong", + "xiu", + "xu", + "xuan", + "xue", + "xun", + "y", + "ya", + "yai", + "yan", + "yang", + "yao", + "ye", + "yi", + "yin", + "ying", + "yo", + "yong", + "you", + "yu", + "yuan", + "yue", + "yun", + "z", + "za", + "zai", + "zan", + "zang", + "zao", + "ze", + "zei", + "zen", + "zeng", + "zh", + "zha", + "zhai", + "zhan", + "zhang", + "zhao", + "zhe", + "zhei", + "zhen", + "zheng", + "zhi", + "zhong", + "zhou", + "zhu", + "zhua", + "zhuai", + "zhuan", + "zhuang", + "zhui", + "zhun", + "zhuo", + "zi", + "zong", + "zou", + "zu", + "zuan", + "zui", + "zun", + "zuo", +}; diff --git a/src/rust/examples/tests/crud/pinyin_dict/pinyin_set_util.rs b/src/rust/examples/tests/crud/pinyin_dict/pinyin_set_util.rs new file mode 100644 index 000000000..de2867753 --- /dev/null +++ b/src/rust/examples/tests/crud/pinyin_dict/pinyin_set_util.rs @@ -0,0 +1,77 @@ +use crate::crud::pinyin_dict::pinyin_set::PINYIN_SET; + +pub struct PinyinOptions { + pub allow_prefix: bool, + pub allow_initials: bool, +} + +// 单个汉字的拼音最大长度 +const PINYIN_MAX_LENGTH: usize = 6; + +/// 输入不带空格拼音 → 输出 WCDB 可用 token 列表 +pub fn generate_pinyin_tokens(input: &str, options: &PinyinOptions) -> Vec { + let s = input.to_lowercase(); + let chars: Vec = s.chars().collect(); + let n = chars.len(); + let mut result = Vec::new(); + let mut i = 0; + + while i < n { + let mut found = None; + for len in (1..=PINYIN_MAX_LENGTH).rev() { + if i + len <= n { + let slice: String = chars[i..i + len].iter().collect(); + if PINYIN_SET.contains(slice.as_str()) { + found = Some(slice); + i += len; + break; + } + } + } + + if let Some(py) = found { + result.push(py); + } else { + if options.allow_initials { + // 把单个字母当作首字母 token + result.push(chars[i].to_string()); + } else if options.allow_prefix { + // 把剩余部分当作前缀 token + result.push(chars[i..].iter().collect()); + break; + } + i += 1; + } + } + + result +} + +#[cfg(test)] +mod generate_pinyin_tokens_test { + use crate::crud::pinyin_dict::pinyin_set_util::{generate_pinyin_tokens, PinyinOptions}; + + #[test] + fn test() { + let options = PinyinOptions { + allow_prefix: true, + allow_initials: true, + }; + + let input = "zhongqinghuoguo"; // 完整拼音 + let token_vec = generate_pinyin_tokens(input, &options); + assert_eq!(token_vec.join(" "), "zhong qing huo guo"); + + let input = "zqhg"; // 首字母 + let token_vec = generate_pinyin_tokens(input, &options); + assert_eq!(token_vec.join(" "), "z q h g"); + + let input = "zhon"; // 前半部分 + let token_vec = generate_pinyin_tokens(input, &options); + assert_eq!(token_vec.join(" "), "zh o n"); + + let input = "bj"; // 北京缩写 + let token_vec = generate_pinyin_tokens(input, &options); + assert_eq!(token_vec.join(" "), "b j"); + } +} diff --git a/src/rust/examples/tests/crud/pinyin_dict/traditional_chinese_map.rs b/src/rust/examples/tests/crud/pinyin_dict/traditional_chinese_map.rs new file mode 100644 index 000000000..2804e386e --- /dev/null +++ b/src/rust/examples/tests/crud/pinyin_dict/traditional_chinese_map.rs @@ -0,0 +1,4122 @@ +//! Auto-generated file. DO NOT EDIT MANUALLY. +// Generated at: 2025-12-12 02:38:34 UTC +// Source URL: https://raw.githubusercontent.com/BYVoid/OpenCC/refs/heads/master/data/dictionary/TSCharacters.txt +// Total records: 4113 + +use phf::phf_map; + +pub static TRADITIONAL_CHINESE_MAP: phf::Map<&'static str, &'static str> = phf_map! { + "㑮" => "𫝈", + "㑯" => "㑔", + "㑳" => "㑇", + "㑶" => "㐹", + "㒓" => "𠉂", + "㓄" => "𪠟", + "㓨" => "刾", + "㔋" => "𪟎", + "㖮" => "𪠵", + "㗲" => "𠵾", + "㗿" => "𪡛", + "㘉" => "𠰱", + "㘓" => "𪢌", + "㘔" => "𫬐", + "㘚" => "㘎", + "㛝" => "𫝦", + "㜄" => "㚯", + "㜏" => "㛣", + "㜐" => "𫝧", + "㜗" => "𡞋", + "㜢" => "𡞱", + "㜷" => "𡝠", + "㞞" => "𪨊", + "㟺" => "𪩇", + "㠏" => "㟆", + "㠣" => "𫵷", + "㢗" => "𪪑", + "㢝" => "𢋈", + "㥮" => "㤘", + "㦎" => "𢛯", + "㦛" => "𢗓", + "㦞" => "𪫷", + "㨻" => "𪮃", + "㩋" => "𪮋", + "㩜" => "㨫", + "㩳" => "㧐", + "㩵" => "擜", + "㪎" => "𪯋", + "㯤" => "𣘐", + "㰙" => "𣗙", + "㵗" => "𣳆", + "㵾" => "𪷍", + "㶆" => "𫞛", + "㷍" => "𤆢", + "㷿" => "𤈷", + "㸇" => "𤎺", + "㹽" => "𫞣", + "㺏" => "𤠋", + "㺜" => "𪺻", + "㻶" => "𪼋", + "㿖" => "𪽮", + "㿗" => "𤻊", + "㿧" => "𤽯", + "䀉" => "𥁢", + "䀹" => "𥅴", + "䁪" => "𥇢", + "䁻" => "䀥", + "䂎" => "𥎝", + "䃮" => "鿎", + "䅐" => "𫀨", + "䅳" => "𫀬", + "䆉" => "𫁂", + "䉑" => "𫁲", + "䉙" => "𥬀", + "䉬" => "𫂈", + "䉲" => "𥮜", + "䉶" => "𫁷", + "䊭" => "𥺅", + "䊷" => "䌶", + "䊺" => "𫄚", + "䋃" => "𫄜", + "䋔" => "𫄞", + "䋙" => "䌺", + "䋚" => "䌻", + "䋦" => "𫄩", + "䋹" => "䌿", + "䋻" => "䌾", + "䋼" => "𫄮", + "䋿" => "𦈓", + "䌈" => "𦈖", + "䌋" => "𦈘", + "䌖" => "𦈜", + "䌝" => "𦈟", + "䌟" => "𦈞", + "䌥" => "𦈠", + "䌰" => "𦈙", + "䍤" => "𫅅", + "䍦" => "䍠", + "䍽" => "𦍠", + "䎙" => "𫅭", + "䎱" => "䎬", + "䓣" => "𬜯", + "䕤" => "𫟕", + "䕳" => "𦰴", + "䖅" => "𫟑", + "䗅" => "𫊪", + "䗿" => "𧉞", + "䙔" => "𫋲", + "䙡" => "䙌", + "䙱" => "𧜭", + "䚩" => "𫌯", + "䛄" => "𫍠", + "䛳" => "𫍫", + "䜀" => "䜧", + "䜖" => "𫟢", + "䝭" => "𫎧", + "䝻" => "𧹕", + "䝼" => "䞍", + "䞈" => "𧹑", + "䞋" => "𫎪", + "䞓" => "𫎭", + "䟃" => "𫎺", + "䟆" => "𫎳", + "䟐" => "𫎱", + "䠆" => "𫏃", + "䠱" => "𨅛", + "䡐" => "𫟤", + "䡩" => "𫟥", + "䡵" => "𫟦", + "䢨" => "𨑹", + "䤤" => "𫟺", + "䥄" => "𫠀", + "䥇" => "䦂", + "䥑" => "鿏", + "䥕" => "𬭯", + "䥗" => "𫔋", + "䥩" => "𨱖", + "䥯" => "𫔆", + "䥱" => "䥾", + "䦘" => "𨸄", + "䦛" => "䦶", + "䦟" => "䦷", + "䦯" => "𫔵", + "䦳" => "𨷿", + "䧢" => "𨸟", + "䪊" => "𫖅", + "䪏" => "𩏼", + "䪗" => "𩐀", + "䪘" => "𩏿", + "䪴" => "𫖫", + "䪾" => "𫖬", + "䫀" => "𫖱", + "䫂" => "𫖰", + "䫟" => "𫖲", + "䫴" => "𩖗", + "䫶" => "𫖺", + "䫻" => "𫗇", + "䫾" => "𫠈", + "䬓" => "𫗊", + "䬘" => "𩙮", + "䬝" => "𩙯", + "䬞" => "𩙧", + "䬧" => "𫗟", + "䭀" => "𩠇", + "䭃" => "𩠈", + "䭑" => "𫗱", + "䭔" => "𫗰", + "䭿" => "𩧭", + "䮄" => "𫠊", + "䮝" => "𩧰", + "䮞" => "𩨁", + "䮠" => "𩧿", + "䮫" => "𩨇", + "䮰" => "𫘮", + "䮳" => "𩨏", + "䮾" => "𩧪", + "䯀" => "䯅", + "䯤" => "𩩈", + "䰾" => "鲃", + "䱀" => "𫚐", + "䱁" => "𫚏", + "䱙" => "𩾈", + "䱧" => "𫚠", + "䱬" => "𩾊", + "䱰" => "𩾋", + "䱷" => "䲣", + "䱸" => "𫠑", + "䱽" => "䲝", + "䲁" => "鳚", + "䲅" => "𫚜", + "䲖" => "𩾂", + "䲘" => "鳤", + "䲰" => "𪉂", + "䳜" => "𫛬", + "䳢" => "𫛰", + "䳤" => "𫛮", + "䳧" => "𫛺", + "䳫" => "𫛼", + "䴉" => "鹮", + "䴋" => "𫜅", + "䴬" => "𪎈", + "䴱" => "𫜒", + "䴴" => "𪎋", + "䴽" => "𫜔", + "䵳" => "𪑅", + "䵴" => "𫜙", + "䶕" => "𫜨", + "䶲" => "𫜳", + "丟" => "丢", + "並" => "并", + "乾" => "干", + "亂" => "乱", + "亙" => "亘", + "亞" => "亚", + "佇" => "伫", + "佈" => "布", + "佔" => "占", + "併" => "并", + "來" => "来", + "侖" => "仑", + "侶" => "侣", + "侷" => "局", + "俁" => "俣", + "係" => "系", + "俓" => "𠇹", + "俔" => "伣", + "俠" => "侠", + "俥" => "伡", + "俬" => "私", + "倀" => "伥", + "倆" => "俩", + "倈" => "俫", + "倉" => "仓", + "個" => "个", + "們" => "们", + "倖" => "幸", + "倫" => "伦", + "倲" => "㑈", + "偉" => "伟", + "偑" => "㐽", + "側" => "侧", + "偵" => "侦", + "偽" => "伪", + "傌" => "㐷", + "傑" => "杰", + "傖" => "伧", + "傘" => "伞", + "備" => "备", + "傢" => "家", + "傭" => "佣", + "傯" => "偬", + "傳" => "传", + "傴" => "伛", + "債" => "债", + "傷" => "伤", + "傾" => "倾", + "僂" => "偻", + "僅" => "仅", + "僉" => "佥", + "僑" => "侨", + "僕" => "仆", + "僞" => "伪", + "僤" => "𫢸", + "僥" => "侥", + "僨" => "偾", + "僱" => "雇", + "價" => "价", + "儀" => "仪", + "儁" => "俊", + "儂" => "侬", + "億" => "亿", + "儈" => "侩", + "儉" => "俭", + "儎" => "傤", + "儐" => "傧", + "儔" => "俦", + "儕" => "侪", + "儘" => "尽", + "償" => "偿", + "儣" => "𠆲", + "優" => "优", + "儭" => "𠋆", + "儲" => "储", + "儷" => "俪", + "儸" => "㑩", + "儺" => "傩", + "儻" => "傥", + "儼" => "俨", + "兇" => "凶", + "兌" => "兑", + "兒" => "儿", + "兗" => "兖", + "內" => "内", + "兩" => "两", + "冊" => "册", + "冑" => "胄", + "冪" => "幂", + "凈" => "净", + "凍" => "冻", + "凙" => "𪞝", + "凜" => "凛", + "凱" => "凯", + "別" => "别", + "刪" => "删", + "剄" => "刭", + "則" => "则", + "剋" => "克", + "剎" => "刹", + "剗" => "刬", + "剛" => "刚", + "剝" => "剥", + "剮" => "剐", + "剴" => "剀", + "創" => "创", + "剷" => "铲", + "剾" => "𠛅", + "劃" => "划", + "劇" => "剧", + "劉" => "刘", + "劊" => "刽", + "劌" => "刿", + "劍" => "剑", + "劏" => "㓥", + "劑" => "剂", + "劚" => "㔉", + "勁" => "劲", + "勑" => "𠡠", + "動" => "动", + "務" => "务", + "勛" => "勋", + "勝" => "胜", + "勞" => "劳", + "勢" => "势", + "勣" => "𪟝", + "勩" => "勚", + "勱" => "劢", + "勳" => "勋", + "勵" => "励", + "勸" => "劝", + "勻" => "匀", + "匭" => "匦", + "匯" => "汇", + "匱" => "匮", + "區" => "区", + "協" => "协", + "卹" => "恤", + "卻" => "却", + "卽" => "即", + "厙" => "厍", + "厠" => "厕", + "厤" => "历", + "厭" => "厌", + "厲" => "厉", + "厴" => "厣", + "參" => "参", + "叄" => "叁", + "叢" => "丛", + "吒" => "咤", + "吳" => "吴", + "吶" => "呐", + "呂" => "吕", + "咼" => "呙", + "員" => "员", + "哯" => "𠯟", + "唄" => "呗", + "唓" => "𪠳", + "唸" => "念", + "問" => "问", + "啓" => "启", + "啞" => "哑", + "啟" => "启", + "啢" => "唡", + "喎" => "㖞", + "喚" => "唤", + "喪" => "丧", + "喫" => "吃", + "喬" => "乔", + "單" => "单", + "喲" => "哟", + "嗆" => "呛", + "嗇" => "啬", + "嗊" => "唝", + "嗎" => "吗", + "嗚" => "呜", + "嗩" => "唢", + "嗰" => "𠮶", + "嗶" => "哔", + "嗹" => "𪡏", + "嘆" => "叹", + "嘍" => "喽", + "嘓" => "啯", + "嘔" => "呕", + "嘖" => "啧", + "嘗" => "尝", + "嘜" => "唛", + "嘩" => "哗", + "嘪" => "𪡃", + "嘮" => "唠", + "嘯" => "啸", + "嘰" => "叽", + "嘳" => "𪡞", + "嘵" => "哓", + "嘸" => "呒", + "嘺" => "𪡀", + "嘽" => "啴", + "噁" => "恶", + "噅" => "𠯠", + "噓" => "嘘", + "噚" => "㖊", + "噝" => "咝", + "噞" => "𪡋", + "噠" => "哒", + "噥" => "哝", + "噦" => "哕", + "噯" => "嗳", + "噲" => "哙", + "噴" => "喷", + "噸" => "吨", + "噹" => "当", + "嚀" => "咛", + "嚇" => "吓", + "嚌" => "哜", + "嚐" => "尝", + "嚕" => "噜", + "嚙" => "啮", + "嚛" => "𪠸", + "嚥" => "咽", + "嚦" => "呖", + "嚧" => "𠰷", + "嚨" => "咙", + "嚮" => "向", + "嚲" => "亸", + "嚳" => "喾", + "嚴" => "严", + "嚶" => "嘤", + "嚽" => "𪢕", + "囀" => "啭", + "囁" => "嗫", + "囂" => "嚣", + "囃" => "𠱞", + "囅" => "冁", + "囈" => "呓", + "囉" => "啰", + "囌" => "苏", + "囑" => "嘱", + "囒" => "𪢠", + "囪" => "囱", + "圇" => "囵", + "國" => "国", + "圍" => "围", + "園" => "园", + "圓" => "圆", + "圖" => "图", + "團" => "团", + "圞" => "𪢮", + "垻" => "坝", + "埡" => "垭", + "埨" => "𫭢", + "埬" => "𪣆", + "埰" => "采", + "執" => "执", + "堅" => "坚", + "堊" => "垩", + "堖" => "垴", + "堚" => "𪣒", + "堝" => "埚", + "堯" => "尧", + "報" => "报", + "場" => "场", + "塊" => "块", + "塋" => "茔", + "塏" => "垲", + "塒" => "埘", + "塗" => "涂", + "塚" => "冢", + "塢" => "坞", + "塤" => "埙", + "塵" => "尘", + "塸" => "𫭟", + "塹" => "堑", + "塿" => "𪣻", + "墊" => "垫", + "墜" => "坠", + "墠" => "𫮃", + "墮" => "堕", + "墰" => "坛", + "墲" => "𪢸", + "墳" => "坟", + "墶" => "垯", + "墻" => "墙", + "墾" => "垦", + "壇" => "坛", + "壈" => "𡒄", + "壋" => "垱", + "壎" => "埙", + "壓" => "压", + "壗" => "𡋤", + "壘" => "垒", + "壙" => "圹", + "壚" => "垆", + "壜" => "坛", + "壞" => "坏", + "壟" => "垄", + "壠" => "垅", + "壢" => "坜", + "壣" => "𪤚", + "壩" => "坝", + "壪" => "塆", + "壯" => "壮", + "壺" => "壶", + "壼" => "壸", + "壽" => "寿", + "夠" => "够", + "夢" => "梦", + "夥" => "伙", + "夾" => "夹", + "奐" => "奂", + "奧" => "奥", + "奩" => "奁", + "奪" => "夺", + "奬" => "奖", + "奮" => "奋", + "奼" => "姹", + "妝" => "妆", + "姍" => "姗", + "姦" => "奸", + "娙" => "𫰛", + "娛" => "娱", + "婁" => "娄", + "婡" => "𫝫", + "婦" => "妇", + "婭" => "娅", + "媈" => "𫝨", + "媧" => "娲", + "媯" => "妫", + "媰" => "㛀", + "媼" => "媪", + "媽" => "妈", + "嫋" => "袅", + "嫗" => "妪", + "嫵" => "妩", + "嫺" => "娴", + "嫻" => "娴", + "嫿" => "婳", + "嬀" => "妫", + "嬃" => "媭", + "嬇" => "𫝬", + "嬈" => "娆", + "嬋" => "婵", + "嬌" => "娇", + "嬙" => "嫱", + "嬡" => "嫒", + "嬣" => "𪥰", + "嬤" => "嬷", + "嬦" => "𫝩", + "嬪" => "嫔", + "嬰" => "婴", + "嬸" => "婶", + "嬻" => "𪥿", + "孃" => "娘", + "孄" => "𫝮", + "孆" => "𫝭", + "孇" => "𪥫", + "孋" => "㛤", + "孌" => "娈", + "孎" => "𡠟", + "孫" => "孙", + "學" => "学", + "孻" => "𡥧", + "孾" => "𪧀", + "孿" => "孪", + "宮" => "宫", + "寀" => "采", + "寠" => "𪧘", + "寢" => "寝", + "實" => "实", + "寧" => "宁", + "審" => "审", + "寫" => "写", + "寬" => "宽", + "寵" => "宠", + "寶" => "宝", + "將" => "将", + "專" => "专", + "尋" => "寻", + "對" => "对", + "導" => "导", + "尷" => "尴", + "屆" => "届", + "屍" => "尸", + "屓" => "屃", + "屜" => "屉", + "屢" => "屡", + "層" => "层", + "屨" => "屦", + "屩" => "𪨗", + "屬" => "属", + "岡" => "冈", + "峯" => "峰", + "峴" => "岘", + "島" => "岛", + "峽" => "峡", + "崍" => "崃", + "崑" => "昆", + "崗" => "岗", + "崙" => "仑", + "崢" => "峥", + "崬" => "岽", + "嵐" => "岚", + "嵗" => "岁", + "嵼" => "𡶴", + "嵽" => "𫶇", + "嵾" => "㟥", + "嶁" => "嵝", + "嶄" => "崭", + "嶇" => "岖", + "嶈" => "𡺃", + "嶔" => "嵚", + "嶗" => "崂", + "嶘" => "𡺄", + "嶠" => "峤", + "嶢" => "峣", + "嶧" => "峄", + "嶨" => "峃", + "嶮" => "崄", + "嶸" => "嵘", + "嶹" => "𫝵", + "嶺" => "岭", + "嶼" => "屿", + "嶽" => "岳", + "巊" => "𪩎", + "巋" => "岿", + "巒" => "峦", + "巔" => "巅", + "巖" => "岩", + "巗" => "𪨷", + "巘" => "𪩘", + "巰" => "巯", + "巹" => "卺", + "帥" => "帅", + "師" => "师", + "帳" => "帐", + "帶" => "带", + "幀" => "帧", + "幃" => "帏", + "幓" => "㡎", + "幗" => "帼", + "幘" => "帻", + "幝" => "𪩷", + "幟" => "帜", + "幣" => "币", + "幩" => "𪩸", + "幫" => "帮", + "幬" => "帱", + "幹" => "干", + "幾" => "几", + "庫" => "库", + "廁" => "厕", + "廂" => "厢", + "廄" => "厩", + "廈" => "厦", + "廎" => "庼", + "廕" => "荫", + "廚" => "厨", + "廝" => "厮", + "廞" => "𫷷", + "廟" => "庙", + "廠" => "厂", + "廡" => "庑", + "廢" => "废", + "廣" => "广", + "廧" => "𪪞", + "廩" => "廪", + "廬" => "庐", + "廳" => "厅", + "弒" => "弑", + "弔" => "吊", + "弳" => "弪", + "張" => "张", + "強" => "强", + "彃" => "𪪼", + "彄" => "𫸩", + "彆" => "别", + "彈" => "弹", + "彌" => "弥", + "彎" => "弯", + "彔" => "录", + "彙" => "汇", + "彠" => "彟", + "彥" => "彦", + "彫" => "雕", + "彲" => "彨", + "彷" => "彷", + "彿" => "佛", + "後" => "后", + "徑" => "径", + "從" => "从", + "徠" => "徕", + "復" => "复", + "徵" => "征", + "徹" => "彻", + "徿" => "𪫌", + "恆" => "恒", + "恥" => "耻", + "悅" => "悦", + "悞" => "悮", + "悵" => "怅", + "悶" => "闷", + "悽" => "凄", + "惡" => "恶", + "惱" => "恼", + "惲" => "恽", + "惻" => "恻", + "愛" => "爱", + "愜" => "惬", + "愨" => "悫", + "愴" => "怆", + "愷" => "恺", + "愻" => "𢙏", + "愾" => "忾", + "慄" => "栗", + "態" => "态", + "慍" => "愠", + "慘" => "惨", + "慚" => "惭", + "慟" => "恸", + "慣" => "惯", + "慤" => "悫", + "慪" => "怄", + "慫" => "怂", + "慮" => "虑", + "慳" => "悭", + "慶" => "庆", + "慺" => "㥪", + "慼" => "戚", + "慾" => "欲", + "憂" => "忧", + "憊" => "惫", + "憐" => "怜", + "憑" => "凭", + "憒" => "愦", + "憖" => "慭", + "憚" => "惮", + "憢" => "𢙒", + "憤" => "愤", + "憫" => "悯", + "憮" => "怃", + "憲" => "宪", + "憶" => "忆", + "憸" => "𪫺", + "憹" => "𢙐", + "懀" => "𢙓", + "懇" => "恳", + "應" => "应", + "懌" => "怿", + "懍" => "懔", + "懎" => "𢠁", + "懞" => "蒙", + "懟" => "怼", + "懣" => "懑", + "懤" => "㤽", + "懨" => "恹", + "懲" => "惩", + "懶" => "懒", + "懷" => "怀", + "懸" => "悬", + "懺" => "忏", + "懼" => "惧", + "懾" => "慑", + "戀" => "恋", + "戇" => "戆", + "戔" => "戋", + "戧" => "戗", + "戩" => "戬", + "戰" => "战", + "戱" => "戯", + "戲" => "戏", + "戶" => "户", + "拋" => "抛", + "挩" => "捝", + "挱" => "挲", + "挾" => "挟", + "捨" => "舍", + "捫" => "扪", + "捱" => "挨", + "捲" => "卷", + "掃" => "扫", + "掄" => "抡", + "掆" => "㧏", + "掗" => "挜", + "掙" => "挣", + "掚" => "𪭵", + "掛" => "挂", + "採" => "采", + "揀" => "拣", + "揚" => "扬", + "換" => "换", + "揮" => "挥", + "揯" => "搄", + "損" => "损", + "搖" => "摇", + "搗" => "捣", + "搵" => "揾", + "搶" => "抢", + "摋" => "𢫬", + "摐" => "𪭢", + "摑" => "掴", + "摜" => "掼", + "摟" => "搂", + "摯" => "挚", + "摳" => "抠", + "摶" => "抟", + "摺" => "折", + "摻" => "掺", + "撈" => "捞", + "撊" => "𪭾", + "撏" => "挦", + "撐" => "撑", + "撓" => "挠", + "撝" => "㧑", + "撟" => "挢", + "撣" => "掸", + "撥" => "拨", + "撧" => "𪮖", + "撫" => "抚", + "撲" => "扑", + "撳" => "揿", + "撻" => "挞", + "撾" => "挝", + "撿" => "捡", + "擁" => "拥", + "擄" => "掳", + "擇" => "择", + "擊" => "击", + "擋" => "挡", + "擓" => "㧟", + "擔" => "担", + "據" => "据", + "擟" => "𪭧", + "擠" => "挤", + "擣" => "捣", + "擫" => "𢬍", + "擬" => "拟", + "擯" => "摈", + "擰" => "拧", + "擱" => "搁", + "擲" => "掷", + "擴" => "扩", + "擷" => "撷", + "擺" => "摆", + "擻" => "擞", + "擼" => "撸", + "擽" => "㧰", + "擾" => "扰", + "攄" => "摅", + "攆" => "撵", + "攋" => "𪮶", + "攏" => "拢", + "攔" => "拦", + "攖" => "撄", + "攙" => "搀", + "攛" => "撺", + "攜" => "携", + "攝" => "摄", + "攢" => "攒", + "攣" => "挛", + "攤" => "摊", + "攪" => "搅", + "攬" => "揽", + "敎" => "教", + "敓" => "敚", + "敗" => "败", + "敘" => "叙", + "敵" => "敌", + "數" => "数", + "斂" => "敛", + "斃" => "毙", + "斅" => "𢽾", + "斆" => "敩", + "斕" => "斓", + "斬" => "斩", + "斷" => "断", + "斸" => "𣃁", + "於" => "于", + "旂" => "旗", + "旣" => "既", + "昇" => "升", + "時" => "时", + "晉" => "晋", + "晛" => "𬀪", + "晝" => "昼", + "暈" => "晕", + "暉" => "晖", + "暐" => "𬀩", + "暘" => "旸", + "暢" => "畅", + "暫" => "暂", + "曄" => "晔", + "曆" => "历", + "曇" => "昙", + "曉" => "晓", + "曊" => "𪰶", + "曏" => "向", + "曖" => "暧", + "曠" => "旷", + "曥" => "𣆐", + "曨" => "昽", + "曬" => "晒", + "書" => "书", + "會" => "会", + "朥" => "𦛨", + "朧" => "胧", + "朮" => "术", + "東" => "东", + "枴" => "拐", + "柵" => "栅", + "柺" => "拐", + "査" => "查", + "桱" => "𣐕", + "桿" => "杆", + "梔" => "栀", + "梖" => "𪱷", + "梘" => "枧", + "梜" => "𬂩", + "條" => "条", + "梟" => "枭", + "梲" => "棁", + "棄" => "弃", + "棊" => "棋", + "棖" => "枨", + "棗" => "枣", + "棟" => "栋", + "棡" => "㭎", + "棧" => "栈", + "棲" => "栖", + "棶" => "梾", + "椏" => "桠", + "椲" => "㭏", + "楇" => "𣒌", + "楊" => "杨", + "楓" => "枫", + "楨" => "桢", + "業" => "业", + "極" => "极", + "榘" => "矩", + "榦" => "干", + "榪" => "杩", + "榮" => "荣", + "榲" => "榅", + "榿" => "桤", + "構" => "构", + "槍" => "枪", + "槓" => "杠", + "槤" => "梿", + "槧" => "椠", + "槨" => "椁", + "槫" => "𣏢", + "槮" => "椮", + "槳" => "桨", + "槶" => "椢", + "槼" => "椝", + "樁" => "桩", + "樂" => "乐", + "樅" => "枞", + "樑" => "梁", + "樓" => "楼", + "標" => "标", + "樞" => "枢", + "樠" => "𣗊", + "樢" => "㭤", + "樣" => "样", + "樤" => "𣔌", + "樧" => "榝", + "樫" => "㭴", + "樳" => "桪", + "樸" => "朴", + "樹" => "树", + "樺" => "桦", + "樿" => "椫", + "橈" => "桡", + "橋" => "桥", + "機" => "机", + "橢" => "椭", + "橫" => "横", + "橯" => "𣓿", + "檁" => "檩", + "檉" => "柽", + "檔" => "档", + "檜" => "桧", + "檟" => "槚", + "檢" => "检", + "檣" => "樯", + "檭" => "𣘴", + "檮" => "梼", + "檯" => "台", + "檳" => "槟", + "檵" => "𪲛", + "檸" => "柠", + "檻" => "槛", + "櫃" => "柜", + "櫅" => "𪲎", + "櫍" => "𬃊", + "櫓" => "橹", + "櫚" => "榈", + "櫛" => "栉", + "櫝" => "椟", + "櫞" => "橼", + "櫟" => "栎", + "櫠" => "𪲮", + "櫥" => "橱", + "櫧" => "槠", + "櫨" => "栌", + "櫪" => "枥", + "櫫" => "橥", + "櫬" => "榇", + "櫱" => "蘖", + "櫳" => "栊", + "櫸" => "榉", + "櫻" => "樱", + "欄" => "栏", + "欅" => "榉", + "欇" => "𪳍", + "權" => "权", + "欍" => "𣐤", + "欏" => "椤", + "欐" => "𪲔", + "欑" => "𪴙", + "欒" => "栾", + "欓" => "𣗋", + "欖" => "榄", + "欘" => "𣚚", + "欞" => "棂", + "欽" => "钦", + "歎" => "叹", + "歐" => "欧", + "歟" => "欤", + "歡" => "欢", + "歲" => "岁", + "歷" => "历", + "歸" => "归", + "歿" => "殁", + "殘" => "残", + "殞" => "殒", + "殢" => "𣨼", + "殤" => "殇", + "殨" => "㱮", + "殫" => "殚", + "殭" => "僵", + "殮" => "殓", + "殯" => "殡", + "殰" => "㱩", + "殲" => "歼", + "殺" => "杀", + "殻" => "壳", + "殼" => "壳", + "毀" => "毁", + "毆" => "殴", + "毊" => "𪵑", + "毿" => "毵", + "氂" => "牦", + "氈" => "毡", + "氌" => "氇", + "氣" => "气", + "氫" => "氢", + "氬" => "氩", + "氭" => "𣱝", + "氳" => "氲", + "氾" => "泛", + "汎" => "泛", + "汙" => "污", + "決" => "决", + "沒" => "没", + "沖" => "冲", + "況" => "况", + "泝" => "溯", + "洩" => "泄", + "洶" => "汹", + "浹" => "浃", + "浿" => "𬇙", + "涇" => "泾", + "涗" => "涚", + "涼" => "凉", + "淒" => "凄", + "淚" => "泪", + "淥" => "渌", + "淨" => "净", + "淩" => "凌", + "淪" => "沦", + "淵" => "渊", + "淶" => "涞", + "淺" => "浅", + "渙" => "涣", + "減" => "减", + "渢" => "沨", + "渦" => "涡", + "測" => "测", + "渾" => "浑", + "湊" => "凑", + "湋" => "𣲗", + "湞" => "浈", + "湧" => "涌", + "湯" => "汤", + "溈" => "沩", + "準" => "准", + "溝" => "沟", + "溡" => "𪶄", + "溫" => "温", + "溮" => "浉", + "溳" => "涢", + "溼" => "湿", + "滄" => "沧", + "滅" => "灭", + "滌" => "涤", + "滎" => "荥", + "滙" => "汇", + "滬" => "沪", + "滯" => "滞", + "滲" => "渗", + "滷" => "卤", + "滸" => "浒", + "滻" => "浐", + "滾" => "滚", + "滿" => "满", + "漁" => "渔", + "漊" => "溇", + "漍" => "𬇹", + "漚" => "沤", + "漢" => "汉", + "漣" => "涟", + "漬" => "渍", + "漲" => "涨", + "漵" => "溆", + "漸" => "渐", + "漿" => "浆", + "潁" => "颍", + "潑" => "泼", + "潔" => "洁", + "潕" => "𣲘", + "潙" => "沩", + "潚" => "㴋", + "潛" => "潜", + "潣" => "𫞗", + "潤" => "润", + "潯" => "浔", + "潰" => "溃", + "潷" => "滗", + "潿" => "涠", + "澀" => "涩", + "澅" => "𣶩", + "澆" => "浇", + "澇" => "涝", + "澐" => "沄", + "澗" => "涧", + "澠" => "渑", + "澤" => "泽", + "澦" => "滪", + "澩" => "泶", + "澫" => "𬇕", + "澬" => "𫞚", + "澮" => "浍", + "澱" => "淀", + "澾" => "㳠", + "濁" => "浊", + "濃" => "浓", + "濄" => "㳡", + "濆" => "𣸣", + "濕" => "湿", + "濘" => "泞", + "濚" => "溁", + "濛" => "蒙", + "濜" => "浕", + "濟" => "济", + "濤" => "涛", + "濧" => "㳔", + "濫" => "滥", + "濰" => "潍", + "濱" => "滨", + "濺" => "溅", + "濼" => "泺", + "濾" => "滤", + "濿" => "𪵱", + "瀂" => "澛", + "瀃" => "𣽷", + "瀅" => "滢", + "瀆" => "渎", + "瀇" => "㲿", + "瀉" => "泻", + "瀋" => "沈", + "瀏" => "浏", + "瀕" => "濒", + "瀘" => "泸", + "瀝" => "沥", + "瀟" => "潇", + "瀠" => "潆", + "瀦" => "潴", + "瀧" => "泷", + "瀨" => "濑", + "瀰" => "弥", + "瀲" => "潋", + "瀾" => "澜", + "灃" => "沣", + "灄" => "滠", + "灍" => "𫞝", + "灑" => "洒", + "灒" => "𪷽", + "灕" => "漓", + "灘" => "滩", + "灙" => "𣺼", + "灝" => "灏", + "灡" => "㳕", + "灣" => "湾", + "灤" => "滦", + "灧" => "滟", + "灩" => "滟", + "災" => "灾", + "為" => "为", + "烏" => "乌", + "烴" => "烃", + "無" => "无", + "煇" => "𪸩", + "煉" => "炼", + "煒" => "炜", + "煙" => "烟", + "煢" => "茕", + "煥" => "焕", + "煩" => "烦", + "煬" => "炀", + "煱" => "㶽", + "熂" => "𪸕", + "熅" => "煴", + "熉" => "𤈶", + "熌" => "𤇄", + "熒" => "荧", + "熓" => "𤆡", + "熗" => "炝", + "熚" => "𤇹", + "熡" => "𤋏", + "熰" => "𬉼", + "熱" => "热", + "熲" => "颎", + "熾" => "炽", + "燀" => "𬊤", + "燁" => "烨", + "燈" => "灯", + "燉" => "炖", + "燒" => "烧", + "燖" => "𬊈", + "燙" => "烫", + "燜" => "焖", + "營" => "营", + "燦" => "灿", + "燬" => "毁", + "燭" => "烛", + "燴" => "烩", + "燶" => "㶶", + "燻" => "熏", + "燼" => "烬", + "燾" => "焘", + "爃" => "𫞡", + "爄" => "𤇃", + "爇" => "𦶟", + "爍" => "烁", + "爐" => "炉", + "爖" => "𤇭", + "爛" => "烂", + "爥" => "𪹳", + "爧" => "𫞠", + "爭" => "争", + "爲" => "为", + "爺" => "爷", + "爾" => "尔", + "牀" => "床", + "牆" => "墙", + "牘" => "牍", + "牴" => "牴", + "牽" => "牵", + "犖" => "荦", + "犛" => "牦", + "犞" => "𪺭", + "犢" => "犊", + "犧" => "牺", + "狀" => "状", + "狹" => "狭", + "狽" => "狈", + "猌" => "𪺽", + "猙" => "狰", + "猶" => "犹", + "猻" => "狲", + "獁" => "犸", + "獃" => "呆", + "獄" => "狱", + "獅" => "狮", + "獊" => "𪺷", + "獎" => "奖", + "獨" => "独", + "獩" => "𤞃", + "獪" => "狯", + "獫" => "猃", + "獮" => "狝", + "獰" => "狞", + "獱" => "㺍", + "獲" => "获", + "獵" => "猎", + "獷" => "犷", + "獸" => "兽", + "獺" => "獭", + "獻" => "献", + "獼" => "猕", + "玀" => "猡", + "玁" => "𤞤", + "珼" => "𫞥", + "現" => "现", + "琱" => "雕", + "琺" => "珐", + "琿" => "珲", + "瑋" => "玮", + "瑒" => "玚", + "瑣" => "琐", + "瑤" => "瑶", + "瑩" => "莹", + "瑪" => "玛", + "瑲" => "玱", + "瑻" => "𪻲", + "瑽" => "𪻐", + "璉" => "琏", + "璊" => "𫞩", + "璕" => "𬍤", + "璗" => "𬍡", + "璝" => "𪻺", + "璡" => "琎", + "璣" => "玑", + "璦" => "瑷", + "璫" => "珰", + "璯" => "㻅", + "環" => "环", + "璵" => "玙", + "璸" => "瑸", + "璼" => "𫞨", + "璽" => "玺", + "璾" => "𫞦", + "璿" => "璇", + "瓄" => "𪻨", + "瓅" => "𬍛", + "瓊" => "琼", + "瓏" => "珑", + "瓔" => "璎", + "瓕" => "𤦀", + "瓚" => "瓒", + "瓛" => "𤩽", + "甌" => "瓯", + "甕" => "瓮", + "產" => "产", + "産" => "产", + "甦" => "苏", + "甯" => "宁", + "畝" => "亩", + "畢" => "毕", + "畫" => "画", + "異" => "异", + "畵" => "画", + "當" => "当", + "畼" => "𪽈", + "疇" => "畴", + "疊" => "叠", + "痙" => "痉", + "痠" => "酸", + "痮" => "𪽪", + "痾" => "疴", + "瘂" => "痖", + "瘋" => "疯", + "瘍" => "疡", + "瘓" => "痪", + "瘞" => "瘗", + "瘡" => "疮", + "瘧" => "疟", + "瘮" => "瘆", + "瘱" => "𪽷", + "瘲" => "疭", + "瘺" => "瘘", + "瘻" => "瘘", + "療" => "疗", + "癆" => "痨", + "癇" => "痫", + "癉" => "瘅", + "癐" => "𤶊", + "癒" => "愈", + "癘" => "疠", + "癟" => "瘪", + "癡" => "痴", + "癢" => "痒", + "癤" => "疖", + "癥" => "症", + "癧" => "疬", + "癩" => "癞", + "癬" => "癣", + "癭" => "瘿", + "癮" => "瘾", + "癰" => "痈", + "癱" => "瘫", + "癲" => "癫", + "發" => "发", + "皁" => "皂", + "皚" => "皑", + "皟" => "𤾀", + "皰" => "疱", + "皸" => "皲", + "皺" => "皱", + "盃" => "杯", + "盜" => "盗", + "盞" => "盏", + "盡" => "尽", + "監" => "监", + "盤" => "盘", + "盧" => "卢", + "盨" => "𪾔", + "盪" => "荡", + "眝" => "𪾣", + "眞" => "真", + "眥" => "眦", + "眾" => "众", + "睍" => "𪾢", + "睏" => "困", + "睜" => "睁", + "睞" => "睐", + "瞘" => "眍", + "瞜" => "䁖", + "瞞" => "瞒", + "瞤" => "𥆧", + "瞭" => "瞭", + "瞶" => "瞆", + "瞼" => "睑", + "矇" => "蒙", + "矉" => "𪾸", + "矑" => "𪾦", + "矓" => "眬", + "矚" => "瞩", + "矯" => "矫", + "硃" => "朱", + "硜" => "硁", + "硤" => "硖", + "硨" => "砗", + "硯" => "砚", + "碕" => "埼", + "碙" => "𥐻", + "碩" => "硕", + "碭" => "砀", + "碸" => "砜", + "確" => "确", + "碼" => "码", + "碽" => "䂵", + "磑" => "硙", + "磚" => "砖", + "磠" => "硵", + "磣" => "碜", + "磧" => "碛", + "磯" => "矶", + "磽" => "硗", + "磾" => "䃅", + "礄" => "硚", + "礆" => "硷", + "礎" => "础", + "礐" => "𬒈", + "礒" => "𥐟", + "礙" => "碍", + "礦" => "矿", + "礪" => "砺", + "礫" => "砾", + "礬" => "矾", + "礮" => "𪿫", + "礱" => "砻", + "祇" => "祇", + "祕" => "秘", + "祿" => "禄", + "禍" => "祸", + "禎" => "祯", + "禕" => "祎", + "禡" => "祃", + "禦" => "御", + "禪" => "禅", + "禮" => "礼", + "禰" => "祢", + "禱" => "祷", + "禿" => "秃", + "秈" => "籼", + "稅" => "税", + "稈" => "秆", + "稏" => "䅉", + "稜" => "棱", + "稟" => "禀", + "種" => "种", + "稱" => "称", + "穀" => "谷", + "穇" => "䅟", + "穌" => "稣", + "積" => "积", + "穎" => "颖", + "穠" => "秾", + "穡" => "穑", + "穢" => "秽", + "穩" => "稳", + "穫" => "获", + "穭" => "穞", + "窩" => "窝", + "窪" => "洼", + "窮" => "穷", + "窯" => "窑", + "窵" => "窎", + "窶" => "窭", + "窺" => "窥", + "竄" => "窜", + "竅" => "窍", + "竇" => "窦", + "竈" => "灶", + "竊" => "窃", + "竚" => "𥩟", + "竪" => "竖", + "竱" => "𫁟", + "競" => "竞", + "筆" => "笔", + "筍" => "笋", + "筧" => "笕", + "筴" => "䇲", + "箇" => "个", + "箋" => "笺", + "箏" => "筝", + "節" => "节", + "範" => "范", + "築" => "筑", + "篋" => "箧", + "篔" => "筼", + "篘" => "𥬠", + "篠" => "筿", + "篢" => "𬕂", + "篤" => "笃", + "篩" => "筛", + "篳" => "筚", + "篸" => "𥮾", + "簀" => "箦", + "簂" => "𫂆", + "簍" => "篓", + "簑" => "蓑", + "簞" => "箪", + "簡" => "简", + "簢" => "𫂃", + "簣" => "篑", + "簫" => "箫", + "簹" => "筜", + "簽" => "签", + "簾" => "帘", + "籃" => "篮", + "籅" => "𥫣", + "籋" => "𥬞", + "籌" => "筹", + "籔" => "䉤", + "籙" => "箓", + "籛" => "篯", + "籜" => "箨", + "籟" => "籁", + "籠" => "笼", + "籤" => "签", + "籩" => "笾", + "籪" => "簖", + "籬" => "篱", + "籮" => "箩", + "籲" => "吁", + "粵" => "粤", + "糉" => "粽", + "糝" => "糁", + "糞" => "粪", + "糧" => "粮", + "糰" => "团", + "糲" => "粝", + "糴" => "籴", + "糶" => "粜", + "糹" => "纟", + "糺" => "𫄙", + "糾" => "纠", + "紀" => "纪", + "紂" => "纣", + "紃" => "𬘓", + "約" => "约", + "紅" => "红", + "紆" => "纡", + "紇" => "纥", + "紈" => "纨", + "紉" => "纫", + "紋" => "纹", + "納" => "纳", + "紐" => "纽", + "紓" => "纾", + "純" => "纯", + "紕" => "纰", + "紖" => "纼", + "紗" => "纱", + "紘" => "纮", + "紙" => "纸", + "級" => "级", + "紛" => "纷", + "紜" => "纭", + "紝" => "纴", + "紞" => "𬘘", + "紟" => "𫄛", + "紡" => "纺", + "紬" => "䌷", + "紮" => "扎", + "細" => "细", + "紱" => "绂", + "紲" => "绁", + "紳" => "绅", + "紵" => "纻", + "紹" => "绍", + "紺" => "绀", + "紼" => "绋", + "紿" => "绐", + "絀" => "绌", + "絁" => "𫄟", + "終" => "终", + "絃" => "弦", + "組" => "组", + "絅" => "䌹", + "絆" => "绊", + "絍" => "𫟃", + "絎" => "绗", + "結" => "结", + "絕" => "绝", + "絙" => "𫄠", + "絛" => "绦", + "絝" => "绔", + "絞" => "绞", + "絡" => "络", + "絢" => "绚", + "絥" => "𫄢", + "給" => "给", + "絧" => "𫄡", + "絨" => "绒", + "絪" => "𬘡", + "絰" => "绖", + "統" => "统", + "絲" => "丝", + "絳" => "绛", + "絶" => "绝", + "絹" => "绢", + "絺" => "𫄨", + "綀" => "𦈌", + "綁" => "绑", + "綃" => "绡", + "綄" => "𬘫", + "綆" => "绠", + "綇" => "𦈋", + "綈" => "绨", + "綉" => "绣", + "綋" => "𫟄", + "綌" => "绤", + "綎" => "𬘩", + "綏" => "绥", + "綐" => "䌼", + "綑" => "捆", + "經" => "经", + "綖" => "𫄧", + "綜" => "综", + "綝" => "𬘭", + "綞" => "缍", + "綟" => "𫄫", + "綠" => "绿", + "綡" => "𫟅", + "綢" => "绸", + "綣" => "绻", + "綧" => "𬘯", + "綪" => "𬘬", + "綫" => "线", + "綬" => "绶", + "維" => "维", + "綯" => "绹", + "綰" => "绾", + "綱" => "纲", + "網" => "网", + "綳" => "绷", + "綴" => "缀", + "綵" => "彩", + "綸" => "纶", + "綹" => "绺", + "綺" => "绮", + "綻" => "绽", + "綽" => "绰", + "綾" => "绫", + "綿" => "绵", + "緄" => "绲", + "緇" => "缁", + "緊" => "紧", + "緋" => "绯", + "緍" => "𦈏", + "緑" => "绿", + "緒" => "绪", + "緓" => "绬", + "緔" => "绱", + "緗" => "缃", + "緘" => "缄", + "緙" => "缂", + "線" => "线", + "緝" => "缉", + "緞" => "缎", + "緟" => "𫟆", + "締" => "缔", + "緡" => "缗", + "緣" => "缘", + "緤" => "𫄬", + "緦" => "缌", + "編" => "编", + "緩" => "缓", + "緬" => "缅", + "緮" => "𫄭", + "緯" => "纬", + "緰" => "𦈕", + "緱" => "缑", + "緲" => "缈", + "練" => "练", + "緶" => "缏", + "緷" => "𦈉", + "緸" => "𦈑", + "緹" => "缇", + "緻" => "致", + "緼" => "缊", + "縈" => "萦", + "縉" => "缙", + "縊" => "缢", + "縋" => "缒", + "縍" => "𫄰", + "縎" => "𦈔", + "縐" => "绉", + "縑" => "缣", + "縕" => "缊", + "縗" => "缞", + "縛" => "缚", + "縝" => "缜", + "縞" => "缟", + "縟" => "缛", + "縣" => "县", + "縧" => "绦", + "縫" => "缝", + "縬" => "𦈚", + "縭" => "缡", + "縮" => "缩", + "縯" => "𬙂", + "縰" => "𫄳", + "縱" => "纵", + "縲" => "缧", + "縳" => "䌸", + "縴" => "纤", + "縵" => "缦", + "縶" => "絷", + "縷" => "缕", + "縸" => "𫄲", + "縹" => "缥", + "縺" => "𦈐", + "總" => "总", + "績" => "绩", + "繂" => "𫄴", + "繃" => "绷", + "繅" => "缫", + "繆" => "缪", + "繈" => "𫄶", + "繏" => "𦈝", + "繐" => "𰬸", + "繒" => "缯", + "繓" => "𦈛", + "織" => "织", + "繕" => "缮", + "繚" => "缭", + "繞" => "绕", + "繟" => "𦈎", + "繡" => "绣", + "繢" => "缋", + "繨" => "𫄤", + "繩" => "绳", + "繪" => "绘", + "繫" => "系", + "繬" => "𫄱", + "繭" => "茧", + "繮" => "缰", + "繯" => "缳", + "繰" => "缲", + "繳" => "缴", + "繶" => "𫄷", + "繷" => "𫄣", + "繸" => "䍁", + "繹" => "绎", + "繻" => "𦈡", + "繼" => "继", + "繽" => "缤", + "繾" => "缱", + "繿" => "䍀", + "纁" => "𫄸", + "纆" => "𬙊", + "纇" => "颣", + "纈" => "缬", + "纊" => "纩", + "續" => "续", + "纍" => "累", + "纏" => "缠", + "纓" => "缨", + "纔" => "才", + "纕" => "𬙋", + "纖" => "纤", + "纗" => "𫄹", + "纘" => "缵", + "纚" => "𫄥", + "纜" => "缆", + "缽" => "钵", + "罃" => "䓨", + "罈" => "坛", + "罌" => "罂", + "罎" => "坛", + "罰" => "罚", + "罵" => "骂", + "罷" => "罢", + "羅" => "罗", + "羆" => "罴", + "羈" => "羁", + "羋" => "芈", + "羣" => "群", + "羥" => "羟", + "羨" => "羡", + "義" => "义", + "羵" => "𫅗", + "羶" => "膻", + "習" => "习", + "翫" => "玩", + "翬" => "翚", + "翹" => "翘", + "翽" => "翙", + "耬" => "耧", + "耮" => "耢", + "聖" => "圣", + "聞" => "闻", + "聯" => "联", + "聰" => "聪", + "聲" => "声", + "聳" => "耸", + "聵" => "聩", + "聶" => "聂", + "職" => "职", + "聹" => "聍", + "聻" => "𫆏", + "聽" => "听", + "聾" => "聋", + "肅" => "肃", + "脅" => "胁", + "脈" => "脉", + "脛" => "胫", + "脣" => "唇", + "脥" => "𣍰", + "脩" => "修", + "脫" => "脱", + "脹" => "胀", + "腎" => "肾", + "腖" => "胨", + "腡" => "脶", + "腦" => "脑", + "腪" => "𣍯", + "腫" => "肿", + "腳" => "脚", + "腸" => "肠", + "膃" => "腽", + "膕" => "腘", + "膚" => "肤", + "膞" => "䏝", + "膠" => "胶", + "膢" => "𦝼", + "膩" => "腻", + "膹" => "𪱥", + "膽" => "胆", + "膾" => "脍", + "膿" => "脓", + "臉" => "脸", + "臍" => "脐", + "臏" => "膑", + "臗" => "𣎑", + "臘" => "腊", + "臚" => "胪", + "臟" => "脏", + "臠" => "脔", + "臢" => "臜", + "臥" => "卧", + "臨" => "临", + "臺" => "台", + "與" => "与", + "興" => "兴", + "舉" => "举", + "舊" => "旧", + "舘" => "馆", + "艙" => "舱", + "艣" => "𫇛", + "艤" => "舣", + "艦" => "舰", + "艫" => "舻", + "艱" => "艰", + "艷" => "艳", + "芻" => "刍", + "苧" => "苎", + "茲" => "兹", + "荊" => "荆", + "莊" => "庄", + "莖" => "茎", + "莢" => "荚", + "莧" => "苋", + "菕" => "𰰨", + "華" => "华", + "菴" => "庵", + "菸" => "烟", + "萇" => "苌", + "萊" => "莱", + "萬" => "万", + "萴" => "荝", + "萵" => "莴", + "葉" => "叶", + "葒" => "荭", + "葝" => "𫈎", + "葤" => "荮", + "葦" => "苇", + "葯" => "药", + "葷" => "荤", + "蒍" => "𫇭", + "蒐" => "搜", + "蒓" => "莼", + "蒔" => "莳", + "蒕" => "蒀", + "蒞" => "莅", + "蒭" => "𫇴", + "蒼" => "苍", + "蓀" => "荪", + "蓆" => "席", + "蓋" => "盖", + "蓧" => "𦰏", + "蓮" => "莲", + "蓯" => "苁", + "蓴" => "莼", + "蓽" => "荜", + "蔄" => "𬜬", + "蔔" => "卜", + "蔘" => "参", + "蔞" => "蒌", + "蔣" => "蒋", + "蔥" => "葱", + "蔦" => "茑", + "蔭" => "荫", + "蔯" => "𫈟", + "蔿" => "𫇭", + "蕁" => "荨", + "蕆" => "蒇", + "蕎" => "荞", + "蕒" => "荬", + "蕓" => "芸", + "蕕" => "莸", + "蕘" => "荛", + "蕝" => "𫈵", + "蕢" => "蒉", + "蕩" => "荡", + "蕪" => "芜", + "蕭" => "萧", + "蕳" => "𫈉", + "蕷" => "蓣", + "蕽" => "𫇽", + "薀" => "蕰", + "薆" => "𫉁", + "薈" => "荟", + "薊" => "蓟", + "薌" => "芗", + "薑" => "姜", + "薔" => "蔷", + "薘" => "荙", + "薟" => "莶", + "薦" => "荐", + "薩" => "萨", + "薳" => "䓕", + "薴" => "苧", + "薵" => "䓓", + "薹" => "苔", + "薺" => "荠", + "藉" => "藉", + "藍" => "蓝", + "藎" => "荩", + "藝" => "艺", + "藥" => "药", + "藪" => "薮", + "藭" => "䓖", + "藴" => "蕴", + "藶" => "苈", + "藷" => "𫉄", + "藹" => "蔼", + "藺" => "蔺", + "蘀" => "萚", + "蘄" => "蕲", + "蘆" => "芦", + "蘇" => "苏", + "蘊" => "蕴", + "蘋" => "苹", + "蘚" => "藓", + "蘞" => "蔹", + "蘟" => "𦻕", + "蘢" => "茏", + "蘭" => "兰", + "蘺" => "蓠", + "蘿" => "萝", + "虆" => "蔂", + "虉" => "𬟁", + "處" => "处", + "虛" => "虚", + "虜" => "虏", + "號" => "号", + "虧" => "亏", + "虯" => "虬", + "蛺" => "蛱", + "蛻" => "蜕", + "蜆" => "蚬", + "蝀" => "𬟽", + "蝕" => "蚀", + "蝟" => "猬", + "蝦" => "虾", + "蝨" => "虱", + "蝸" => "蜗", + "螄" => "蛳", + "螞" => "蚂", + "螢" => "萤", + "螮" => "䗖", + "螻" => "蝼", + "螿" => "螀", + "蟂" => "𫋇", + "蟄" => "蛰", + "蟈" => "蝈", + "蟎" => "螨", + "蟘" => "𫋌", + "蟜" => "𫊸", + "蟣" => "虮", + "蟬" => "蝉", + "蟯" => "蛲", + "蟲" => "虫", + "蟳" => "𫊻", + "蟶" => "蛏", + "蟻" => "蚁", + "蠀" => "𧏗", + "蠁" => "蚃", + "蠅" => "蝇", + "蠆" => "虿", + "蠍" => "蝎", + "蠐" => "蛴", + "蠑" => "蝾", + "蠔" => "蚝", + "蠙" => "𧏖", + "蠟" => "蜡", + "蠣" => "蛎", + "蠦" => "𫊮", + "蠨" => "蟏", + "蠱" => "蛊", + "蠶" => "蚕", + "蠻" => "蛮", + "蠾" => "𧑏", + "衆" => "众", + "衊" => "蔑", + "術" => "术", + "衕" => "同", + "衚" => "胡", + "衛" => "卫", + "衝" => "冲", + "衹" => "衹", + "袞" => "衮", + "裊" => "袅", + "裏" => "里", + "補" => "补", + "裝" => "装", + "裡" => "里", + "製" => "制", + "複" => "复", + "褌" => "裈", + "褘" => "袆", + "褲" => "裤", + "褳" => "裢", + "褸" => "褛", + "褻" => "亵", + "襀" => "𫌀", + "襇" => "裥", + "襉" => "裥", + "襏" => "袯", + "襓" => "𫋹", + "襖" => "袄", + "襗" => "𫋷", + "襘" => "𫋻", + "襝" => "裣", + "襠" => "裆", + "襤" => "褴", + "襪" => "袜", + "襬" => "摆", + "襯" => "衬", + "襰" => "𧝝", + "襲" => "袭", + "襴" => "襕", + "襵" => "𫌇", + "覆" => "覆", + "覈" => "核", + "見" => "见", + "覎" => "觃", + "規" => "规", + "覓" => "觅", + "視" => "视", + "覘" => "觇", + "覛" => "𫌪", + "覡" => "觋", + "覥" => "觍", + "覦" => "觎", + "親" => "亲", + "覬" => "觊", + "覯" => "觏", + "覲" => "觐", + "覷" => "觑", + "覹" => "𫌭", + "覺" => "觉", + "覼" => "𫌨", + "覽" => "览", + "覿" => "觌", + "觀" => "观", + "觴" => "觞", + "觶" => "觯", + "觸" => "触", + "訁" => "讠", + "訂" => "订", + "訃" => "讣", + "計" => "计", + "訊" => "讯", + "訌" => "讧", + "討" => "讨", + "訏" => "𬣙", + "訐" => "讦", + "訑" => "𫍙", + "訒" => "讱", + "訓" => "训", + "訕" => "讪", + "訖" => "讫", + "託" => "托", + "記" => "记", + "訛" => "讹", + "訜" => "𫍛", + "訝" => "讶", + "訞" => "𫍚", + "訟" => "讼", + "訢" => "䜣", + "訣" => "诀", + "訥" => "讷", + "訨" => "𫟞", + "訩" => "讻", + "訪" => "访", + "設" => "设", + "許" => "许", + "訴" => "诉", + "訶" => "诃", + "診" => "诊", + "註" => "注", + "証" => "证", + "詀" => "𧮪", + "詁" => "诂", + "詆" => "诋", + "詊" => "𫟟", + "詎" => "讵", + "詐" => "诈", + "詑" => "𫍡", + "詒" => "诒", + "詓" => "𫍜", + "詔" => "诏", + "評" => "评", + "詖" => "诐", + "詗" => "诇", + "詘" => "诎", + "詛" => "诅", + "詝" => "𬣞", + "詞" => "词", + "詠" => "咏", + "詡" => "诩", + "詢" => "询", + "詣" => "诣", + "試" => "试", + "詩" => "诗", + "詪" => "𬣳", + "詫" => "诧", + "詬" => "诟", + "詭" => "诡", + "詮" => "诠", + "詰" => "诘", + "話" => "话", + "該" => "该", + "詳" => "详", + "詵" => "诜", + "詷" => "𫍣", + "詼" => "诙", + "詿" => "诖", + "誂" => "𫍥", + "誄" => "诔", + "誅" => "诛", + "誆" => "诓", + "誇" => "夸", + "誋" => "𫍪", + "誌" => "志", + "認" => "认", + "誑" => "诳", + "誒" => "诶", + "誕" => "诞", + "誘" => "诱", + "誚" => "诮", + "語" => "语", + "誠" => "诚", + "誡" => "诫", + "誣" => "诬", + "誤" => "误", + "誥" => "诰", + "誦" => "诵", + "誨" => "诲", + "說" => "说", + "誫" => "𫍨", + "説" => "说", + "誰" => "谁", + "課" => "课", + "誳" => "𫍮", + "誴" => "𫟡", + "誶" => "谇", + "誷" => "𫍬", + "誹" => "诽", + "誺" => "𫍧", + "誼" => "谊", + "誾" => "訚", + "調" => "调", + "諂" => "谄", + "諄" => "谆", + "談" => "谈", + "諉" => "诿", + "請" => "请", + "諍" => "诤", + "諏" => "诹", + "諑" => "诼", + "諒" => "谅", + "諓" => "𬣡", + "論" => "论", + "諗" => "谂", + "諛" => "谀", + "諜" => "谍", + "諝" => "谞", + "諞" => "谝", + "諟" => "𬤊", + "諡" => "谥", + "諢" => "诨", + "諣" => "𫍩", + "諤" => "谔", + "諥" => "𫍳", + "諦" => "谛", + "諧" => "谐", + "諫" => "谏", + "諭" => "谕", + "諮" => "咨", + "諯" => "𫍱", + "諰" => "𫍰", + "諱" => "讳", + "諲" => "𬤇", + "諳" => "谙", + "諴" => "𫍯", + "諶" => "谌", + "諷" => "讽", + "諸" => "诸", + "諺" => "谚", + "諼" => "谖", + "諾" => "诺", + "謀" => "谋", + "謁" => "谒", + "謂" => "谓", + "謄" => "誊", + "謅" => "诌", + "謆" => "𫍸", + "謉" => "𫍷", + "謊" => "谎", + "謎" => "谜", + "謏" => "𫍲", + "謐" => "谧", + "謔" => "谑", + "謖" => "谡", + "謗" => "谤", + "謙" => "谦", + "謚" => "谥", + "講" => "讲", + "謝" => "谢", + "謠" => "谣", + "謡" => "谣", + "謨" => "谟", + "謫" => "谪", + "謬" => "谬", + "謭" => "谫", + "謯" => "𫍹", + "謱" => "𫍴", + "謳" => "讴", + "謸" => "𫍵", + "謹" => "谨", + "謾" => "谩", + "譁" => "哗", + "譂" => "𫟠", + "譅" => "𰶎", + "譆" => "𫍻", + "證" => "证", + "譊" => "𫍢", + "譎" => "谲", + "譏" => "讥", + "譑" => "𫍤", + "譓" => "𬤝", + "譖" => "谮", + "識" => "识", + "譙" => "谯", + "譚" => "谭", + "譜" => "谱", + "譞" => "𫍽", + "譟" => "噪", + "譨" => "𫍦", + "譫" => "谵", + "譭" => "毁", + "譯" => "译", + "議" => "议", + "譴" => "谴", + "護" => "护", + "譸" => "诪", + "譽" => "誉", + "譾" => "谫", + "讀" => "读", + "讅" => "谉", + "變" => "变", + "讋" => "詟", + "讌" => "䜩", + "讎" => "雠", + "讒" => "谗", + "讓" => "让", + "讕" => "谰", + "讖" => "谶", + "讚" => "赞", + "讜" => "谠", + "讞" => "谳", + "豈" => "岂", + "豎" => "竖", + "豐" => "丰", + "豔" => "艳", + "豬" => "猪", + "豵" => "𫎆", + "豶" => "豮", + "貓" => "猫", + "貗" => "𫎌", + "貙" => "䝙", + "貝" => "贝", + "貞" => "贞", + "貟" => "贠", + "負" => "负", + "財" => "财", + "貢" => "贡", + "貧" => "贫", + "貨" => "货", + "販" => "贩", + "貪" => "贪", + "貫" => "贯", + "責" => "责", + "貯" => "贮", + "貰" => "贳", + "貲" => "赀", + "貳" => "贰", + "貴" => "贵", + "貶" => "贬", + "買" => "买", + "貸" => "贷", + "貺" => "贶", + "費" => "费", + "貼" => "贴", + "貽" => "贻", + "貿" => "贸", + "賀" => "贺", + "賁" => "贲", + "賂" => "赂", + "賃" => "赁", + "賄" => "贿", + "賅" => "赅", + "資" => "资", + "賈" => "贾", + "賊" => "贼", + "賑" => "赈", + "賒" => "赊", + "賓" => "宾", + "賕" => "赇", + "賙" => "赒", + "賚" => "赉", + "賜" => "赐", + "賝" => "𫎩", + "賞" => "赏", + "賟" => "𧹖", + "賠" => "赔", + "賡" => "赓", + "賢" => "贤", + "賣" => "卖", + "賤" => "贱", + "賦" => "赋", + "賧" => "赕", + "質" => "质", + "賫" => "赍", + "賬" => "账", + "賭" => "赌", + "賰" => "䞐", + "賴" => "赖", + "賵" => "赗", + "賺" => "赚", + "賻" => "赙", + "購" => "购", + "賽" => "赛", + "賾" => "赜", + "贃" => "𧹗", + "贄" => "贽", + "贅" => "赘", + "贇" => "赟", + "贈" => "赠", + "贉" => "𫎫", + "贊" => "赞", + "贋" => "赝", + "贍" => "赡", + "贏" => "赢", + "贐" => "赆", + "贑" => "𫎬", + "贓" => "赃", + "贔" => "赑", + "贖" => "赎", + "贗" => "赝", + "贚" => "𫎦", + "贛" => "赣", + "贜" => "赃", + "赬" => "赪", + "趕" => "赶", + "趙" => "赵", + "趨" => "趋", + "趲" => "趱", + "跡" => "迹", + "踐" => "践", + "踰" => "逾", + "踴" => "踊", + "蹌" => "跄", + "蹔" => "𫏐", + "蹕" => "跸", + "蹟" => "迹", + "蹠" => "跖", + "蹣" => "蹒", + "蹤" => "踪", + "蹳" => "𫏆", + "蹺" => "跷", + "蹻" => "𫏋", + "躂" => "跶", + "躉" => "趸", + "躊" => "踌", + "躋" => "跻", + "躍" => "跃", + "躎" => "䟢", + "躑" => "踯", + "躒" => "跞", + "躓" => "踬", + "躕" => "蹰", + "躘" => "𨀁", + "躚" => "跹", + "躝" => "𨅬", + "躡" => "蹑", + "躥" => "蹿", + "躦" => "躜", + "躪" => "躏", + "軀" => "躯", + "軉" => "𨉗", + "車" => "车", + "軋" => "轧", + "軌" => "轨", + "軍" => "军", + "軏" => "𫐄", + "軑" => "轪", + "軒" => "轩", + "軔" => "轫", + "軕" => "𫐅", + "軗" => "𨐅", + "軛" => "轭", + "軜" => "𫐇", + "軝" => "𬨂", + "軟" => "软", + "軤" => "轷", + "軨" => "𫐉", + "軫" => "轸", + "軬" => "𫐊", + "軲" => "轱", + "軷" => "𫐈", + "軸" => "轴", + "軹" => "轵", + "軺" => "轺", + "軻" => "轲", + "軼" => "轶", + "軾" => "轼", + "軿" => "𫐌", + "較" => "较", + "輄" => "𨐈", + "輅" => "辂", + "輇" => "辁", + "輈" => "辀", + "載" => "载", + "輊" => "轾", + "輋" => "𪨶", + "輒" => "辄", + "輓" => "挽", + "輔" => "辅", + "輕" => "轻", + "輖" => "𫐏", + "輗" => "𫐐", + "輛" => "辆", + "輜" => "辎", + "輝" => "辉", + "輞" => "辋", + "輟" => "辍", + "輢" => "𫐎", + "輥" => "辊", + "輦" => "辇", + "輨" => "𫐑", + "輩" => "辈", + "輪" => "轮", + "輬" => "辌", + "輮" => "𫐓", + "輯" => "辑", + "輳" => "辏", + "輶" => "𬨎", + "輷" => "𫐒", + "輸" => "输", + "輻" => "辐", + "輼" => "辒", + "輾" => "辗", + "輿" => "舆", + "轀" => "辒", + "轂" => "毂", + "轄" => "辖", + "轅" => "辕", + "轆" => "辘", + "轇" => "𫐖", + "轉" => "转", + "轊" => "𫐕", + "轍" => "辙", + "轎" => "轿", + "轐" => "𫐗", + "轔" => "辚", + "轗" => "𫐘", + "轟" => "轰", + "轠" => "𫐙", + "轡" => "辔", + "轢" => "轹", + "轣" => "𫐆", + "轤" => "轳", + "辦" => "办", + "辭" => "辞", + "辮" => "辫", + "辯" => "辩", + "農" => "农", + "迴" => "回", + "逕" => "迳", + "這" => "这", + "連" => "连", + "週" => "周", + "進" => "进", + "遊" => "游", + "運" => "运", + "過" => "过", + "達" => "达", + "違" => "违", + "遙" => "遥", + "遜" => "逊", + "遞" => "递", + "遠" => "远", + "遡" => "溯", + "適" => "适", + "遱" => "𫐷", + "遲" => "迟", + "遷" => "迁", + "選" => "选", + "遺" => "遗", + "遼" => "辽", + "邁" => "迈", + "還" => "还", + "邇" => "迩", + "邊" => "边", + "邏" => "逻", + "邐" => "逦", + "郟" => "郏", + "郵" => "邮", + "鄆" => "郓", + "鄉" => "乡", + "鄒" => "邹", + "鄔" => "邬", + "鄖" => "郧", + "鄟" => "𫑘", + "鄧" => "邓", + "鄩" => "𬩽", + "鄭" => "郑", + "鄰" => "邻", + "鄲" => "郸", + "鄳" => "𫑡", + "鄴" => "邺", + "鄶" => "郐", + "鄺" => "邝", + "酇" => "酂", + "酈" => "郦", + "醃" => "腌", + "醖" => "酝", + "醜" => "丑", + "醞" => "酝", + "醟" => "蒏", + "醣" => "糖", + "醫" => "医", + "醬" => "酱", + "醱" => "酦", + "醲" => "𬪩", + "醶" => "𫑷", + "釀" => "酿", + "釁" => "衅", + "釃" => "酾", + "釅" => "酽", + "釋" => "释", + "釐" => "厘", + "釒" => "钅", + "釓" => "钆", + "釔" => "钇", + "釕" => "钌", + "釗" => "钊", + "釘" => "钉", + "釙" => "钋", + "釚" => "𫟲", + "針" => "针", + "釟" => "𫓥", + "釣" => "钓", + "釤" => "钐", + "釦" => "扣", + "釧" => "钏", + "釨" => "𫓦", + "釩" => "钒", + "釲" => "𫟳", + "釳" => "𨰿", + "釴" => "𬬩", + "釵" => "钗", + "釷" => "钍", + "釹" => "钕", + "釺" => "钎", + "釾" => "䥺", + "釿" => "𬬱", + "鈀" => "钯", + "鈁" => "钫", + "鈃" => "钘", + "鈄" => "钭", + "鈅" => "钥", + "鈆" => "𫓪", + "鈇" => "𫓧", + "鈈" => "钚", + "鈉" => "钠", + "鈋" => "𨱂", + "鈍" => "钝", + "鈎" => "钩", + "鈐" => "钤", + "鈑" => "钣", + "鈒" => "钑", + "鈔" => "钞", + "鈕" => "钮", + "鈖" => "𫟴", + "鈗" => "𫟵", + "鈛" => "𫓨", + "鈞" => "钧", + "鈠" => "𨱁", + "鈡" => "钟", + "鈣" => "钙", + "鈥" => "钬", + "鈦" => "钛", + "鈧" => "钪", + "鈮" => "铌", + "鈯" => "𨱄", + "鈰" => "铈", + "鈲" => "𨱃", + "鈳" => "钶", + "鈴" => "铃", + "鈷" => "钴", + "鈸" => "钹", + "鈹" => "铍", + "鈺" => "钰", + "鈽" => "钸", + "鈾" => "铀", + "鈿" => "钿", + "鉀" => "钾", + "鉁" => "𨱅", + "鉅" => "巨", + "鉆" => "钻", + "鉈" => "铊", + "鉉" => "铉", + "鉊" => "𬬿", + "鉋" => "铇", + "鉍" => "铋", + "鉑" => "铂", + "鉔" => "𫓬", + "鉕" => "钷", + "鉗" => "钳", + "鉚" => "铆", + "鉛" => "铅", + "鉝" => "𫟷", + "鉞" => "钺", + "鉠" => "𫓭", + "鉢" => "钵", + "鉤" => "钩", + "鉥" => "𬬸", + "鉦" => "钲", + "鉧" => "𬭁", + "鉬" => "钼", + "鉭" => "钽", + "鉮" => "𬬹", + "鉳" => "锫", + "鉶" => "铏", + "鉷" => "𫟹", + "鉸" => "铰", + "鉺" => "铒", + "鉻" => "铬", + "鉽" => "𫟸", + "鉾" => "𫓴", + "鉿" => "铪", + "銀" => "银", + "銁" => "𫓲", + "銂" => "𫟻", + "銃" => "铳", + "銅" => "铜", + "銈" => "𫓯", + "銊" => "𫓰", + "銍" => "铚", + "銏" => "𫟶", + "銑" => "铣", + "銓" => "铨", + "銖" => "铢", + "銘" => "铭", + "銚" => "铫", + "銛" => "铦", + "銜" => "衔", + "銠" => "铑", + "銣" => "铷", + "銥" => "铱", + "銦" => "铟", + "銨" => "铵", + "銩" => "铥", + "銪" => "铕", + "銫" => "铯", + "銬" => "铐", + "銱" => "铞", + "銳" => "锐", + "銶" => "𨱇", + "銷" => "销", + "銹" => "锈", + "銻" => "锑", + "銼" => "锉", + "鋁" => "铝", + "鋂" => "𰾄", + "鋃" => "锒", + "鋅" => "锌", + "鋇" => "钡", + "鋉" => "𨱈", + "鋌" => "铤", + "鋏" => "铗", + "鋐" => "𬭎", + "鋒" => "锋", + "鋗" => "𫓶", + "鋙" => "铻", + "鋝" => "锊", + "鋟" => "锓", + "鋠" => "𫓵", + "鋣" => "铘", + "鋤" => "锄", + "鋥" => "锃", + "鋦" => "锔", + "鋨" => "锇", + "鋩" => "铓", + "鋪" => "铺", + "鋭" => "锐", + "鋮" => "铖", + "鋯" => "锆", + "鋰" => "锂", + "鋱" => "铽", + "鋶" => "锍", + "鋸" => "锯", + "鋹" => "𬬮", + "鋼" => "钢", + "錀" => "𬬭", + "錁" => "锞", + "錂" => "𨱋", + "錄" => "录", + "錆" => "锖", + "錇" => "锫", + "錈" => "锩", + "錏" => "铔", + "錐" => "锥", + "錒" => "锕", + "錕" => "锟", + "錘" => "锤", + "錙" => "锱", + "錚" => "铮", + "錛" => "锛", + "錜" => "𫓻", + "錝" => "𫓽", + "錞" => "𬭚", + "錟" => "锬", + "錠" => "锭", + "錡" => "锜", + "錢" => "钱", + "錤" => "𫓹", + "錥" => "𫓾", + "錦" => "锦", + "錨" => "锚", + "錩" => "锠", + "錫" => "锡", + "錮" => "锢", + "錯" => "错", + "録" => "录", + "錳" => "锰", + "錶" => "表", + "錸" => "铼", + "錼" => "镎", + "錽" => "𫓸", + "鍀" => "锝", + "鍁" => "锨", + "鍃" => "锪", + "鍄" => "𨱉", + "鍅" => "钫", + "鍆" => "钔", + "鍇" => "锴", + "鍈" => "锳", + "鍉" => "𫔂", + "鍊" => "炼", + "鍋" => "锅", + "鍍" => "镀", + "鍒" => "𫔄", + "鍔" => "锷", + "鍘" => "铡", + "鍚" => "钖", + "鍛" => "锻", + "鍠" => "锽", + "鍤" => "锸", + "鍥" => "锲", + "鍩" => "锘", + "鍬" => "锹", + "鍭" => "𬭤", + "鍮" => "𨱎", + "鍰" => "锾", + "鍵" => "键", + "鍶" => "锶", + "鍺" => "锗", + "鍼" => "针", + "鍾" => "钟", + "鎂" => "镁", + "鎄" => "锿", + "鎇" => "镅", + "鎈" => "𫟿", + "鎊" => "镑", + "鎌" => "镰", + "鎍" => "𫔅", + "鎓" => "𬭩", + "鎔" => "镕", + "鎖" => "锁", + "鎘" => "镉", + "鎙" => "𫔈", + "鎚" => "锤", + "鎛" => "镈", + "鎝" => "𨱏", + "鎞" => "𫔇", + "鎡" => "镃", + "鎢" => "钨", + "鎣" => "蓥", + "鎦" => "镏", + "鎧" => "铠", + "鎩" => "铩", + "鎪" => "锼", + "鎬" => "镐", + "鎭" => "镇", + "鎮" => "镇", + "鎯" => "𨱍", + "鎰" => "镒", + "鎲" => "镋", + "鎳" => "镍", + "鎵" => "镓", + "鎶" => "鿔", + "鎷" => "𨰾", + "鎸" => "镌", + "鎿" => "镎", + "鏃" => "镞", + "鏆" => "𨱌", + "鏇" => "旋", + "鏈" => "链", + "鏉" => "𨱒", + "鏌" => "镆", + "鏍" => "镙", + "鏏" => "𬭬", + "鏐" => "镠", + "鏑" => "镝", + "鏗" => "铿", + "鏘" => "锵", + "鏚" => "𬭭", + "鏜" => "镗", + "鏝" => "镘", + "鏞" => "镛", + "鏟" => "铲", + "鏡" => "镜", + "鏢" => "镖", + "鏤" => "镂", + "鏥" => "𫔊", + "鏦" => "𫓩", + "鏨" => "錾", + "鏰" => "镚", + "鏵" => "铧", + "鏷" => "镤", + "鏹" => "镪", + "鏺" => "䥽", + "鏻" => "𬭸", + "鏽" => "锈", + "鏾" => "𫔌", + "鐃" => "铙", + "鐄" => "𨱑", + "鐇" => "𫔍", + "鐈" => "𫓱", + "鐋" => "铴", + "鐍" => "𫔎", + "鐎" => "𨱓", + "鐏" => "𨱔", + "鐐" => "镣", + "鐒" => "铹", + "鐓" => "镦", + "鐔" => "镡", + "鐘" => "钟", + "鐙" => "镫", + "鐝" => "镢", + "鐠" => "镨", + "鐥" => "䦅", + "鐦" => "锎", + "鐧" => "锏", + "鐨" => "镄", + "鐩" => "𬭼", + "鐪" => "𫓺", + "鐫" => "镌", + "鐮" => "镰", + "鐯" => "䦃", + "鐲" => "镯", + "鐳" => "镭", + "鐵" => "铁", + "鐶" => "镮", + "鐸" => "铎", + "鐺" => "铛", + "鐼" => "𫔁", + "鐽" => "𫟼", + "鐿" => "镱", + "鑀" => "𰾭", + "鑄" => "铸", + "鑉" => "𫠁", + "鑊" => "镬", + "鑌" => "镔", + "鑑" => "鉴", + "鑒" => "鉴", + "鑔" => "镲", + "鑕" => "锧", + "鑞" => "镴", + "鑠" => "铄", + "鑣" => "镳", + "鑥" => "镥", + "鑪" => "𬬻", + "鑭" => "镧", + "鑰" => "钥", + "鑱" => "镵", + "鑲" => "镶", + "鑴" => "𫔔", + "鑷" => "镊", + "鑹" => "镩", + "鑼" => "锣", + "鑽" => "钻", + "鑾" => "銮", + "鑿" => "凿", + "钁" => "镢", + "钂" => "镋", + "長" => "长", + "門" => "门", + "閂" => "闩", + "閃" => "闪", + "閆" => "闫", + "閈" => "闬", + "閉" => "闭", + "開" => "开", + "閌" => "闶", + "閍" => "𨸂", + "閎" => "闳", + "閏" => "闰", + "閐" => "𨸃", + "閑" => "闲", + "閒" => "闲", + "間" => "间", + "閔" => "闵", + "閗" => "𫔯", + "閘" => "闸", + "閝" => "𫠂", + "閞" => "𫔰", + "閡" => "阂", + "閣" => "阁", + "閤" => "合", + "閥" => "阀", + "閨" => "闺", + "閩" => "闽", + "閫" => "阃", + "閬" => "阆", + "閭" => "闾", + "閱" => "阅", + "閲" => "阅", + "閵" => "𫔴", + "閶" => "阊", + "閹" => "阉", + "閻" => "阎", + "閼" => "阏", + "閽" => "阍", + "閾" => "阈", + "閿" => "阌", + "闃" => "阒", + "闆" => "板", + "闇" => "暗", + "闈" => "闱", + "闉" => "𬮱", + "闊" => "阔", + "闋" => "阕", + "闌" => "阑", + "闍" => "阇", + "闐" => "阗", + "闑" => "𫔶", + "闒" => "阘", + "闓" => "闿", + "闔" => "阖", + "闕" => "阙", + "闖" => "闯", + "關" => "关", + "闞" => "阚", + "闠" => "阓", + "闡" => "阐", + "闢" => "辟", + "闤" => "阛", + "闥" => "闼", + "阪" => "阪", + "陘" => "陉", + "陝" => "陕", + "陞" => "升", + "陣" => "阵", + "陰" => "阴", + "陳" => "陈", + "陸" => "陆", + "陽" => "阳", + "隉" => "陧", + "隊" => "队", + "階" => "阶", + "隑" => "𬮿", + "隕" => "陨", + "際" => "际", + "隤" => "𬯎", + "隨" => "随", + "險" => "险", + "隮" => "𬯀", + "隯" => "陦", + "隱" => "隐", + "隴" => "陇", + "隸" => "隶", + "隻" => "只", + "雋" => "隽", + "雖" => "虽", + "雙" => "双", + "雛" => "雏", + "雜" => "杂", + "雞" => "鸡", + "離" => "离", + "難" => "难", + "雲" => "云", + "電" => "电", + "霑" => "沾", + "霢" => "霡", + "霣" => "𫕥", + "霧" => "雾", + "霼" => "𪵣", + "霽" => "霁", + "靂" => "雳", + "靄" => "霭", + "靆" => "叇", + "靈" => "灵", + "靉" => "叆", + "靚" => "靓", + "靜" => "静", + "靝" => "靔", + "靦" => "腼", + "靧" => "𫖃", + "靨" => "靥", + "鞏" => "巩", + "鞝" => "绱", + "鞦" => "秋", + "鞽" => "鞒", + "鞾" => "𫖇", + "韁" => "缰", + "韃" => "鞑", + "韆" => "千", + "韉" => "鞯", + "韋" => "韦", + "韌" => "韧", + "韍" => "韨", + "韓" => "韩", + "韙" => "韪", + "韚" => "𫠅", + "韛" => "𫖔", + "韜" => "韬", + "韝" => "鞲", + "韞" => "韫", + "韠" => "𫖒", + "韻" => "韵", + "響" => "响", + "頁" => "页", + "頂" => "顶", + "頃" => "顷", + "項" => "项", + "順" => "顺", + "頇" => "顸", + "須" => "须", + "頊" => "顼", + "頌" => "颂", + "頍" => "𫠆", + "頎" => "颀", + "頏" => "颃", + "預" => "预", + "頑" => "顽", + "頒" => "颁", + "頓" => "顿", + "頔" => "𬱖", + "頗" => "颇", + "領" => "领", + "頜" => "颌", + "頠" => "𬱟", + "頡" => "颉", + "頤" => "颐", + "頦" => "颏", + "頫" => "𫖯", + "頭" => "头", + "頮" => "颒", + "頰" => "颊", + "頲" => "颋", + "頴" => "颕", + "頵" => "𫖳", + "頷" => "颔", + "頸" => "颈", + "頹" => "颓", + "頻" => "频", + "頽" => "颓", + "顂" => "𩓋", + "顃" => "𩖖", + "顅" => "𫖶", + "顆" => "颗", + "題" => "题", + "額" => "额", + "顎" => "颚", + "顏" => "颜", + "顒" => "颙", + "顓" => "颛", + "顔" => "颜", + "顗" => "𫖮", + "願" => "愿", + "顙" => "颡", + "顛" => "颠", + "類" => "类", + "顢" => "颟", + "顣" => "𫖹", + "顥" => "颢", + "顧" => "顾", + "顫" => "颤", + "顬" => "颥", + "顯" => "显", + "顰" => "颦", + "顱" => "颅", + "顳" => "颞", + "顴" => "颧", + "風" => "风", + "颭" => "飐", + "颮" => "飑", + "颯" => "飒", + "颰" => "𩙥", + "颱" => "台", + "颳" => "刮", + "颶" => "飓", + "颷" => "𩙪", + "颸" => "飔", + "颺" => "飏", + "颻" => "飖", + "颼" => "飕", + "颾" => "𩙫", + "飀" => "飗", + "飄" => "飘", + "飆" => "飙", + "飈" => "飚", + "飋" => "𫗋", + "飛" => "飞", + "飠" => "饣", + "飢" => "饥", + "飣" => "饤", + "飥" => "饦", + "飦" => "𫗞", + "飩" => "饨", + "飪" => "饪", + "飫" => "饫", + "飭" => "饬", + "飯" => "饭", + "飱" => "飧", + "飲" => "饮", + "飴" => "饴", + "飵" => "𫗢", + "飶" => "𫗣", + "飼" => "饲", + "飽" => "饱", + "飾" => "饰", + "飿" => "饳", + "餃" => "饺", + "餄" => "饸", + "餅" => "饼", + "餈" => "糍", + "餉" => "饷", + "養" => "养", + "餌" => "饵", + "餎" => "饹", + "餏" => "饻", + "餑" => "饽", + "餒" => "馁", + "餓" => "饿", + "餔" => "𫗦", + "餕" => "馂", + "餖" => "饾", + "餗" => "𫗧", + "餘" => "余", + "餚" => "肴", + "餛" => "馄", + "餜" => "馃", + "餞" => "饯", + "餡" => "馅", + "餦" => "𫗠", + "餧" => "𫗪", + "館" => "馆", + "餪" => "𫗬", + "餫" => "𫗥", + "餬" => "糊", + "餭" => "𫗮", + "餱" => "糇", + "餳" => "饧", + "餵" => "喂", + "餶" => "馉", + "餷" => "馇", + "餸" => "𩠌", + "餺" => "馎", + "餼" => "饩", + "餾" => "馏", + "餿" => "馊", + "饁" => "馌", + "饃" => "馍", + "饅" => "馒", + "饈" => "馐", + "饉" => "馑", + "饊" => "馓", + "饋" => "馈", + "饌" => "馔", + "饑" => "饥", + "饒" => "饶", + "饗" => "飨", + "饘" => "𫗴", + "饜" => "餍", + "饞" => "馋", + "饟" => "𫗵", + "饠" => "𫗩", + "饢" => "馕", + "馬" => "马", + "馭" => "驭", + "馮" => "冯", + "馯" => "𫘛", + "馱" => "驮", + "馳" => "驰", + "馴" => "驯", + "馹" => "驲", + "馼" => "𫘜", + "駁" => "驳", + "駃" => "𫘝", + "駉" => "𬳶", + "駊" => "𫘟", + "駎" => "𩧨", + "駐" => "驻", + "駑" => "驽", + "駒" => "驹", + "駓" => "𬳵", + "駔" => "驵", + "駕" => "驾", + "駘" => "骀", + "駙" => "驸", + "駚" => "𩧫", + "駛" => "驶", + "駝" => "驼", + "駞" => "𫘞", + "駟" => "驷", + "駡" => "骂", + "駢" => "骈", + "駤" => "𫘠", + "駧" => "𩧲", + "駩" => "𩧴", + "駪" => "𬳽", + "駫" => "𫘡", + "駭" => "骇", + "駰" => "骃", + "駱" => "骆", + "駶" => "𩧺", + "駸" => "骎", + "駻" => "𫘣", + "駼" => "𬳿", + "駿" => "骏", + "騁" => "骋", + "騂" => "骍", + "騃" => "𫘤", + "騄" => "𫘧", + "騅" => "骓", + "騉" => "𫘥", + "騊" => "𫘦", + "騌" => "骔", + "騍" => "骒", + "騎" => "骑", + "騏" => "骐", + "騑" => "𬴂", + "騔" => "𩨀", + "騖" => "骛", + "騙" => "骗", + "騚" => "𩨊", + "騜" => "𫘩", + "騝" => "𩨃", + "騞" => "𬴃", + "騟" => "𩨈", + "騠" => "𫘨", + "騤" => "骙", + "騧" => "䯄", + "騪" => "𩨄", + "騫" => "骞", + "騭" => "骘", + "騮" => "骝", + "騰" => "腾", + "騱" => "𫘬", + "騴" => "𫘫", + "騵" => "𫘪", + "騶" => "驺", + "騷" => "骚", + "騸" => "骟", + "騻" => "𫘭", + "騼" => "𫠋", + "騾" => "骡", + "驀" => "蓦", + "驁" => "骜", + "驂" => "骖", + "驃" => "骠", + "驄" => "骢", + "驅" => "驱", + "驊" => "骅", + "驋" => "𩧯", + "驌" => "骕", + "驍" => "骁", + "驎" => "𬴊", + "驏" => "骣", + "驓" => "𫘯", + "驕" => "骄", + "驗" => "验", + "驙" => "𫘰", + "驚" => "惊", + "驛" => "驿", + "驟" => "骤", + "驢" => "驴", + "驤" => "骧", + "驥" => "骥", + "驦" => "骦", + "驨" => "𫘱", + "驪" => "骊", + "驫" => "骉", + "骯" => "肮", + "髏" => "髅", + "髒" => "脏", + "體" => "体", + "髕" => "髌", + "髖" => "髋", + "髮" => "发", + "鬆" => "松", + "鬍" => "胡", + "鬖" => "𩭹", + "鬚" => "须", + "鬠" => "𫘽", + "鬢" => "鬓", + "鬥" => "斗", + "鬧" => "闹", + "鬨" => "哄", + "鬩" => "阋", + "鬮" => "阄", + "鬱" => "郁", + "鬹" => "鬶", + "魎" => "魉", + "魘" => "魇", + "魚" => "鱼", + "魛" => "鱽", + "魟" => "𫚉", + "魢" => "鱾", + "魥" => "𩽹", + "魦" => "𫚌", + "魨" => "鲀", + "魯" => "鲁", + "魴" => "鲂", + "魵" => "𫚍", + "魷" => "鱿", + "魺" => "鲄", + "魽" => "𫠐", + "鮀" => "𬶍", + "鮁" => "鲅", + "鮃" => "鲆", + "鮄" => "𫚒", + "鮅" => "𫚑", + "鮆" => "𫚖", + "鮈" => "𬶋", + "鮊" => "鲌", + "鮋" => "鲉", + "鮍" => "鲏", + "鮎" => "鲇", + "鮐" => "鲐", + "鮑" => "鲍", + "鮒" => "鲋", + "鮓" => "鲊", + "鮚" => "鲒", + "鮜" => "鲘", + "鮝" => "鲞", + "鮞" => "鲕", + "鮟" => "𩽾", + "鮠" => "𬶏", + "鮡" => "𬶐", + "鮣" => "䲟", + "鮤" => "𫚓", + "鮦" => "鲖", + "鮪" => "鲔", + "鮫" => "鲛", + "鮭" => "鲑", + "鮮" => "鲜", + "鮯" => "𫚗", + "鮰" => "𫚔", + "鮳" => "鲓", + "鮵" => "𫚛", + "鮶" => "鲪", + "鮸" => "𩾃", + "鮺" => "鲝", + "鮿" => "𫚚", + "鯀" => "鲧", + "鯁" => "鲠", + "鯄" => "𩾁", + "鯆" => "𫚙", + "鯇" => "鲩", + "鯉" => "鲤", + "鯊" => "鲨", + "鯒" => "鲬", + "鯔" => "鲻", + "鯕" => "鲯", + "鯖" => "鲭", + "鯗" => "鲞", + "鯛" => "鲷", + "鯝" => "鲴", + "鯞" => "𫚡", + "鯡" => "鲱", + "鯢" => "鲵", + "鯤" => "鲲", + "鯧" => "鲳", + "鯨" => "鲸", + "鯪" => "鲮", + "鯫" => "鲰", + "鯬" => "𫚞", + "鯰" => "鲶", + "鯱" => "𩾇", + "鯴" => "鲺", + "鯶" => "𩽼", + "鯷" => "鳀", + "鯻" => "𬶟", + "鯽" => "鲫", + "鯾" => "𫚣", + "鯿" => "鳊", + "鰁" => "鳈", + "鰂" => "鲗", + "鰃" => "鳂", + "鰆" => "䲠", + "鰈" => "鲽", + "鰉" => "鳇", + "鰊" => "𬶠", + "鰋" => "𫚢", + "鰌" => "䲡", + "鰍" => "鳅", + "鰏" => "鲾", + "鰐" => "鳄", + "鰑" => "𫚊", + "鰒" => "鳆", + "鰓" => "鳃", + "鰕" => "𫚥", + "鰛" => "鳁", + "鰜" => "鳒", + "鰟" => "鳑", + "鰠" => "鳋", + "鰣" => "鲥", + "鰤" => "𫚕", + "鰥" => "鳏", + "鰦" => "𫚤", + "鰧" => "䲢", + "鰨" => "鳎", + "鰩" => "鳐", + "鰫" => "𫚦", + "鰭" => "鳍", + "鰮" => "鳁", + "鰱" => "鲢", + "鰲" => "鳌", + "鰳" => "鳓", + "鰵" => "鳘", + "鰶" => "𬶭", + "鰷" => "鲦", + "鰹" => "鲣", + "鰺" => "鲹", + "鰻" => "鳗", + "鰼" => "鳛", + "鰽" => "𫚧", + "鰾" => "鳔", + "鱀" => "𬶨", + "鱂" => "鳉", + "鱄" => "𫚋", + "鱅" => "鳙", + "鱆" => "𫠒", + "鱇" => "𩾌", + "鱈" => "鳕", + "鱉" => "鳖", + "鱊" => "𫚪", + "鱒" => "鳟", + "鱔" => "鳝", + "鱖" => "鳜", + "鱗" => "鳞", + "鱘" => "鲟", + "鱚" => "𬶮", + "鱝" => "鲼", + "鱟" => "鲎", + "鱠" => "鲙", + "鱢" => "𫚫", + "鱣" => "鳣", + "鱤" => "鳡", + "鱧" => "鳢", + "鱨" => "鲿", + "鱭" => "鲚", + "鱮" => "𫚈", + "鱯" => "鳠", + "鱲" => "𫚭", + "鱷" => "鳄", + "鱸" => "鲈", + "鱺" => "鲡", + "鳥" => "鸟", + "鳧" => "凫", + "鳩" => "鸠", + "鳬" => "凫", + "鳲" => "鸤", + "鳳" => "凤", + "鳴" => "鸣", + "鳶" => "鸢", + "鳷" => "𫛛", + "鳼" => "𪉃", + "鳽" => "𫛚", + "鳾" => "䴓", + "鴀" => "𫛜", + "鴃" => "𫛞", + "鴅" => "𫛝", + "鴆" => "鸩", + "鴇" => "鸨", + "鴉" => "鸦", + "鴐" => "𫛤", + "鴒" => "鸰", + "鴔" => "𫛡", + "鴕" => "鸵", + "鴗" => "𫁡", + "鴛" => "鸳", + "鴜" => "𪉈", + "鴝" => "鸲", + "鴞" => "鸮", + "鴟" => "鸱", + "鴣" => "鸪", + "鴥" => "𫛣", + "鴦" => "鸯", + "鴨" => "鸭", + "鴮" => "𫛦", + "鴯" => "鸸", + "鴰" => "鸹", + "鴲" => "𪉆", + "鴳" => "𫛩", + "鴴" => "鸻", + "鴷" => "䴕", + "鴻" => "鸿", + "鴽" => "𫛪", + "鴿" => "鸽", + "鵁" => "䴔", + "鵂" => "鸺", + "鵃" => "鸼", + "鵊" => "𫛥", + "鵏" => "𬷕", + "鵐" => "鹀", + "鵑" => "鹃", + "鵒" => "鹆", + "鵓" => "鹁", + "鵚" => "𪉍", + "鵜" => "鹈", + "鵝" => "鹅", + "鵟" => "𫛭", + "鵠" => "鹄", + "鵡" => "鹉", + "鵧" => "𫛨", + "鵩" => "𫛳", + "鵪" => "鹌", + "鵫" => "𫛱", + "鵬" => "鹏", + "鵮" => "鹐", + "鵯" => "鹎", + "鵰" => "雕", + "鵲" => "鹊", + "鵷" => "鹓", + "鵾" => "鹍", + "鶄" => "䴖", + "鶇" => "鸫", + "鶉" => "鹑", + "鶊" => "鹒", + "鶌" => "𫛵", + "鶒" => "𫛶", + "鶓" => "鹋", + "鶖" => "鹙", + "鶗" => "𫛸", + "鶘" => "鹕", + "鶚" => "鹗", + "鶠" => "𬸘", + "鶡" => "鹖", + "鶥" => "鹛", + "鶦" => "𫛷", + "鶩" => "鹜", + "鶪" => "䴗", + "鶬" => "鸧", + "鶭" => "𫛯", + "鶯" => "莺", + "鶰" => "𫛫", + "鶱" => "𬸣", + "鶲" => "鹟", + "鶴" => "鹤", + "鶹" => "鹠", + "鶺" => "鹡", + "鶻" => "鹘", + "鶼" => "鹣", + "鶿" => "鹚", + "鷀" => "鹚", + "鷁" => "鹢", + "鷂" => "鹞", + "鷄" => "鸡", + "鷅" => "𫛽", + "鷉" => "䴘", + "鷊" => "鹝", + "鷐" => "𫜀", + "鷓" => "鹧", + "鷔" => "𪉑", + "鷖" => "鹥", + "鷗" => "鸥", + "鷙" => "鸷", + "鷚" => "鹨", + "鷟" => "𬸦", + "鷣" => "𫜃", + "鷤" => "𫛴", + "鷥" => "鸶", + "鷦" => "鹪", + "鷨" => "𪉊", + "鷩" => "𫜁", + "鷫" => "鹔", + "鷭" => "𬸪", + "鷯" => "鹩", + "鷲" => "鹫", + "鷳" => "鹇", + "鷴" => "鹇", + "鷷" => "𫜄", + "鷸" => "鹬", + "鷹" => "鹰", + "鷺" => "鹭", + "鷽" => "鸴", + "鷿" => "𬸯", + "鸂" => "㶉", + "鸇" => "鹯", + "鸊" => "䴙", + "鸋" => "𫛢", + "鸌" => "鹱", + "鸏" => "鹲", + "鸑" => "𬸚", + "鸕" => "鸬", + "鸗" => "𫛟", + "鸘" => "鹴", + "鸚" => "鹦", + "鸛" => "鹳", + "鸝" => "鹂", + "鸞" => "鸾", + "鹵" => "卤", + "鹹" => "咸", + "鹺" => "鹾", + "鹼" => "碱", + "鹽" => "盐", + "麗" => "丽", + "麥" => "麦", + "麨" => "𪎊", + "麩" => "麸", + "麪" => "面", + "麫" => "面", + "麬" => "𤿲", + "麯" => "曲", + "麲" => "𪎉", + "麳" => "𪎌", + "麴" => "曲", + "麵" => "面", + "麷" => "𫜑", + "麼" => "么", + "麽" => "么", + "黃" => "黄", + "黌" => "黉", + "點" => "点", + "黨" => "党", + "黲" => "黪", + "黴" => "霉", + "黶" => "黡", + "黷" => "黩", + "黽" => "黾", + "黿" => "鼋", + "鼂" => "鼌", + "鼉" => "鼍", + "鼕" => "冬", + "鼴" => "鼹", + "齊" => "齐", + "齋" => "斋", + "齎" => "赍", + "齏" => "齑", + "齒" => "齿", + "齔" => "龀", + "齕" => "龁", + "齗" => "龂", + "齘" => "𬹼", + "齙" => "龅", + "齜" => "龇", + "齟" => "龃", + "齠" => "龆", + "齡" => "龄", + "齣" => "出", + "齦" => "龈", + "齧" => "啮", + "齩" => "𫜪", + "齪" => "龊", + "齬" => "龉", + "齭" => "𫜭", + "齮" => "𬺈", + "齯" => "𫠜", + "齰" => "𫜬", + "齲" => "龋", + "齴" => "𫜮", + "齶" => "腭", + "齷" => "龌", + "齼" => "𬺓", + "齾" => "𫜰", + "龍" => "龙", + "龎" => "厐", + "龐" => "庞", + "龑" => "䶮", + "龓" => "𫜲", + "龔" => "龚", + "龕" => "龛", + "龜" => "龟", + "龭" => "𩨎", + "龯" => "𨱆", + "鿁" => "䜤", + "鿓" => "鿒", + "𠁞" => "𠀾", + "𠌥" => "𠆿", + "𠏢" => "𠉗", + "𠐊" => "𫝋", + "𠗣" => "㓆", + "𠞆" => "𠛆", + "𠠎" => "𠚳", + "𠬙" => "𪠡", + "𠽃" => "𪠺", + "𠿕" => "𪜎", + "𡂡" => "𪢒", + "𡃄" => "𪡺", + "𡃕" => "𠴛", + "𡃤" => "𪢐", + "𡄔" => "𠴢", + "𡄣" => "𠵸", + "𡅏" => "𠲥", + "𡅯" => "𪢖", + "𡑍" => "𫭼", + "𡑭" => "𡋗", + "𡓁" => "𪤄", + "𡓾" => "𡋀", + "𡔖" => "𡍣", + "𡞵" => "㛟", + "𡟫" => "𫝪", + "𡠹" => "㛿", + "𡢃" => "㛠", + "𡮉" => "𡭜", + "𡮣" => "𡭬", + "𡳳" => "𡳃", + "𡸗" => "𪨩", + "𡹬" => "𪨹", + "𡻕" => "岁", + "𡽗" => "𡸃", + "𡾱" => "㟜", + "𡿖" => "𪩛", + "𢍰" => "𪪴", + "𢠼" => "𢙑", + "𢣐" => "𪬚", + "𢣚" => "𢘝", + "𢣭" => "𢘞", + "𢤩" => "𪫡", + "𢤱" => "𢘙", + "𢤿" => "𪬯", + "𢯷" => "𪭝", + "𢶒" => "𪭯", + "𢶫" => "𢫞", + "𢷮" => "𢫊", + "𢹿" => "𢬦", + "𢺳" => "𪮳", + "𣈶" => "暅", + "𣋋" => "𣈣", + "𣍐" => "𫧃", + "𣙎" => "㭣", + "𣜬" => "𪳗", + "𣝕" => "𣘷", + "𣞻" => "𣘓", + "𣠩" => "𣞎", + "𣠲" => "𣑶", + "𣯩" => "𣯣", + "𣯴" => "𣭤", + "𣯶" => "毶", + "𣽏" => "𪶮", + "𣾷" => "㳢", + "𣿉" => "𣶫", + "𤁣" => "𣺽", + "𤄷" => "𪶒", + "𤅶" => "𣷷", + "𤑳" => "𤎻", + "𤑹" => "𪹀", + "𤒎" => "𤊀", + "𤒻" => "𪹹", + "𤓌" => "𪹠", + "𤓎" => "𤎺", + "𤓩" => "𤊰", + "𤘀" => "𪺣", + "𤛮" => "𤙯", + "𤛱" => "𫞢", + "𤜆" => "𪺪", + "𤠮" => "𪺸", + "𤢟" => "𤝢", + "𤢻" => "𢢐", + "𤩂" => "𫞧", + "𤪺" => "㻘", + "𤫩" => "㻏", + "𤬅" => "𪼴", + "𤳷" => "𪽝", + "𤳸" => "𤳄", + "𤷃" => "𪽭", + "𤸫" => "𤶧", + "𤺔" => "𪽴", + "𥊝" => "𥅿", + "𥌃" => "𥅘", + "𥏝" => "𪿊", + "𥕥" => "𥐰", + "𥖅" => "𥐯", + "𥖲" => "𪿞", + "𥗇" => "𪿵", + "𥗽" => "𬒗", + "𥜐" => "𫀓", + "𥜰" => "𫀌", + "𥞵" => "𥞦", + "𥢢" => "䅪", + "𥢶" => "𫞷", + "𥢷" => "𫀮", + "𥨐" => "𥧂", + "𥪂" => "𥩺", + "𥯤" => "𫁳", + "𥴨" => "𫂖", + "𥴼" => "𫁺", + "𥵃" => "𥱔", + "𥵊" => "𥭉", + "𥶽" => "𫁱", + "𥸠" => "𥮋", + "𥻦" => "𫂿", + "𥼽" => "𥹥", + "𥽖" => "𥺇", + "𥾯" => "𫄝", + "𥿊" => "𦈈", + "𦀖" => "𫄦", + "𦂅" => "𦈒", + "𦃄" => "𦈗", + "𦃩" => "𫄯", + "𦅇" => "𫄪", + "𦅈" => "𫄵", + "𦆲" => "𫟇", + "𦒀" => "𫅥", + "𦔖" => "𫅼", + "𦘧" => "𡳒", + "𦟼" => "𫆝", + "𦠅" => "𫞅", + "𦡝" => "𫆫", + "𦢈" => "𣍨", + "𦣎" => "𦟗", + "𦧺" => "𫇘", + "𦪙" => "䑽", + "𦪽" => "𦨩", + "𦱌" => "𫇪", + "𦾟" => "𦶻", + "𧎈" => "𧌥", + "𧒯" => "𫊹", + "𧔥" => "𧒭", + "𧕟" => "𧉐", + "𧜗" => "䘞", + "𧜵" => "䙊", + "𧝞" => "䘛", + "𧞫" => "𫌋", + "𧟀" => "𧝧", + "𧡴" => "𫌫", + "𧢄" => "𫌬", + "𧦝" => "𫍞", + "𧦧" => "𫍟", + "𧩕" => "𫍭", + "𧩙" => "䜥", + "𧩼" => "𫍶", + "𧫝" => "𫍺", + "𧬤" => "𫍼", + "𧭈" => "𫍾", + "𧭹" => "𫍐", + "𧳟" => "𧳕", + "𧵳" => "䞌", + "𧶔" => "𧹓", + "𧶧" => "䞎", + "𧷎" => "𪠀", + "𧸘" => "𫎨", + "𧹈" => "𪥠", + "𧽯" => "𫎸", + "𨂐" => "𫏌", + "𨄣" => "𨀱", + "𨅍" => "𨁴", + "𨆪" => "𫏕", + "𨇁" => "𧿈", + "𨇞" => "𨅫", + "𨇤" => "𫏨", + "𨇰" => "𫏞", + "𨇽" => "𫏑", + "𨈊" => "𨂺", + "𨈌" => "𨄄", + "𨊰" => "䢀", + "𨊸" => "䢁", + "𨊻" => "𨐆", + "𨋢" => "䢂", + "𨌈" => "𫐍", + "𨍰" => "𫐔", + "𨎌" => "𫐋", + "𨎮" => "𨐉", + "𨏠" => "𨐇", + "𨏥" => "𨐊", + "𨞺" => "𫟫", + "𨟊" => "𫟬", + "𨢿" => "𨡙", + "𨣈" => "𨡺", + "𨣞" => "𨟳", + "𨣧" => "𨠨", + "𨤻" => "𨤰", + "𨥛" => "𨱀", + "𨥟" => "𫓫", + "𨦫" => "䦀", + "𨧀" => "𬭊", + "𨧜" => "䦁", + "𨧰" => "𫟽", + "𨧱" => "𨱊", + "𨨏" => "𬭛", + "𨨛" => "𫓼", + "𨨢" => "𫓿", + "𨩰" => "𫟾", + "𨪕" => "𫓮", + "𨫒" => "𨱐", + "𨬖" => "𫔏", + "𨭆" => "𬭶", + "𨭎" => "𬭳", + "𨭖" => "𫔑", + "𨭸" => "𫔐", + "𨮂" => "𨱕", + "𨮳" => "𫔒", + "𨯅" => "䥿", + "𨯟" => "𫔓", + "𨰃" => "𫔉", + "𨰋" => "𫓳", + "𨰥" => "𫔕", + "𨰲" => "𫔃", + "𨲳" => "𫔖", + "𨳑" => "𨸁", + "𨳕" => "𨸀", + "𨴗" => "𨸅", + "𨴹" => "𫔲", + "𨵩" => "𨸆", + "𨵸" => "𨸇", + "𨶀" => "𨸉", + "𨶏" => "𨸊", + "𨶮" => "𨸌", + "𨶲" => "𨸋", + "𨷲" => "𨸎", + "𨼳" => "𫔽", + "𨽏" => "𨸘", + "𩀨" => "𫕚", + "𩅙" => "𫕨", + "𩎖" => "𫖑", + "𩎢" => "𩏾", + "𩏂" => "𫖓", + "𩏠" => "𫖖", + "𩏪" => "𩏽", + "𩏷" => "𫃗", + "𩑔" => "𫖪", + "𩒎" => "𫖭", + "𩓣" => "𩖕", + "𩓥" => "𫖵", + "𩔑" => "𫖷", + "𩔳" => "𫖴", + "𩖰" => "𫠇", + "𩗀" => "𩙦", + "𩗓" => "𫗈", + "𩗴" => "𫗉", + "𩘀" => "𩙩", + "𩘝" => "𩙭", + "𩘹" => "𩙨", + "𩘺" => "𩙬", + "𩙈" => "𩙰", + "𩚛" => "𩟿", + "𩚥" => "𩠀", + "𩚩" => "𫗡", + "𩚵" => "𩠁", + "𩛆" => "𩠂", + "𩛌" => "𫗤", + "𩛡" => "𫗨", + "𩛩" => "𩠃", + "𩜇" => "𩠉", + "𩜦" => "𩠆", + "𩜵" => "𩠊", + "𩝔" => "𩠋", + "𩝽" => "𫗳", + "𩞄" => "𩠎", + "𩞦" => "𩠏", + "𩞯" => "䭪", + "𩟐" => "𩠅", + "𩟗" => "𫗚", + "𩠴" => "𩠠", + "𩡣" => "𩡖", + "𩡺" => "𩧦", + "𩢡" => "𩧬", + "𩢴" => "𩧵", + "𩢸" => "𩧳", + "𩢾" => "𩧮", + "𩣏" => "𩧶", + "𩣑" => "䯃", + "𩣫" => "𩧸", + "𩣵" => "𩧻", + "𩣺" => "𩧼", + "𩤊" => "𩧩", + "𩤙" => "𩨆", + "𩤲" => "𩨉", + "𩤸" => "𩨅", + "𩥄" => "𩨋", + "𩥇" => "𩨍", + "𩥉" => "𩧱", + "𩥑" => "𩨌", + "𩦠" => "𫠌", + "𩧆" => "𩨐", + "𩭙" => "𩬣", + "𩯁" => "𫙂", + "𩯳" => "𩯒", + "𩰀" => "𩬤", + "𩰹" => "𩰰", + "𩳤" => "𩲒", + "𩴵" => "𩴌", + "𩵦" => "𫠏", + "𩵩" => "𩽺", + "𩵹" => "𩽻", + "𩶁" => "𫚎", + "𩶘" => "䲞", + "𩶰" => "𩽿", + "𩶱" => "𩽽", + "𩷰" => "𩾄", + "𩸃" => "𩾅", + "𩸄" => "𫚝", + "𩸡" => "𫚟", + "𩸦" => "𩾆", + "𩻗" => "𫚨", + "𩻬" => "𫚩", + "𩻮" => "𫚘", + "𩼶" => "𫚬", + "𩽇" => "𩾎", + "𩿅" => "𫠖", + "𩿤" => "𫛠", + "𩿪" => "𪉄", + "𪀖" => "𫛧", + "𪀦" => "𪉅", + "𪀾" => "𪉋", + "𪁈" => "𪉉", + "𪁖" => "𪉌", + "𪂆" => "𪉎", + "𪃍" => "𪉐", + "𪃏" => "𪉏", + "𪃒" => "𫛻", + "𪃧" => "𫛹", + "𪄆" => "𪉔", + "𪄕" => "𪉒", + "𪅂" => "𫜂", + "𪆷" => "𫛾", + "𪇳" => "𪉕", + "𪈼" => "𱊜", + "𪉸" => "𫜊", + "𪋿" => "𫧮", + "𪌭" => "𫜓", + "𪍠" => "𫜕", + "𪓰" => "𫜟", + "𪔵" => "𪔭", + "𪘀" => "𪚏", + "𪘯" => "𪚐", + "𪙏" => "𫜯", + "𪟖" => "𠛾", + "𪷓" => "𣶭", + "𫒡" => "𫓷", + "𫜦" => "𫜫", +}; diff --git a/src/rust/examples/tests/database/config_test_case.rs b/src/rust/examples/tests/database/config_test_case.rs new file mode 100644 index 000000000..d1697564f --- /dev/null +++ b/src/rust/examples/tests/database/config_test_case.rs @@ -0,0 +1,298 @@ +use crate::base::base_test_case::TestCaseTrait; +use crate::base::random_tool::RandomTool; +use crate::base::table_test_case::TableTestCase; +use crate::base::test_object::TestObject; +use lazy_static::lazy_static; +use std::sync::{Arc, RwLock}; +use wcdb::base::wcdb_exception::WCDBResult; +use wcdb::core::database::SetDatabaseConfigTrait; + +pub struct ConfigTest { + table_test_case: TableTestCase, + config_name: String, +} + +impl TestCaseTrait for ConfigTest { + fn setup(&self) -> WCDBResult<()> { + self.table_test_case.setup() + } + + fn teardown(&self) -> WCDBResult<()> { + { + let database = self.table_test_case.get_database().clone(); + let _ret = database.read().unwrap().set_config_with_default_priority:: + , Box> + (&self.table_test_case.get_table_name(), None); + } + self.table_test_case.teardown() + } +} + +impl ConfigTest { + pub fn new() -> Self { + ConfigTest { + table_test_case: TableTestCase::new(), + config_name: "testConfig".to_string(), + } + } + + pub fn get_table_test_case(&self) -> &TableTestCase { + &self.table_test_case + } + + pub fn get_mut_table_test_case(&mut self) -> &mut TableTestCase { + &mut self.table_test_case + } + + pub fn get_config_name(&self) -> String { + self.config_name.clone() + } +} + +lazy_static! { + static ref CONFIG_TEST: Arc> = Arc::new(RwLock::new(ConfigTest::new())); + static ref PRE_INSERT_OBJECTS: Vec = + RandomTool::auto_increment_test_case_objects(2); +} + +#[cfg(test)] +pub mod config_test_case { + use crate::base::base_test_case::TestCaseTrait; + use crate::base::database_test_case::Expect; + use crate::base::random_tool::RandomTool; + use crate::base::test_object::{DbTestObject, DB_TEST_OBJECT_INSTANCE}; + use crate::base::wrapped_value::WrappedValue; + use crate::database::config_test_case::CONFIG_TEST; + use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard}; + use wcdb::core::database::{CipherVersion, ConfigPriority, Database, SetDatabaseConfigTrait}; + use wcdb::core::handle::Handle; + use wcdb::core::handle_operation::HandleOperationTrait; + use wcdb::core::table_orm_operation::TableORMOperationTrait; + use wcdb::winq::pragma::Pragma; + use wcdb::winq::statement_pragma::StatementPragma; + + pub fn setup() { + { + let config_clone = Arc::clone(&CONFIG_TEST); + let config_test = config_clone.read().expect("config_clone write failure"); + config_test.setup().expect("teardown failure"); + + let database_arc = config_test.get_table_test_case().get_database(); + database_arc.read().unwrap().remove_files().unwrap(); + } + } + + pub fn teardown() { + { + let config_clone = Arc::clone(&CONFIG_TEST); + let config_test = config_clone.read().expect("config_clone write failure"); + config_test.teardown().expect("teardown failure"); + } + } + + pub fn get_arc_database() -> Arc> { + let config_clone = Arc::clone(&CONFIG_TEST); + let ret = config_clone + .read() + .unwrap() + .get_table_test_case() + .get_database(); + Arc::clone(&ret) + } + + #[test] + pub fn test_config() { + setup(); + let set_secure_delete = Arc::new(Mutex::new(StatementPragma::new())); + { + set_secure_delete + .lock() + .unwrap() + .pragma(Pragma::secure_delete()) + .to_value_bool(true); + } + let unset_secure_delete = Arc::new(StatementPragma::new()); + { + unset_secure_delete + .pragma(Pragma::secure_delete()) + .to_value_bool(false); + } + let binding = StatementPragma::new(); + let get_secure_delete = binding.pragma(Pragma::secure_delete()); + let un_invoked = Arc::new(Mutex::new(WrappedValue::new())); + let database_arc = get_arc_database(); + { + let database = database_arc.read().unwrap(); + let config_test_clone = Arc::clone(&CONFIG_TEST); + let config_test = config_test_clone.read().unwrap(); + + let set_secure_delete_clone = Arc::clone(&set_secure_delete); + let unset_secure_delete_clone = Arc::clone(&unset_secure_delete); + let wrapped_value_clone = Arc::clone(&un_invoked); + let ret = database.set_config( + &*config_test.get_config_name(), + Some(move |handle: Handle| { + let tmp = set_secure_delete_clone.lock().unwrap(); + handle.execute(&*tmp).unwrap(); + return true; + }), + Some(move |handle: Handle| { + let tmp = &*unset_secure_delete_clone.as_ref(); + let mut value = wrapped_value_clone.lock().unwrap(); + value.bool_value = true; + handle.execute(tmp).unwrap(); + return true; + }), + ConfigPriority::Low, + ); + config_test + .table_test_case + .data_base_test_case + .set_expect_mode(Expect::SomeSQLs); + } + { + let config_test_clone = Arc::clone(&CONFIG_TEST); + let config_test = config_test_clone.read().unwrap(); + let binding = Arc::clone(&database_arc); + config_test.table_test_case.data_base_test_case.do_test_sql( + "PRAGMA secure_delete = TRUE", + || { + let database = binding.read().unwrap(); + database.close(Some(|| {})); + assert!(database.can_open()); + Ok(()) + }, + ); + } + { + let binding = Arc::clone(&database_arc); + let database = binding.read().unwrap(); + let config_test_clone = Arc::clone(&CONFIG_TEST); + let config_test = config_test_clone.read().unwrap(); + assert!(database + .get_value_from_statement(get_secure_delete) + .expect("get_value_from_statement failure") + .get_bool()); + + let ret = database.set_config_with_default_priority::, Box>(&*config_test.get_config_name(), None); + assert!(database.can_open()); + let un_invoked_clone = Arc::clone(&un_invoked); + assert!(un_invoked_clone.lock().unwrap().bool_value); + assert_eq!( + !database + .get_value_from_statement(get_secure_delete) + .unwrap() + .get_bool(), + false + ); + } + teardown(); + } + + #[test] + pub fn test_cipher() { + setup(); + let cipher = "123".as_bytes().to_vec(); + let wrong_cipher = "456".as_bytes().to_vec(); + + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database.set_cipher_key(&cipher, None, None); + assert_eq!(database.can_open(), true); + + database.close(Some(|| {})); + + database.set_cipher_key(&wrong_cipher, None, None); + assert_eq!(database.can_open(), false); + teardown(); + } + + // #[test] + pub fn test_cipher_with_page_size() { + setup(); + let cipher = "123".as_bytes().to_vec(); + let page_size = 8 * 1024; + let wrong_page_size = 16 * 1024; + + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database.set_cipher_key(&cipher, Some(page_size), None); + assert_eq!(database.can_open(), true); + + database.close(Some(|| {})); + database.set_cipher_key(&cipher, Some(wrong_page_size), None); + assert_eq!(database.can_open(), false); + teardown(); + } + + // #[test] + pub fn test_cipher_with_different_version() { + setup(); + + let cipher = "123".as_bytes().to_vec(); + let page_size = 4096; + + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database.set_cipher_key(&cipher, Some(page_size), Some(CipherVersion::Version3)); + assert_eq!(database.can_open(), true); + + database.close(Some(|| {})); + database.set_cipher_key(&cipher, Some(page_size), None); + assert_eq!(database.can_open(), false); + + database.set_cipher_key(&cipher, Some(page_size), Some(CipherVersion::Version3)); + assert_eq!(database.can_open(), true); + + database.remove_files().unwrap(); + database.set_cipher_key(&cipher, Some(page_size), Some(CipherVersion::Version4)); + assert_eq!(database.can_open(), true); + database.close(Some(|| {})); + + database.set_cipher_key(&cipher, Some(page_size), None); + assert_eq!(database.can_open(), true); + Database::set_default_cipher_version(CipherVersion::Version4); + assert_eq!(database.can_open(), true); + teardown(); + } + + #[test] + pub fn test_incremental_vacuum() { + setup(); + { + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database.enable_auto_backup(true); + } + { + let config_test_clone = Arc::clone(&CONFIG_TEST); + let mut config_test = config_test_clone.write().unwrap(); + config_test + .get_mut_table_test_case() + .create_table() + .unwrap(); + + let table_name: &str = config_test.get_table_test_case().get_table_name(); + let database_arc: Arc> = + config_test.get_table_test_case().get_database(); + let database_clone = Arc::clone(&database_arc); + let database: RwLockReadGuard = database_clone.read().unwrap(); + let table = database.get_table(table_name, &*DB_TEST_OBJECT_INSTANCE); + let table_clone = Arc::clone(&table); + + table_clone + .insert_objects( + RandomTool::auto_increment_test_case_objects(2), + Some(DbTestObject::all_fields()), + ) + .unwrap(); + } + { + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database.truncate_check_point().unwrap() + // database.dropTable(tableName); + } + teardown(); + } +} diff --git a/src/rust/examples/tests/database/data_base_test_case.rs b/src/rust/examples/tests/database/data_base_test_case.rs new file mode 100644 index 000000000..b460eadbe --- /dev/null +++ b/src/rust/examples/tests/database/data_base_test_case.rs @@ -0,0 +1,222 @@ +use crate::base::base_test_case::TestCaseTrait; +use crate::base::table_test_case::TableTestCase; +use lazy_static::lazy_static; +use std::sync::{Arc, RwLock}; +use wcdb::base::wcdb_exception::WCDBResult; + +pub struct DatabaseTest { + table_test_case: TableTestCase, +} + +unsafe impl Sync for DatabaseTest {} +unsafe impl Send for DatabaseTest {} + +impl TestCaseTrait for DatabaseTest { + fn setup(&self) -> WCDBResult<()> { + self.table_test_case.setup() + } + + fn teardown(&self) -> WCDBResult<()> { + self.table_test_case.teardown() + } +} + +impl DatabaseTest { + pub fn new() -> Self { + DatabaseTest { + table_test_case: TableTestCase::new(), + } + } + + pub fn get_table_test_case(&self) -> &TableTestCase { + &self.table_test_case + } +} + +lazy_static! { + static ref DATABASE_TEST: Arc> = { + let database_test = DatabaseTest::new(); + Arc::new(RwLock::new(database_test)) + }; +} + +#[cfg(test)] +pub mod data_base_test { + use crate::base::base_test_case::TestCaseTrait; + use crate::base::wrapped_value::WrappedValue; + use crate::database::data_base_test_case::{DatabaseTest, DATABASE_TEST}; + use std::sync::{Arc, Mutex, RwLock}; + use std::thread; + use std::thread::JoinHandle; + use std::time::{Duration, SystemTime, UNIX_EPOCH}; + use wcdb::core::database::Database; + use wcdb::core::handle_operation::HandleOperationTrait; + use wcdb::winq::pragma::Pragma; + use wcdb::winq::statement_pragma::StatementPragma; + + pub fn setup() { + let tmp_clone = Arc::clone(&DATABASE_TEST); + let current = tmp_clone.read().expect("repair_test write failure"); + current.setup().expect("setup failure"); + } + pub fn teardown() { + let tmp_clone = Arc::clone(&DATABASE_TEST); + let current = tmp_clone.read().expect("repair_test write failure"); + current.teardown().expect("teardown failure"); + } + + pub fn get_arc_database() -> Arc> { + let tmp_clone = Arc::clone(&DATABASE_TEST); + let ret = tmp_clone + .read() + .unwrap() + .get_table_test_case() + .get_database(); + Arc::clone(&ret) + } + + fn current_time_millis() -> u128 { + let now = SystemTime::now(); + now.duration_since(UNIX_EPOCH) + .expect("Time went backwards") + .as_millis() + } + + #[test] + pub fn test_tag() { + setup(); + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + assert_ne!(database.get_tag(), 0); + let new_database = Database::new(database.get_path().as_str(), None); + assert_eq!(database.get_tag(), new_database.get_tag()); + teardown(); + } + + #[test] + pub fn test_path() { + setup(); + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + assert_eq!(database.can_open(), true); + let binding = Arc::clone(&DATABASE_TEST); + let database_test_clone = binding.read().unwrap(); + assert_eq!( + database.get_path(), + database_test_clone.get_table_test_case().get_path() + ); + teardown(); + } + + #[test] + pub fn test_open_and_close() { + setup(); + let database_test = DatabaseTest::new(); + let binding = database_test.get_table_test_case().get_database(); + let database = binding.read().unwrap(); + assert_eq!(database.is_opened(), false); + + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + assert_eq!(database.can_open(), true); + assert_eq!(database.is_opened(), true); + database.close(Some(|| {})); + assert_eq!(database.is_opened(), false); + teardown(); + } + + #[test] + pub fn test_blockade() { + setup(); + { + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database.blockade(); + } + let time = Arc::new(Mutex::new(WrappedValue::new())); + let thread_handle = { + let database_clone = get_arc_database(); + let time_clone = Arc::clone(&time); + thread::spawn(move || { + let database = database_clone.read().unwrap(); + assert!(database.can_open()); + thread::sleep(Duration::from_millis(100)); //todo dengxudong 怀疑 can_open 未阻塞 + let mut time = time_clone.lock().unwrap(); + time.int_value = current_time_millis() as i64; + }) + }; + thread::sleep(Duration::from_millis(1000)); + let new_time = current_time_millis() as i64; + { + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database.un_blockade(); + } + thread_handle.join().unwrap(); + let time = time.lock().unwrap(); + assert!(new_time < time.int_value); + teardown(); + } + + #[test] + pub fn test_blockade_and_close() { + setup(); + let main = Arc::new(Mutex::new(WrappedValue::current_time())); + let sub_thread = Arc::new(Mutex::new(WrappedValue::current_time())); + + let thread_handle: JoinHandle<()> = { + let database_clone = get_arc_database(); + let sub_thread = Arc::clone(&sub_thread); + thread::spawn(move || { + let db = database_clone.read().unwrap(); + assert!(db.can_open()); + thread::sleep(Duration::from_millis(100)); //todo dengxudong 怀疑 can_open 未阻塞 + let mut sub_thread_value = sub_thread.lock().unwrap(); + sub_thread_value.int_value = current_time_millis() as i64; + }) + }; + + let main_clone = Arc::clone(&main); + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database.close(Some(move || { + let mut main_value = main_clone.lock().unwrap(); + thread::sleep(Duration::from_secs(1)); + main_value.int_value = current_time_millis() as i64; + })); + + thread_handle.join().unwrap(); + + let main_value = main.lock().unwrap(); + let sub_thread_value = sub_thread.lock().unwrap(); + assert!(main_value.int_value < sub_thread_value.int_value); + teardown(); + } + + #[test] + pub fn test_readonly() { + setup(); + teardown(); + } + + #[test] + pub fn test_run_while_close() { + setup(); + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + assert_eq!(database.can_open(), true); + assert_eq!(database.is_opened(), true); + let database_clone = get_arc_database(); + database.close(Some(move || { + let database = database_clone.read().unwrap(); + let statement_pragma = StatementPragma::new(); + let statement_pragma = statement_pragma + .pragma(Pragma::user_version()) + .to_value(123); + let ret = database.execute(statement_pragma); + assert!(ret.is_ok()); + })); + assert_eq!(database.is_opened(), false); + teardown(); + } +} diff --git a/src/rust/examples/tests/database/database_upgrade_test_case.rs b/src/rust/examples/tests/database/database_upgrade_test_case.rs new file mode 100644 index 000000000..3b8cb8afd --- /dev/null +++ b/src/rust/examples/tests/database/database_upgrade_test_case.rs @@ -0,0 +1,595 @@ +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding)] +#[WCDBTable( + multi_primaries(columns = ["target_id", "category_id", "channel_id"]) +)] +pub struct ConversationTableV1 { + #[WCDBField] + pub target_id: String, + #[WCDBField] + pub category_id: String, + #[WCDBField] + pub channel_id: String, + #[WCDBField] + pub conversation_title: String, + #[WCDBField] + pub is_top: bool, + #[WCDBField] + pub last_time: i64, +} +impl ConversationTableV1 { + pub fn insert(target_id: &str) -> Self { + ConversationTableV1 { + target_id: target_id.to_string(), + category_id: "category_1".to_string(), + channel_id: "channel_1".to_string(), + conversation_title: "default".to_string(), + is_top: true, + last_time: 17900000000, + } + } +} + +#[derive(WCDBTableCoding)] +#[WCDBTable( + multi_primaries(columns = ["target_id", "category_id", "channel_id"]) +)] +pub struct ConversationTableV1_1 { + #[WCDBField] + pub target_id: String, + #[WCDBField] + pub category_id: String, + #[WCDBField] + pub channel_id: String, + #[WCDBField] + pub conversation_title: String, + #[WCDBField(default(i32_value = 100))] + pub a1: i32, + #[WCDBField] + pub a2: i32, + #[WCDBField(default(i32_value = 100))] + pub a3: i32, +} + +#[derive(WCDBTableCoding)] +#[WCDBTable( + multi_primaries(columns = ["target_id", "category_id", "channel_id"]) +)] +pub struct ConversationTableV1_2 { + #[WCDBField] + pub target_id: String, + #[WCDBField] + pub category_id: String, + #[WCDBField] + pub channel_id: String, +} +impl ConversationTableV1_2 { + pub(crate) fn new() -> Self { + ConversationTableV1_2 { + target_id: "test3".to_string(), + category_id: "category_1".to_string(), + channel_id: "channel_1".to_string(), + } + } +} + +#[derive(WCDBTableCoding)] +#[WCDBTable( + multi_primaries(columns = ["target_id", "category_id", "channel_id"]) +)] +pub struct ConversationTableV2 { + #[WCDBField] + id: i32, + #[WCDBField] + pub target_id: String, + #[WCDBField] + pub category_id: String, + #[WCDBField] + pub channel_id: String, + #[WCDBField] + pub conversation_title: String, + #[WCDBField] + pub is_top: bool, + #[WCDBField] + pub last_time: i64, + #[WCDBField] + pub channel_type: i32, + #[WCDBField] + pub draft_message: String, +} +impl ConversationTableV2 { + pub fn insert() -> Self { + ConversationTableV2 { + id: 0, + target_id: "t1".to_string(), + category_id: "category_1".to_string(), + channel_id: "channel_1".to_string(), + conversation_title: "default".to_string(), + is_top: true, + last_time: 17900000000, + channel_type: 0, + draft_message: "".to_string(), + } + } +} + +#[derive(WCDBTableCoding)] +#[WCDBTable( + multi_primaries(columns = ["target_id", "category_id", "channel_id"]), + multi_indexes(name = "index1", columns = ["target_id", "category_id", "channel_id"]), +)] +pub struct ConversationTableV2_1 { + #[WCDBField(is_primary = true, is_auto_increment = true)] + id: i32, + #[WCDBField] + pub target_id: String, + #[WCDBField] + pub category_id: String, + #[WCDBField] + pub channel_id: String, + #[WCDBField] + pub conversation_title: String, + #[WCDBField] + pub is_top: bool, + #[WCDBField] + pub last_time: i64, + #[WCDBField] + pub channel_type: i32, + #[WCDBField] + pub draft_message: String, +} + +#[derive(WCDBTableCoding)] +#[WCDBTable( + multi_primaries(columns = ["target_id", "category_id", "channel_id"]) +)] +pub struct ConversationTableV3 { + #[WCDBField] + pub target_id: String, + #[WCDBField] + pub category_id: String, + #[WCDBField] + pub channel_id: String, + #[WCDBField] + pub conversation_title: String, + #[WCDBField] + pub is_top: bool, + #[WCDBField(default(i32_value = 3))] + pub status: i32, + #[WCDBField(default(text_value = "default"))] + pub extra_column1: String, +} +impl ConversationTableV3 { + pub fn insert() -> Self { + ConversationTableV3 { + target_id: "t1".to_string(), + category_id: "category_1".to_string(), + channel_id: "channel_1".to_string(), + conversation_title: "default".to_string(), + is_top: true, + status: 0, + extra_column1: "".to_string(), + } + } +} + +#[derive(WCDBTableCoding)] +#[WCDBTable] +pub struct MessageTableV1 { + #[WCDBField] + target_id: String, + #[WCDBField] + category_id: String, + #[WCDBField] + content: String, +} + +impl MessageTableV1 { + pub fn insert(target_id: &str) -> Self { + MessageTableV1 { + target_id: target_id.to_string(), + category_id: "category_1".to_string(), + content: "insert_content".to_string(), + } + } +} + +#[derive(WCDBTableCoding)] +#[WCDBTable] +pub struct MessageTableV1_1 { + #[WCDBField(is_primary = true)] + target_id: String, + #[WCDBField] + category_id: String, + #[WCDBField] + content: String, +} + +#[derive(WCDBTableCoding)] +#[WCDBTable] +pub struct TagTableV1 { + #[WCDBField] + content: String, +} +impl TagTableV1 { + pub fn new() -> Self { + TagTableV1 { + content: "tag".to_string(), + } + } +} + +#[cfg(test)] +pub mod database_upgrade_test { + use crate::database::database_upgrade_test_case::{ + ConversationTableV1, ConversationTableV1_2, DbConversationTableV1, DbConversationTableV1_2, + DbMessageTableV1, DbTagTableV1, MessageTableV1, TagTableV1, + DB_CONVERSATION_TABLE_V1_1_INSTANCE, DB_CONVERSATION_TABLE_V1_2_INSTANCE, + DB_CONVERSATION_TABLE_V1_INSTANCE, DB_CONVERSATION_TABLE_V2_1_INSTANCE, + DB_CONVERSATION_TABLE_V2_INSTANCE, DB_CONVERSATION_TABLE_V3_INSTANCE, + DB_MESSAGE_TABLE_V1_1_INSTANCE, DB_MESSAGE_TABLE_V1_INSTANCE, DB_TAG_TABLE_V1_INSTANCE, + }; + use std::fmt::Pointer; + use std::panic::AssertUnwindSafe; + use std::{panic, thread}; + use wcdb::core::database::Database; + use wcdb::core::handle_operation::HandleOperationTrait; + use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + use wcdb::core::table_orm_operation::TableORMOperationTrait; + use wcdb::orm::table_binding::TableBinding; + use wcdb::winq::column::Column; + use wcdb::winq::identifier::IdentifierTrait; + use wcdb::winq::statement_alter_table::StatementAlterTable; + use wcdb::winq::statement_create_index::StatementCreateIndex; + use wcdb::winq::statement_drop_index::StatementDropIndex; + use wcdb::winq::statement_drop_table::StatementDropTable; + + #[test] + pub fn upgrade() { + { + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database.remove_files().unwrap(); + } + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database + .create_table("ConversationTable", &*DB_CONVERSATION_TABLE_V1_INSTANCE) + .unwrap(); + // insert + let conversation_table = + database.get_table("ConversationTable", &*DB_CONVERSATION_TABLE_V1_INSTANCE); + let insert_result = conversation_table.insert_object( + ConversationTableV1::insert("t1"), + Some(DbConversationTableV1::all_fields()), + ); + assert!(insert_result.is_ok()); + let insert_result = conversation_table.insert_object( + ConversationTableV1::insert("t2"), + Some(DbConversationTableV1::all_fields()), + ); + assert!(insert_result.is_ok()); + database.close(Some(|| {})); + + upgrade_to_v2(); + upgrade_to_v3(); + upgrade_to_v4(); + upgrade_to_v5(); + upgrade_to_v6(); + + { + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database.remove_files().unwrap(); + } + } + + // 从V1升级到V2,升级内容: + // 1.ConversationTable 表增加 channel_type、draft_message、id,字段无默认值 + // 2.id 字段增加自增主键约束 + // 3.给 "target_id", "category_id", "channel_id" 增加索引 + fn upgrade_to_v2() { + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database + .create_table("ConversationTable", &*DB_CONVERSATION_TABLE_V2_INSTANCE) + .unwrap(); + let conversation_table = + database.get_table("ConversationTable", &*DB_CONVERSATION_TABLE_V2_INSTANCE); + let result = conversation_table.get_all_objects(None, None, None, None, None); + assert!(result.is_ok()); + match result { + Ok(vec) => { + for table_v2_item in vec { + assert_eq!(table_v2_item.channel_type, 0); + assert_eq!(table_v2_item.draft_message, ""); + } + } + Err(_) => {} + } + database.close(Some(|| {})); + + // id 字段增加自增主键约束 + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database + .create_table("ConversationTable", &*DB_CONVERSATION_TABLE_V2_1_INSTANCE) + .unwrap(); + let field_id = unsafe { &*(&*DB_CONVERSATION_TABLE_V2_1_INSTANCE).id }; + assert!(field_id.is_auto_increment()); + assert!(field_id.is_primary_key()); + database.close(Some(|| {})); + + // 验证删除索引 + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + let statement_drop_index = StatementDropIndex::new(); + statement_drop_index.drop_index("index1"); + assert_eq!("DROP INDEX index1", statement_drop_index.get_description()); + database.execute(&statement_drop_index).unwrap(); + database.close(Some(|| {})); + + // 手动创建索引 + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + let statement_create_index = StatementCreateIndex::new(); + let column1 = Column::new("target_id", None); + let statement = statement_create_index + .create_index("my_index") + .on("ConversationTable") + .indexed_by(vec![&column1]); + assert_eq!( + statement.get_description(), + "CREATE INDEX my_index ON ConversationTable(target_id)" + ); + database.execute(statement).unwrap(); + database.close(Some(|| {})); + } + + // 从V2升级到V3,升级内容: + // 1.ConversationTable 表增加 status、extra_column1 字段,且有默认值 + // 2.删除 last_time 字段 + // 3.重命名字段 is_top 为 rename_is_top + fn upgrade_to_v3() { + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database + .create_table("ConversationTable", &*DB_CONVERSATION_TABLE_V3_INSTANCE) + .unwrap(); + let conversation_table = + database.get_table("ConversationTable", &*DB_CONVERSATION_TABLE_V3_INSTANCE); + let result = conversation_table.get_all_objects(None, None, None, None, None); + assert!(result.is_ok()); + match result { + Ok(vec) => { + for table_v3_item in vec { + assert_eq!(table_v3_item.status, 3); + assert_eq!(table_v3_item.extra_column1, "default"); + } + } + Err(_) => {} + } + + // 删除字段检查 + let fields = unsafe { &*DB_CONVERSATION_TABLE_V3_INSTANCE.all_binding_fields() }; + for x in fields { + assert_ne!(x.get_name(), "last_time"); + } + + // 重命名字段 + let statement_alter_table = StatementAlterTable::new(); + let column_is_top = Column::new("is_top", None); + let column_rename_is_top = Column::new("rename_is_top", None); + let statement = statement_alter_table + .alter_table("ConversationTable") + .rename_column(&column_is_top) + .to_column(&column_rename_is_top); + database.execute(statement).unwrap(); + + database.close(Some(|| {})); + } + + // 从V3升级到V4,升级内容: + // 1.增加MessageTable表 + // 2.给表增加主键 + // 3.修改表名 + // 4. + fn upgrade_to_v4() { + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database + .create_table("MessageTable", &*DB_MESSAGE_TABLE_V1_INSTANCE) + .unwrap(); + let msg_table = database.get_table("MessageTable", &*DB_MESSAGE_TABLE_V1_INSTANCE); + // insert + let insert_result = msg_table.insert_object( + MessageTableV1::insert("t1"), + Some(DbMessageTableV1::all_fields()), + ); + assert!(insert_result.is_ok()); + + let insert_result = msg_table.insert_object( + MessageTableV1::insert("t2"), + Some(DbMessageTableV1::all_fields()), + ); + assert!(insert_result.is_ok()); + + let result = msg_table.get_all_objects(None, None, None, None, None); + assert!(result.is_ok()); + match result { + Ok(vec) => { + for msg_v1_item in vec { + assert_eq!(msg_v1_item.content, "insert_content"); + } + } + Err(_) => {} + } + database.close(Some(|| {})); + + // 2.给表增加主键 + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database + .create_table("MessageTable", &*DB_MESSAGE_TABLE_V1_1_INSTANCE) + .unwrap(); + + let target_id = unsafe { &*(*&DB_MESSAGE_TABLE_V1_1_INSTANCE.target_id) }; + assert!(target_id.is_primary_key()); + + // 3.修改表名 + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + // database + // .execute_sql("ALTER TABLE MessageTable RENAME TO MsgTable") + // .unwrap(); + + let statement = StatementAlterTable::new(); + statement.alter_table("MessageTable").rename_to("MsgTable"); + database.execute(&statement).unwrap(); + + let msg_table = database.get_table("MsgTable", &*DB_MESSAGE_TABLE_V1_1_INSTANCE); + let result = msg_table.get_all_objects(None, None, None, None, None); + assert!(result.is_ok()); + match result { + Ok(vec) => { + let mut is_t1: bool = false; + let mut is_t2: bool = false; + for msg_v1_item in vec { + if msg_v1_item.target_id == "t1" { + is_t1 = true; + } else if msg_v1_item.target_id == "t2" { + is_t2 = true; + } + } + assert!(is_t1); + assert!(is_t2); + } + Err(_) => {} + } + database.close(Some(|| {})); + } + + // 1.删除 TagTable 表 + fn upgrade_to_v5() { + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database + .create_table("TagTable", &*DB_TAG_TABLE_V1_INSTANCE) + .unwrap(); + let tag_table = database.get_table("TagTable", &*DB_TAG_TABLE_V1_INSTANCE); + // insert + let insert_result = + tag_table.insert_object(TagTableV1::new(), Some(DbTagTableV1::all_fields())); + assert!(insert_result.is_ok()); + + let insert_result = + tag_table.insert_object(TagTableV1::new(), Some(DbTagTableV1::all_fields())); + assert!(insert_result.is_ok()); + + // 删除表 + let statement = StatementDropTable::new(); + statement.drop_table("TagTable").if_exist(); + assert_eq!("DROP TABLE IF EXISTS TagTable", statement.get_description()); + database.execute(&statement).unwrap(); + + let result = tag_table.get_all_objects(None, None, None, None, None); + match result { + Ok(tag_vec) => { + assert!(tag_vec.is_empty()); + } + Err(_) => {} + } + database.close(Some(|| {})); + } + + // 升级中断 + fn upgrade_to_v6() { + { + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database.remove_files().unwrap(); + } + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database + .create_table("ConversationTable", &*DB_CONVERSATION_TABLE_V1_INSTANCE) + .unwrap(); + // insert + let conversation_table = + database.get_table("ConversationTable", &*DB_CONVERSATION_TABLE_V1_INSTANCE); + let mut vec: Vec = Vec::with_capacity(100); + let length = 10000; + for x in 0..length { + vec.push(ConversationTableV1::insert( + &*("x".to_string() + &*String::from(x.to_string())), + )); + } + let insert_result = + conversation_table.insert_objects(vec, Some(DbConversationTableV1::all_fields())); + assert!(insert_result.is_ok()); + + database.close(Some(|| {})); + + // 模拟升级崩溃,ConversationTableV1_1 结构体增加了3个字段,删除了2个字段 + let handle = thread::spawn(move || { + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + let result = panic::catch_unwind(AssertUnwindSafe(|| { + database + .create_table("ConversationTable", &*DB_CONVERSATION_TABLE_V1_1_INSTANCE) + .unwrap(); + })); + if let Err(e) = result {} + }); + thread::sleep(std::time::Duration::from_millis(100)); + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + let is_exist = database.table_exist("ConversationTable").unwrap(); + assert!(is_exist); + handle.join().unwrap(); + let conversation_table = + database.get_table("ConversationTable", &*DB_CONVERSATION_TABLE_V1_1_INSTANCE); + let result = conversation_table.get_all_objects(None, None, None, None, None); + assert!(result.is_ok()); + match result { + Ok(vec) => { + assert_eq!(vec.len(), length); + } + Err(_) => {} + } + database.close(Some(|| {})); + + // 模拟从6个字段的表降级为3个字段的表 + // 结论: + // 1.其他3个字段任然在表里 数据也在 + // 2.当给3个字段的结构体代表的表插入数据时,其他字段数据为空 + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + let statement = StatementDropTable::new(); + statement.drop_table("ConversationTable").if_exist(); + database.execute(&statement).unwrap(); + database + .create_table("ConversationTable", &*DB_CONVERSATION_TABLE_V1_INSTANCE) + .unwrap(); + // insert + let conversation_table = + database.get_table("ConversationTable", &*DB_CONVERSATION_TABLE_V1_INSTANCE); + let mut vec: Vec = Vec::with_capacity(100); + let length = 10; + for x in 0..length { + vec.push(ConversationTableV1::insert( + &*("x".to_string() + &*String::from(x.to_string())), + )); + } + let insert_result = + conversation_table.insert_objects(vec, Some(DbConversationTableV1::all_fields())); + assert!(insert_result.is_ok()); + + database.close(Some(|| {})); + + let database = Database::new("./tests/database/custom/upgrade_db.sqlite3", None); + database + .create_table("ConversationTable", &*DB_CONVERSATION_TABLE_V1_2_INSTANCE) + .unwrap(); + let conversation_table = + database.get_table("ConversationTable", &*DB_CONVERSATION_TABLE_V1_2_INSTANCE); + let result = conversation_table.get_all_objects(None, None, None, None, None); + assert!(result.is_ok()); + let insert_result = conversation_table.insert_object( + ConversationTableV1_2::new(), + Some(DbConversationTableV1_2::all_fields()), + ); + assert!(insert_result.is_ok()); + + let conversation_table = + database.get_table("ConversationTable", &*DB_CONVERSATION_TABLE_V1_2_INSTANCE); + let result = conversation_table.get_all_objects(None, None, None, None, None); + assert!(result.is_ok()); + + database.close(Some(|| {})); + } +} diff --git a/src/rust/examples/tests/database/mod.rs b/src/rust/examples/tests/database/mod.rs new file mode 100644 index 000000000..7b1b91eeb --- /dev/null +++ b/src/rust/examples/tests/database/mod.rs @@ -0,0 +1,5 @@ +pub(crate) mod config_test_case; +pub(crate) mod data_base_test_case; +pub(crate) mod database_upgrade_test_case; +pub(crate) mod repair_test_case; +pub(crate) mod trace_test; diff --git a/src/rust/examples/tests/database/repair_test_case.rs b/src/rust/examples/tests/database/repair_test_case.rs new file mode 100644 index 000000000..1b97485fc --- /dev/null +++ b/src/rust/examples/tests/database/repair_test_case.rs @@ -0,0 +1,544 @@ +use crate::base::base_test_case::TestCaseTrait; +use crate::base::random_tool::RandomTool; +use crate::base::table_test_case::TableTestCase; +use crate::base::test_object::TestObject; +use lazy_static::lazy_static; +use std::sync::{Arc, RwLock}; +use wcdb::base::wcdb_exception::WCDBResult; + +pub struct RepairTest { + table_test_case: TableTestCase, +} + +impl TestCaseTrait for RepairTest { + fn setup(&self) -> WCDBResult<()> { + self.table_test_case.setup() + } + + fn teardown(&self) -> WCDBResult<()> { + self.table_test_case.teardown() + } +} + +impl RepairTest { + pub fn new() -> Self { + RepairTest { + table_test_case: TableTestCase::new(), + } + } + + pub fn get_table_test_case(&self) -> &TableTestCase { + &self.table_test_case + } + + pub fn get_mut_table_test_case(&mut self) -> &mut TableTestCase { + &mut self.table_test_case + } +} + +lazy_static! { + static ref REPAIR_TEST: Arc> = Arc::new(RwLock::new(RepairTest::new())); + static ref PRE_INSERT_OBJECTS: Vec = + RandomTool::auto_increment_test_case_objects(2); +} + +#[cfg(test)] +pub mod repair_test_case { + use crate::base::base_test_case::TestCaseTrait; + use crate::base::file_tool::FileTool; + use crate::base::random_tool::RandomTool; + use crate::base::test_object::{DbTestObject, TestObject, DB_TEST_OBJECT_INSTANCE}; + use crate::base::wrapped_value::WrappedValue; + use crate::database::repair_test_case::{PRE_INSERT_OBJECTS, REPAIR_TEST}; + use std::sync::{Arc, RwLock}; + use std::thread; + use wcdb::base::wcdb_exception::WCDBResult; + use wcdb::core::database::{BackupFilterCallbackTrait, Database}; + use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + use wcdb::core::table_orm_operation::TableORMOperationTrait; + + pub fn setup() { + { + let repair_clone = Arc::clone(&REPAIR_TEST); + let repair_test = repair_clone.read().expect("repair_test write failure"); + repair_test.setup().expect("setup failure"); + } + } + pub fn teardown() { + { + let repair_clone = Arc::clone(&REPAIR_TEST); + let repair_test = repair_clone.read().expect("repair_test write failure"); + repair_test.teardown().expect("teardown failure"); + } + } + + pub fn get_arc_database() -> Arc> { + let repair_clone = Arc::clone(&REPAIR_TEST); + let ret = repair_clone + .read() + .unwrap() + .get_table_test_case() + .get_database(); + Arc::clone(&ret) + } + + pub fn execute_test(execute: Execute) + where + Execute: Fn(), + { + { + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + // database.setCipherKey(null); + let repair_clone = Arc::clone(&REPAIR_TEST); + let mut repair_test = repair_clone.write().expect("repair_test write failure"); + repair_test + .get_mut_table_test_case() + .create_table() + .expect("Create table table failure"); + + let table_name: &str = repair_test.get_table_test_case().get_table_name(); + let database_arc: Arc> = + repair_test.get_table_test_case().get_database(); + let database_clone = Arc::clone(&database_arc); + let database = database_clone.read().unwrap(); + let table = database.get_table(table_name, &*DB_TEST_OBJECT_INSTANCE); + let table_clone = Arc::clone(&table); + + let mut tmp_vec: Vec = Vec::new(); + PRE_INSERT_OBJECTS.iter().for_each(|o: &TestObject| { + tmp_vec.push(TestObject::create_auto_increment_object( + o.content().parse().expect("parse error"), + )); + }); + table_clone + .insert_objects(tmp_vec, Some(DbTestObject::all_fields())) + .expect("insert objects failure"); + } + execute(); + { + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database.remove_files().expect("remove files failure"); + // database.setCipherKey("123".getBytes()); + } + { + let repair_clone = Arc::clone(&REPAIR_TEST); + let mut repair_test = repair_clone.write().expect("repair_test write failure"); + repair_test + .get_mut_table_test_case() + .create_table() + .expect("Create table table failure"); + + let table_name: &str = repair_test.get_table_test_case().get_table_name(); + let database_arc: Arc> = + repair_test.get_table_test_case().get_database(); + let database_clone = Arc::clone(&database_arc); + let database = database_clone.read().unwrap(); + let table = database.get_table(table_name, &*DB_TEST_OBJECT_INSTANCE); + let table_clone = Arc::clone(&table); + + let mut tmp_vec: Vec = Vec::new(); + PRE_INSERT_OBJECTS.iter().for_each(|o: &TestObject| { + tmp_vec.push(TestObject::create_auto_increment_object( + o.content().parse().expect("parse error"), + )); + }); + table + .insert_objects(tmp_vec, Some(DbTestObject::all_fields())) + .expect("insert objects failure"); + } + execute(); + } + + // #[test] + pub fn test_backup() { + setup(); + { + // 执行该测试任务前 可能执行过 backup 存在缓存文件。 + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database + .remove_files() + .expect("The remove_files method failed to be executed"); + } + execute_test(move || { + { + let repair_test = REPAIR_TEST.read().expect("Fails to obtain REPAIR_TEST"); + // /Users/xxx/wcdb_rust/src/rust/wcdb_rust/BaseTestCase/target/tmp/testDatabase-first.material + assert_eq!( + FileTool::file_exist( + &*repair_test + .get_table_test_case() + .get_data_base_test_case() + .first_material_path() + ), + false + ); + assert_eq!( + FileTool::file_exist( + &*repair_test + .get_table_test_case() + .get_data_base_test_case() + .last_material_path() + ), + false + ); + } + + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database + .backup() + .expect("The backup method failed to be executed"); + let repair_test = REPAIR_TEST.read().unwrap(); + assert_eq!( + FileTool::file_exist( + &*repair_test + .get_table_test_case() + .get_data_base_test_case() + .first_material_path() + ), + true + ); + assert_eq!( + FileTool::file_exist( + &*repair_test + .get_table_test_case() + .get_data_base_test_case() + .last_material_path() + ), + false + ); + + thread::sleep(std::time::Duration::from_millis(1000)); + + let ret = database.backup(); + match ret { + Ok(_) => {} + Err(error) => { + // todo dengxudong error + // WCDBNormalException(Level: NoticeCode: CorruptException { Message: "Acquired page number: 3 exceeds the page count: 1." }) + println!("backup error: {:?}", error); + } + } + let repair_test = REPAIR_TEST.read().expect("Fails to obtain REPAIR_TEST"); + assert_eq!( + FileTool::file_exist( + &*repair_test + .get_table_test_case() + .get_data_base_test_case() + .first_material_path() + ), + true + ); + assert_eq!( + FileTool::file_exist( + &*repair_test + .get_table_test_case() + .get_data_base_test_case() + .last_material_path() + ), + true + ); + }); + teardown(); + } + + // #[test] + pub fn test_backup_with_filter() { + setup(); + { + let mut repair_test = REPAIR_TEST.write().expect("Fails to obtain REPAIR_TEST"); + repair_test + .get_mut_table_test_case() + .create_table() + .expect("Table creation failed"); + + let table_name: &str = repair_test.get_table_test_case().get_table_name(); + let database_arc: Arc> = + repair_test.get_table_test_case().get_database(); + let database_clone = Arc::clone(&database_arc); + let database = database_clone.read().unwrap(); + let table = database.get_table(table_name, &*DB_TEST_OBJECT_INSTANCE); + let table_clone = Arc::clone(&table); + + table_clone + .insert_objects( + RandomTool::auto_increment_test_case_objects(2), + Some(DbTestObject::all_fields()), + ) + .expect("Inserting objects failed"); + } + + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + let ret = database.filter_backup::>(None); + assert!(ret.is_ok()); + database + .backup() + .expect("The backup method failed to be executed"); + + { + let mut repair_test = REPAIR_TEST.write().unwrap(); + assert_eq!( + FileTool::file_exist( + &*repair_test + .get_table_test_case() + .get_data_base_test_case() + .first_material_path() + ), + true + ); + assert_eq!( + FileTool::file_exist( + &*repair_test + .get_table_test_case() + .get_data_base_test_case() + .last_material_path() + ), + false + ); + } + let ret = database.filter_backup(Some(|table_name: &str| { + return false; + })); + assert!(ret.is_ok()); + thread::sleep(std::time::Duration::from_millis(1000)); + database + .backup() + .expect("The backup method failed to be executed"); + { + let mut repair_test = REPAIR_TEST.write().unwrap(); + assert_eq!( + FileTool::file_exist( + &*repair_test + .get_table_test_case() + .get_data_base_test_case() + .first_material_path() + ), + true + ); + assert_eq!( + FileTool::file_exist( + &*repair_test + .get_table_test_case() + .get_data_base_test_case() + .last_material_path() + ), + true + ); + assert_eq!( + FileTool::get_file_size( + &*repair_test + .get_table_test_case() + .get_data_base_test_case() + .first_material_path() + ) >= FileTool::get_file_size( + &*repair_test + .get_table_test_case() + .get_data_base_test_case() + .last_material_path() + ), + true + ); + } + teardown(); + } + + // #[test] + pub fn test_auto_backup() { + setup(); + teardown(); + } + + // #[test] + pub fn test_deposit() { + setup(); + // execute_test(|| { + // 0. + // { + // let repair_clone = Arc::clone(&REPAIR_TEST); + // let repair_test = repair_clone.read().unwrap(); + // let table = repair_test.get_table_test_case().get_table(); + // let table_clone = Arc::clone(&table); + // todo dengxudong 需要实现 getValue 方法 + // long num0 = table.getValue(Column.all().count()).getLong(); + // } + // }); + teardown(); + } + + pub fn do_test_retrieve(success: bool) -> WCDBResult<()> { + let last_percentage = Arc::new(RwLock::new(WrappedValue::new())); + let last_percentage_clone = Arc::clone(&last_percentage); + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + let score = database.retrieve(Some(move |percentage, increment| { + assert!( + (percentage - last_percentage_clone.read().unwrap().double_value) == increment + && increment > 0f64 + ); + last_percentage_clone.write().unwrap().double_value = percentage; + return true; + })); + match score { + Ok(val) => { + assert!((success && val == 1.0f64) || (!success && val < 1.0f64)); + } + Err(error) => { + println!("do_test_retrieve error:{:?}", error); + } + } + assert_eq!( + last_percentage.read().unwrap().double_value - 1.0 <= 0.00001, + true + ); + Ok(()) + } + + pub fn do_test_objects_retrieved(success: bool) { + let repair_clone = Arc::clone(&REPAIR_TEST); + let repair_test = repair_clone.read().unwrap(); + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + + let table_name: &str = repair_test.get_table_test_case().get_table_name(); + + let objects = database.get_all_objects( + DbTestObject::all_fields(), + table_name, + None, + None, + None, + None, + ); + match objects { + Ok(object_vec) => { + if success { + assert_eq!(object_vec.is_empty(), false); + let mut tmp_vec: Vec = Vec::new(); + PRE_INSERT_OBJECTS.iter().for_each(|o: &TestObject| { + tmp_vec.push(TestObject::create_auto_increment_object( + o.content().parse().unwrap(), + )); + }); + // assert_eq!(object_vec, tmp_vec); + } else { + assert_eq!(object_vec.is_empty(), true); + } + } + Err(error) => { + println!( + "do_test_objects_retrieved -> get_all_objects error:{:?}, table_name: {1}", + error, table_name + ); + } + } + } + + #[test] + pub fn test_retrieve_with_backup_and_deposit() { + setup(); + execute_test(|| { + { + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database + .backup() + .expect("The backup method failed to be executed"); + database.deposit().expect("Deposit failed"); + } + { + let repair_clone = Arc::clone(&REPAIR_TEST); + let repair_test = repair_clone.read().expect("Fails to obtain REPAIR_TEST"); + repair_test + .get_table_test_case() + .get_data_base_test_case() + .corrupt_header(); + } + do_test_retrieve(true).expect("Failed to retrieve database"); + do_test_objects_retrieved(true); + }); + teardown(); + } + + // #[test] + pub fn test_retrieve_with_backup_and_without_deposit() { + setup(); + execute_test(|| { + { + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database + .backup() + .expect("The backup method failed to be executed"); + } + { + let repair_clone = Arc::clone(&REPAIR_TEST); + let repair_test = repair_clone.read().expect("Fails to obtain REPAIR_TEST"); + repair_test + .get_table_test_case() + .get_data_base_test_case() + .corrupt_header(); + } + do_test_retrieve(true).expect("Failed to retrieve database"); + do_test_objects_retrieved(true); + }); + teardown(); + } + + // #[test] + // todo dengxudong error + // 方法 do_test_retrieve(false) 中 score 始终返回 1 测试 java 中该方法返回的是 0.5 + pub fn test_retrieve_without_backup_and_deposit() { + setup(); + execute_test(|| { + { + let repair_clone = Arc::clone(&REPAIR_TEST); + let repair_test = repair_clone.read().expect("Fails to obtain REPAIR_TEST"); + repair_test + .get_table_test_case() + .get_data_base_test_case() + .corrupt_header(); + } + { + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database.deposit().expect("Deposit failed"); + } + do_test_retrieve(false).expect("Failed to retrieve database"); + do_test_objects_retrieved(false); + }); + teardown(); + } + + // #[test] + pub fn test_vacuum() { + setup(); + execute_test(|| { + let last_percentage = Arc::new(RwLock::new(WrappedValue::new())); + let last_percentage_clone = Arc::clone(&last_percentage); + let ret = { + let database_arc = get_arc_database(); + let database = database_arc.read().unwrap(); + database.vacuum(Some(move |percentage, increment| { + assert!( + (percentage - last_percentage_clone.read().unwrap().double_value) + == increment + && increment > 0f64 + ); + last_percentage_clone.write().unwrap().double_value = percentage; + return true; + })) + }; + assert!(ret.is_ok()); + assert_eq!( + last_percentage.read().unwrap().double_value - 1.0 <= 0.00001, + true + ); + do_test_objects_retrieved(true) + }); + teardown(); + } +} diff --git a/src/rust/examples/tests/database/trace_test.rs b/src/rust/examples/tests/database/trace_test.rs new file mode 100644 index 000000000..97d066ebe --- /dev/null +++ b/src/rust/examples/tests/database/trace_test.rs @@ -0,0 +1,448 @@ +use crate::base::random_tool::RandomTool; +use crate::base::table_test_case::TableTestCase; +use crate::base::test_object::{DbTestObject, TestObject}; +use crate::base::wrapped_value::WrappedValue; +use std::sync::{Arc, Mutex}; +use wcdb::base::wcdb_exception::{ExceptionCode, ExceptionLevel, WCDBException}; +use wcdb::core::database::{ + Database, PerformanceInfo, TraceExceptionCallback, TracePerformanceCallback, TraceSqlCallback, +}; +use wcdb::core::handle_operation::HandleOperationTrait; +use wcdb::core::handle_orm_operation::HandleORMOperationTrait; +use wcdb::winq::column::ColumnTrait; +use wcdb::winq::identifier::IdentifierTrait; +use wcdb::winq::ordering_term::Order; +use wcdb::winq::pragma::Pragma; +use wcdb::winq::statement_create_index::StatementCreateIndex; +use wcdb::winq::statement_pragma::StatementPragma; +use wcdb::winq::statement_select::StatementSelect; + +static TABLE_NAME: &'static str = "trace_test_case"; +pub struct TraceTest { + table_test_case: TableTestCase, +} + +impl TraceTest { + pub fn new() -> Self { + TraceTest { + table_test_case: TableTestCase::new_with_table_name(TABLE_NAME), + } + } +} + +impl TraceTest { + pub fn test_trace_sql(&self) { + let statement = StatementPragma::new(); + let statement = statement.pragma(Pragma::user_version()); + let desc = statement.get_description(); + let mut tested = Arc::new(Mutex::new(WrappedValue::new())); + let tested_clone = tested.clone(); + + let database_arc = self.table_test_case.get_database().clone(); + let database = database_arc.read().unwrap(); + let db_tag = database.get_tag(); + let db_path = database.get_path(); + + database + .trace_sql(Some( + move |tag: i64, path: String, handle_id: i64, sql: String, info: String| { + assert_eq!(tag, db_tag); + assert_eq!(path, db_path); + if sql.eq(desc.as_str()) { + tested.lock().unwrap().bool_value = true; + } + }, + )) + .unwrap(); + + database.execute(statement).unwrap(); + assert!(tested_clone.lock().unwrap().bool_value); + database.trace_sql::(None).unwrap(); + } + + pub fn test_global_trace_sql(&self) { + let database_arc = self.table_test_case.get_database().clone(); + let database = database_arc.read().unwrap(); + database.remove_files().unwrap(); + + let statement = StatementPragma::new(); + let statement = statement.pragma(Pragma::user_version()); + let desc = statement.get_description(); + let mut tested = Arc::new(Mutex::new(WrappedValue::new())); + let tested_clone = tested.clone(); + let db_tag = database.get_tag(); + let db_path = database.get_path(); + + Database::global_trace_sql::(None).unwrap(); + Database::global_trace_sql(Some( + move |tag: i64, path: String, handle_id: i64, sql: String, info: String| { + if !path.eq(db_path.as_str()) { + return; + } + assert_eq!(tag, db_tag); + if sql.eq(desc.as_str()) { + tested.lock().unwrap().bool_value = true; + } + }, + )) + .unwrap(); + database.execute(statement).unwrap(); + assert!(tested_clone.lock().unwrap().bool_value); + Database::global_trace_sql::(None).unwrap(); + } + + pub fn test_trace_performance(&self) { + let database_arc = self.table_test_case.get_database().clone(); + let database = database_arc.read().unwrap(); + database.remove_files().unwrap(); + + self.table_test_case.create_table().unwrap(); + let mut objects = vec![]; + let obj_size = 1000; + for _ in 0..obj_size { + let content = RandomTool::string_by_length(4096); + let object = TestObject::new(content); + objects.push(object); + } + + let mut test_count = Arc::new(Mutex::new(WrappedValue::new())); + let test_count_clone = test_count.clone(); + let db_tag = database.get_tag(); + let db_path = database.get_path(); + + database + .trace_performance::(None) + .unwrap(); + database + .trace_performance(Some( + move |tag: i64, + path: String, + handle_id: i64, + sql: String, + info: PerformanceInfo| { + assert_eq!(tag, db_tag); + assert_eq!(path, db_path); + if sql.starts_with("COMMIT") { + assert!(info.cost_in_nanoseconds > 0); + assert!(info.table_page_write_count > 0); + assert_eq!(0, info.index_page_write_count); + assert!(info.overflow_page_write_count > 0); + assert_eq!(0, info.table_page_read_count); + assert_eq!(0, info.index_page_read_count); + assert_eq!(0, info.overflow_page_read_count); + test_count.lock().unwrap().int_value += 1; + } else if sql.starts_with("CREATE INDEX") { + assert!(info.cost_in_nanoseconds > 0); + assert_eq!(1, info.table_page_write_count); + assert!(info.index_page_write_count > 0); + assert_eq!(info.overflow_page_write_count, obj_size); + assert!(info.table_page_read_count > 0); + assert!(info.index_page_read_count >= 0); + assert!(info.overflow_page_read_count > obj_size / 2); + test_count.lock().unwrap().int_value += 1; + } else if sql.starts_with("SELECT") { + assert!(info.cost_in_nanoseconds > 0); + assert_eq!(0, info.table_page_write_count); + assert_eq!(0, info.index_page_write_count); + assert_eq!(0, info.overflow_page_write_count); + test_count.lock().unwrap().int_value += 1; + if sql.ends_with("ORDER BY content DESC") { + assert_eq!(0, info.table_page_read_count); + assert!(info.index_page_read_count > 0); + assert_eq!(info.overflow_page_read_count, obj_size); + } else { + assert!(info.table_page_read_count > 0); + assert_eq!(0, info.index_page_read_count); + assert_eq!(info.overflow_page_read_count, obj_size); + } + } + }, + )) + .unwrap(); + + database + .insert_objects(objects, DbTestObject::all_fields(), TABLE_NAME) + .unwrap(); + + database + .execute( + StatementCreateIndex::new() + .create_index("testIndex") + .on(TABLE_NAME) + .indexed_by(vec![DbTestObject::content()]), + ) + .unwrap(); + + assert_eq!( + database + .get_all_objects( + DbTestObject::all_fields(), + TABLE_NAME, + None, + None, + None, + None + ) + .unwrap() + .len(), + obj_size as usize + ); + + assert_eq!( + database + .get_all_objects( + DbTestObject::all_fields(), + TABLE_NAME, + None, + Some(&DbTestObject::content().order(Order::Desc)), + None, + None + ) + .unwrap() + .len(), + obj_size as usize + ); + + assert_eq!(test_count_clone.lock().unwrap().int_value, 4); + database + .trace_performance::(None) + .unwrap(); + } + + pub fn test_global_trace_performance(&self) { + let database_arc = self.table_test_case.get_database().clone(); + let database = database_arc.read().unwrap(); + database.remove_files().unwrap(); + let mut objects = vec![]; + let obj_size = 1000; + for _ in 0..obj_size { + let content = RandomTool::string_by_length(4096); + let object = TestObject::new(content); + objects.push(object); + } + + let mut test_count = Arc::new(Mutex::new(WrappedValue::new())); + let test_count_clone = test_count.clone(); + let mut last_sql_is_insert = Arc::new(Mutex::new(WrappedValue::new())); + + let db_tag = database.get_tag(); + let db_path = database.get_path(); + + Database::global_trace_performance::(None).unwrap(); + Database::global_trace_performance(Some( + move |tag: i64, path: String, handle_id: i64, sql: String, info: PerformanceInfo| { + if !path.eq(&db_path) { + return; + } + assert_eq!(tag, db_tag); + if sql.starts_with("COMMIT") && last_sql_is_insert.lock().unwrap().bool_value { + assert!(info.cost_in_nanoseconds > 0); + assert!(info.table_page_write_count >= 0); + assert!(info.index_page_write_count >= 0); + assert!(info.overflow_page_write_count >= 0); + assert_eq!(0, info.table_page_read_count); + assert_eq!(0, info.index_page_read_count); + assert_eq!(0, info.overflow_page_read_count); + test_count.lock().unwrap().int_value += 1; + } else if sql.starts_with("CREATE INDEX") { + assert!(info.cost_in_nanoseconds > 0); + assert_eq!(1, info.table_page_write_count); + assert!(info.index_page_write_count > 0); + assert_eq!(info.overflow_page_write_count, obj_size); + assert!(info.table_page_read_count > 0); + assert!(info.index_page_read_count >= 0); + assert!(info.overflow_page_read_count > obj_size / 2); + test_count.lock().unwrap().int_value += 1; + } else if sql.starts_with("SELECT") { + assert!(info.cost_in_nanoseconds > 0); + assert_eq!(0, info.table_page_write_count); + assert_eq!(0, info.index_page_write_count); + assert_eq!(0, info.overflow_page_write_count); + test_count.lock().unwrap().int_value += 1; + if sql.ends_with("ORDER BY content DESC") { + assert_eq!(0, info.table_page_read_count); + assert!(info.index_page_read_count > 0); + assert_eq!(info.overflow_page_read_count, obj_size); + } else { + assert!(info.table_page_read_count > 0); + assert_eq!(0, info.index_page_read_count); + assert_eq!(info.overflow_page_read_count, obj_size); + } + } + last_sql_is_insert.lock().unwrap().bool_value = sql.starts_with("INSERT"); + }, + )) + .unwrap(); + + self.table_test_case.create_table().unwrap(); + database + .insert_objects(objects, DbTestObject::all_fields(), TABLE_NAME) + .unwrap(); + + database + .execute( + StatementCreateIndex::new() + .create_index("testIndex") + .on(TABLE_NAME) + .indexed_by(vec![DbTestObject::content()]), + ) + .unwrap(); + + assert_eq!( + database + .get_all_objects( + DbTestObject::all_fields(), + TABLE_NAME, + None, + None, + None, + None + ) + .unwrap() + .len(), + obj_size as usize + ); + + assert_eq!( + database + .get_all_objects( + DbTestObject::all_fields(), + TABLE_NAME, + None, + Some(&DbTestObject::content().order(Order::Desc)), + None, + None + ) + .unwrap() + .len(), + obj_size as usize + ); + + assert_eq!(test_count_clone.lock().unwrap().int_value, 4); + Database::global_trace_performance::(None).unwrap(); + } + + pub fn test_trace_error(&self) { + let mut tested = Arc::new(Mutex::new(WrappedValue::new())); + let tested_clone = tested.clone(); + let database_arc = self.table_test_case.get_database().clone(); + let database = database_arc.read().unwrap(); + let db_tag = database.get_tag(); + let db_path = database.get_path(); + database + .trace_exception(Some(move |exception: WCDBException| { + if exception.level == ExceptionLevel::Error + && exception.path().eq(db_path.as_str()) + && exception.tag() == db_tag + && exception.code == ExceptionCode::Error + && exception.sql().eq("SELECT 1 FROM dummy") + && exception.message().eq("no such table: dummy") + { + tested.lock().unwrap().bool_value = true; + } + })) + .unwrap(); + + assert!(database.can_open()); + let ret = database.execute( + StatementSelect::new() + .select(vec!["1"]) + .from(vec!["dummy"] /* table_subquery_obj_vec */), + ); + match ret { + Ok(_) => { + assert!(tested_clone.lock().unwrap().bool_value); + } + Err(_) => { + assert!(tested_clone.lock().unwrap().bool_value); + } + } + database + .trace_exception::(None) + .unwrap(); + } + + pub fn test_global_trace_error(&self) { + let mut tested = Arc::new(Mutex::new(WrappedValue::new())); + let tested_clone = tested.clone(); + let database_arc = self.table_test_case.get_database().clone(); + let database = database_arc.read().unwrap(); + let db_tag = database.get_tag(); + let db_path = database.get_path(); + + Database::global_trace_exception::(None).unwrap(); + + Database::global_trace_exception(Some(move |exception: WCDBException| { + if exception.level == ExceptionLevel::Error + && exception.path().eq(db_path.as_str()) + && exception.tag() == db_tag + && exception.code == ExceptionCode::Error + && exception.sql().eq("SELECT 1 FROM dummy") + && exception.message().eq("no such table: dummy") + { + tested.lock().unwrap().bool_value = true; + } + })) + .unwrap(); + + assert!(database.can_open()); + let ret = database.execute( + StatementSelect::new() + .select(vec!["1".to_string()]) + .from(vec!["dummy"]), + ); + match ret { + Ok(_) => { + assert!(tested_clone.lock().unwrap().bool_value); + } + Err(_) => { + assert!(tested_clone.lock().unwrap().bool_value); + } + } + Database::global_trace_exception::(None).unwrap(); + } +} + +#[cfg(test)] +pub mod trace_test { + use crate::database::trace_test::TraceTest; + + // #[test] + // todo qixinbing 单独运行正常,但是集成测试会失败 + pub fn test_trace_sql() { + let trace_test = TraceTest::new(); + trace_test.test_trace_sql(); + } + + #[test] + pub fn test_global_trace_sql() { + let trace_test = TraceTest::new(); + trace_test.test_global_trace_sql(); + } + + // #[test] + // todo qixinbing 单独运行正常,但是集成测试会失败 + pub fn test_trace_performance() { + let trace_test = TraceTest::new(); + trace_test.test_trace_performance(); + } + + // #[test] + // todo qixinbing 本地单独、集测运行正常,但是 ci 集成测试会失败 + pub fn test_global_trace_performance() { + let trace_test = TraceTest::new(); + trace_test.test_global_trace_performance(); + } + + #[test] + pub fn test_trace_error() { + let trace_test = TraceTest::new(); + trace_test.test_trace_error(); + } + + #[test] + pub fn test_global_trace_error() { + let trace_test = TraceTest::new(); + trace_test.test_global_trace_error(); + } +} diff --git a/src/rust/examples/tests/db_corrupted/corrupted_base_test_case.rs b/src/rust/examples/tests/db_corrupted/corrupted_base_test_case.rs new file mode 100644 index 000000000..30378d067 --- /dev/null +++ b/src/rust/examples/tests/db_corrupted/corrupted_base_test_case.rs @@ -0,0 +1,107 @@ +use crate::db_corrupted::testclass::table_goods_object::{ + DbTableGoodsObject, TableGoodsObject, DB_TABLE_GOODS_OBJECT_INSTANCE, +}; +use wcdb::base::wcdb_exception::WCDBException; +use wcdb::core::database::Database; +use wcdb::core::handle_orm_operation::HandleORMOperationTrait; +use wcdb::core::table_orm_operation::TableORMOperationTrait; + +pub struct CorruptedBaseTestCase { + database: Database, + db_name: String, + table_name: String, +} + +impl CorruptedBaseTestCase { + pub fn new(db_name: &str, auto_backup: bool) -> Self { + let path = format!("./target/tmp/{}", db_name); + let table_name = "table_goods_object"; + let test = CorruptedBaseTestCase { + database: Database::new(path.as_str(), None), + db_name: db_name.to_string(), + table_name: table_name.to_string(), + }; + test.database.enable_auto_backup(auto_backup); + test + } + + pub fn database(&self) -> &Database { + &self.database + } + + pub fn db_name(&self) -> String { + self.db_name.clone() + } + + pub fn table_name(&self) -> String { + self.table_name.clone() + } + + pub fn setup(&self) { + self.database + .create_table(self.table_name.as_str(), &*DB_TABLE_GOODS_OBJECT_INSTANCE) + .unwrap(); + } + + pub fn teardown(&self, delete_all: bool) { + if delete_all { + self.delete_all(); + } + } + + pub fn insert_objects(&self, data_num: i32) { + let obj_vec = TableGoodsObject::get_obj_vec(data_num); + + let table = self + .database + .get_table(self.table_name.as_str(), &*DB_TABLE_GOODS_OBJECT_INSTANCE); + table + .insert_objects(obj_vec, Some(DbTableGoodsObject::all_fields())) + .unwrap(); + } + + pub fn get_all_objects(&self) -> Vec { + let table = self + .database + .get_table(self.table_name.as_str(), &*DB_TABLE_GOODS_OBJECT_INSTANCE); + + table.get_all_objects(None, None, None, None, None).unwrap() + } + + fn delete_all(&self) { + let path = "./target/tmp"; + let db_name = format!("{}/{}", path, self.db_name); + for item in std::fs::read_dir(path).unwrap() { + let path = item.unwrap().path(); + if path.starts_with(db_name.as_str()) { + std::fs::remove_file(path).unwrap(); + } + } + } + + pub fn trace_exception(&self, exp_msg: &str) { + let exp_msg_string = exp_msg.to_string(); + let ret = self + .database() + .trace_exception(Some(move |exception: WCDBException| { + let msg = exception.message(); + println!("trace_exception: {}", msg); + if msg.starts_with(exp_msg_string.as_str()) { + assert!(true); + } + })); + assert!(ret.is_ok()); + } + + pub fn has_back_up(&self) -> bool { + let first_material = format!("./target/tmp/{}-first.material", self.db_name); + let incremental_material = format!("./target/tmp/{}-incremental.material", self.db_name); + if !std::path::Path::new(first_material.as_str()).exists() { + return false; + } + if !std::path::Path::new(incremental_material.as_str()).exists() { + return false; + } + true + } +} diff --git a/src/rust/examples/tests/db_corrupted/delete_db_file_test.rs b/src/rust/examples/tests/db_corrupted/delete_db_file_test.rs new file mode 100644 index 000000000..63c3b8585 --- /dev/null +++ b/src/rust/examples/tests/db_corrupted/delete_db_file_test.rs @@ -0,0 +1,158 @@ +use crate::db_corrupted::corrupted_base_test_case::CorruptedBaseTestCase; + +struct DeleteDbFileTest { + test_case: CorruptedBaseTestCase, +} + +impl DeleteDbFileTest { + pub fn new(db_name: &str, auto_backup: bool) -> Self { + let test_case = CorruptedBaseTestCase::new(db_name, auto_backup); + DeleteDbFileTest { test_case } + } + + pub fn test_case(&self) -> &CorruptedBaseTestCase { + &self.test_case + } + + pub fn delete_db_file(&self) { + let path = format!("./target/tmp/{}", self.test_case().db_name()); + std::fs::remove_file(path).expect("delete db file failed"); + } +} + +#[cfg(test)] +pub mod delete_db_file_test_exception { + use crate::db_corrupted::delete_db_file_test::DeleteDbFileTest; + use wcdb::core::handle_operation::HandleOperationTrait; + + pub fn delete_db_file_when_write_operation(db_name: &str) { + let delete_db_file_test = DeleteDbFileTest::new(db_name, true); + delete_db_file_test.test_case().setup(); + + let data_num = 10; + delete_db_file_test.test_case().insert_objects(data_num); + + delete_db_file_test.delete_db_file(); + } + + // #[test] //todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_delete_db_file_then_write_exception() { + let db_name = "delete_db_file_when_write_operation.db"; + let delete_db_file_test = DeleteDbFileTest::new(db_name, true); + delete_db_file_test.test_case().setup(); + + delete_db_file_test + .test_case() + .trace_exception("file unlinked while open"); + + let data_num = 10; + delete_db_file_test.test_case().insert_objects(data_num); + + delete_db_file_test.delete_db_file(); + + // trace_exception: file unlinked while open: path/to/target/tmp/delete_db_file_when_write_operation.db + delete_db_file_test.test_case().insert_objects(data_num); + } + + // #[test] //todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_delete_db_file_then_write_back_exception() { + let db_name = "delete_db_file_when_write_operation.db"; + let delete_db_file_test = DeleteDbFileTest::new(db_name, true); + delete_db_file_test.test_case().setup(); + + delete_db_file_test + .test_case() + .trace_exception("file unlinked while open"); + + let data_num = 10; + delete_db_file_test.test_case().insert_objects(data_num); + + delete_db_file_test.delete_db_file(); + + // trace_exception: file unlinked while open: path/to/target/tmp/delete_db_file_when_write_operation.db + let sql = "PRAGMA wal_checkpoint(FULL);"; + delete_db_file_test + .test_case() + .database() + .execute_sql(sql) + .unwrap(); + } + + // #[test] //todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_delete_db_file_then_retrieve_exception() { + let db_name = "delete_db_file_when_write_operation.db"; + let delete_db_file_test = DeleteDbFileTest::new(db_name, true); + delete_db_file_test.test_case().setup(); + + delete_db_file_test + .test_case() + .trace_exception("file unlinked while open"); + + let data_num = 10; + delete_db_file_test.test_case().insert_objects(data_num); + + delete_db_file_test.delete_db_file(); + + // // trace_exception: file unlinked while open: path/to/target/tmp/delete_db_file_when_write_operation.db + let _ = delete_db_file_test.test_case().database().retrieve(Some( + move |percentage: f64, increment: f64| { + println!( + "Database retrieve percentage:{} , increment:{}", + percentage, increment + ); + if percentage >= 1.0 { + println!("Database retrieve complete"); + } + true + }, + )); + } + + // #[test] //todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_delete_db_file_when_backup_then_write_back_exception() { + let db_name = "delete_db_file_when_write_success.db"; + let delete_db_file_test = DeleteDbFileTest::new(db_name, true); + delete_db_file_test.test_case().setup(); + + let data_num = 10; + delete_db_file_test.test_case().insert_objects(data_num); + + let sql = "PRAGMA wal_checkpoint(FULL);"; + delete_db_file_test + .test_case() + .database() + .execute_sql(sql) + .unwrap(); + + delete_db_file_test.test_case().database().backup().unwrap(); + + delete_db_file_test.delete_db_file(); + + let _ = delete_db_file_test.test_case().database().retrieve(Some( + move |percentage: f64, increment: f64| { + println!( + "Database retrieve percentage:{} , increment:{}", + percentage, increment + ); + if percentage >= 1.0 { + println!("Database retrieve complete"); + } + true + }, + )); + + // 无法恢复,表结构没有,数据也没有 + let ret = delete_db_file_test.test_case().database().execute_sql(sql); + match ret { + Ok(_) => { + assert!(true); + } + Err(ex) => { + assert_eq!( + ex.message(), + "no such table: table_goods_object".to_string() + ); + } + } + } +} diff --git a/src/rust/examples/tests/db_corrupted/delete_wal_shm_test.rs b/src/rust/examples/tests/db_corrupted/delete_wal_shm_test.rs new file mode 100644 index 000000000..dbf9f4b5d --- /dev/null +++ b/src/rust/examples/tests/db_corrupted/delete_wal_shm_test.rs @@ -0,0 +1,294 @@ +use crate::db_corrupted::corrupted_base_test_case::CorruptedBaseTestCase; +use wcdb::core::handle_orm_operation::HandleORMOperationTrait; +use wcdb::core::table_orm_operation::TableORMOperationTrait; + +struct DeleteWalTest { + test_case: CorruptedBaseTestCase, +} + +impl DeleteWalTest { + pub fn new(db_name: &str, auto_backup: bool) -> DeleteWalTest { + let test_case = CorruptedBaseTestCase::new(db_name, auto_backup); + DeleteWalTest { test_case } + } + + pub fn test_case(&self) -> &CorruptedBaseTestCase { + &self.test_case + } + + pub fn delete_wal_shm(&self) { + let db_name = self.test_case.db_name(); + let path = format!("./target/tmp/{}-wal", db_name); + if std::path::Path::new(path.as_str()).exists() { + std::fs::remove_file(path.as_str()).unwrap(); + } + + let path = format!("./target/tmp/{}-shm", db_name); + if std::path::Path::new(path.as_str()).exists() { + std::fs::remove_file(path.as_str()).unwrap(); + } + } +} + +#[cfg(test)] +pub mod delete_wal_shm_exception_test { + use crate::db_corrupted::delete_wal_shm_test::DeleteWalTest; + use wcdb::base::wcdb_exception::WCDBException; + use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + + // 测试删除 wal 文件的效果 + // #[test] //todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_check_delete_wal() { + // 只删除 wal 文件 + // 报错 1 no such table: main.table_goods_object + // 报错 2 file unlinked while open: /path/to/wcdb_rust/target/tmp/db_for_delete_wal.db + // 表现:数据库可以正常的插入、查询数据,但是无法被数据库软件打开 + let db_name = "db_for_check_delete_wal.db"; + let delete_wal_test = DeleteWalTest::new(db_name, false); + let table_name = delete_wal_test.test_case().table_name().clone(); + let data_num = 100; + + let ret = delete_wal_test.test_case().database().trace_exception(Some( + move |exception: WCDBException| { + let msg = exception.message(); + // println!("trace_exception: {}",msg); + + // 检测报错 1 + if msg.starts_with("no such table") { + // "no such table: main.table_goods_object" + let expect_msg = format!("no such table: main.{}", table_name); + assert_eq!(msg, expect_msg); + return; + } + + // 检测报错 2 + if msg.starts_with("file unlinked while open") { + assert!(true); + return; + } + + // 遇到其他错误则测试失败 + assert!(false); + }, + )); + assert!(ret.is_ok()); + + delete_wal_test.test_case().setup(); + delete_wal_test.test_case().insert_objects(data_num); + + delete_wal_test.test_case().database().backup().unwrap(); + + // delete_wal_test.delete_wal(); + // 验证可以正常插入和查询数据 + delete_wal_test.test_case().insert_objects(100); + assert_eq!(delete_wal_test.test_case().get_all_objects().len(), 200); + + delete_wal_test.test_case().teardown(true); + } + + // warning: 本用例手动执行两次做完整的测试 + // 备份后之后删除 wal,打开数据库后恢复。结论:表可以恢复,但是数据没了 + // 第一次运行,没有备份,用例直接跳过 + // 第二次运行,存在备份了,再做修复逻辑 + // #[test] //todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_retrieve_delete_wal_empty_data() { + // 当前进程结束之后,wcdb 才能生成备份文件,如果第一次打开就主动损坏,那就没有备份文件,就无法恢复数据库 + // 使用之后第二次 打开,才能恢复数据 + + // 开启自动备份 + let db_name = "db_for_retrieve_delete_wal_empty_data.db"; + let delete_wal_test = DeleteWalTest::new(db_name, true); + let table_name = delete_wal_test.test_case().table_name().clone(); + let has_back_up = delete_wal_test.test_case().has_back_up(); + let data_num = 100; + + let ret = delete_wal_test.test_case().database().trace_exception(Some( + move |exception: WCDBException| { + let msg = exception.message(); + println!("trace_exception: {}", msg); + + // 恢复数据报错 + // trace_exception: Acquired page number: 4 exceeds the page count: 1. + // trace_exception: Acquired page number: 5 exceeds the page count: 1. + // trace_exception: Acquired page number: 6 exceeds the page count: 1. + }, + )); + assert!(ret.is_ok()); + + delete_wal_test.test_case().setup(); + delete_wal_test.test_case().insert_objects(data_num); + + if !has_back_up { + // 没备份,只插入了 100 个数据 + assert_eq!( + delete_wal_test.test_case().get_all_objects().len() as i32, + data_num + ); + delete_wal_test.test_case().database().backup().unwrap(); + } else { + delete_wal_test.delete_wal_shm(); + + // 主动删除 wal 之后验证可以正常插入和查询数据 + delete_wal_test.test_case().insert_objects(data_num); + assert!(delete_wal_test.test_case().get_all_objects().len() > 0); + + delete_wal_test + .test_case() + .database() + .retrieve(Some(move |percentage: f64, increment: f64| { + println!( + "Database retrieve percentage:{} , increment:{}", + percentage, increment + ); + if percentage >= 1.0 { + println!("Database retrieve complete"); + } + true + })) + .unwrap(); + + // 恢复成功后,所有数据都没了,重新插入并验证数量 + delete_wal_test.test_case().insert_objects(data_num); + assert_eq!( + delete_wal_test.test_case().get_all_objects().len() as i32, + data_num + ); + + delete_wal_test.test_case().teardown(true); + } + } +} + +pub mod delete_wal_shm_success_test { + use crate::db_corrupted::delete_wal_shm_test::DeleteWalTest; + use wcdb::base::wcdb_exception::WCDBException; + use wcdb::core::handle_operation::HandleOperationTrait; + + // 手动回写用例连续调用两次做完整的测试 + // 第一次写入数据,并手动回写 wal 文件 + // 第二次删除 wal,恢复数据库,并验证之前的数据恢复 + // #[test] //todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_retrieve_delete_wal_manual_success() { + let db_name = "db_for_retrieve_delete_wal_manual_success.db"; + let delete_wal_test = DeleteWalTest::new(db_name, true); + let has_back_up = delete_wal_test.test_case().has_back_up(); + let data_num = 100; + + let ret = delete_wal_test.test_case().database().trace_exception(Some( + move |exception: WCDBException| { + let msg = exception.message(); + println!("trace_exception: {}", msg); + }, + )); + assert!(ret.is_ok()); + + if !has_back_up { + // 第一次插入 100 个数据 + delete_wal_test.test_case().setup(); + delete_wal_test.test_case().insert_objects(data_num); + // 主动备份 + delete_wal_test.test_case().database().backup().unwrap(); + assert_eq!( + delete_wal_test.test_case().get_all_objects().len() as i32, + data_num + ); + // 把 wal shm 数据写回 db 文件,确保删除 wal shm 可以正常恢复数据 + // 如果 wal 文件过大,会导致,回写会有性能问题,需要设计回写策略 + let sql = "PRAGMA wal_checkpoint(FULL);"; + delete_wal_test + .test_case() + .database() + .execute_sql(sql) + .unwrap(); + } else { + delete_wal_test.delete_wal_shm(); + delete_wal_test + .test_case() + .database() + .retrieve(Some(move |percentage: f64, increment: f64| { + println!( + "Database retrieve percentage:{} , increment:{}", + percentage, increment + ); + if percentage >= 1.0 { + println!("Database retrieve complete"); + } + true + })) + .unwrap(); + + // 恢复后再插入 100 个数据,检查是否为 200 个 + delete_wal_test.test_case().insert_objects(data_num); + assert_eq!( + delete_wal_test.test_case().get_all_objects().len() as i32, + data_num * 2 + ); + + delete_wal_test.test_case().teardown(true); + } + } + + // wal shm 回写主库之后,不管有没有开启备份,删除 wal shm,都可以正常恢复数据库 + // 设置自动回写之后,超过 page_size 的 wal shm 文件,会自动回写主库,这部分数据不会丢 + // 低于 page_size 的数据是存在 wal shm 文件中,需要手动回写主库,才能恢复数据,不会写则会丢失 + // 第一次写入数据并关闭 + // 第二次删除 wal,恢复数据库,并验证之前的数据恢复 + // #[test] //todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_retrieve_delete_wal_write_back_success() { + let db_name = "db_for_retrieve_delete_wal_page_size_success.db"; + let delete_wal_test = DeleteWalTest::new(db_name, false); + + // 100 * 4k = 400k,即超过 400k 的 wal shm 文件,会自动回写主库 + // 默认应该设置为 1000,可以根据设备硬件动态调整,比如低端硬件可以降低 page_size + let page_size = 100; + let sql = format!("PRAGMA wal_autocheckpoint={};", page_size); + delete_wal_test + .test_case() + .database() + .execute_sql(sql.as_str()) + .unwrap(); + + let data_num = 1000; + delete_wal_test.test_case().setup(); + delete_wal_test.test_case().insert_objects(data_num); + + // 第一次只插入 1000 个数据 + if delete_wal_test.test_case().get_all_objects().len() <= (data_num as usize) { + return; + } + + println!( + "database retrieve count before {}", + delete_wal_test.test_case().get_all_objects().len() + ); + + // 第二次运行手动删除 wal shm 文件,并验证是否恢复成功 + delete_wal_test.delete_wal_shm(); + delete_wal_test + .test_case() + .database() + .retrieve(Some(move |percentage: f64, increment: f64| { + println!( + "Database retrieve percentage:{} , increment:{}", + percentage, increment + ); + if percentage >= 1.0 { + println!("Database retrieve complete"); + } + true + })) + .unwrap(); + + delete_wal_test.test_case().insert_objects(data_num); + println!( + "database retrieve count after {}", + delete_wal_test.test_case().get_all_objects().len() + ); + assert_eq!( + delete_wal_test.test_case().get_all_objects().len() as i32, + data_num * 2 + ); + + delete_wal_test.test_case().teardown(true); + } +} diff --git a/src/rust/examples/tests/db_corrupted/mod.rs b/src/rust/examples/tests/db_corrupted/mod.rs new file mode 100644 index 000000000..0c5b92398 --- /dev/null +++ b/src/rust/examples/tests/db_corrupted/mod.rs @@ -0,0 +1,8 @@ +pub(crate) mod corrupted_base_test_case; +pub(crate) mod delete_db_file_test; // 删除 db 文件用例,不可恢复 +pub(crate) mod delete_wal_shm_test; // 删除 wal 和 shm 文件用例,可恢复 +pub(crate) mod modify_db_file_test; // 修改 db 文件头部用例,可恢复 +pub(crate) mod terminated_when_write_test; // 强制终止写操作,可恢复 +pub(crate) mod testclass; +pub(crate) mod truncate_file_test; // 截断 db 文件用例,不可恢复 +pub(crate) mod utils; diff --git a/src/rust/examples/tests/db_corrupted/modify_db_file_test.rs b/src/rust/examples/tests/db_corrupted/modify_db_file_test.rs new file mode 100644 index 000000000..3bdd8e138 --- /dev/null +++ b/src/rust/examples/tests/db_corrupted/modify_db_file_test.rs @@ -0,0 +1,168 @@ +use crate::db_corrupted::corrupted_base_test_case::CorruptedBaseTestCase; +use crate::db_corrupted::utils::run_cmd; +use wcdb::core::handle_orm_operation::HandleORMOperationTrait; +use wcdb::core::table_orm_operation::TableORMOperationTrait; + +struct ModifyDbFileTest { + test_case: CorruptedBaseTestCase, +} + +impl ModifyDbFileTest { + pub fn new(db_name: &str, auto_backup: bool) -> Self { + let test_case = CorruptedBaseTestCase::new(db_name, auto_backup); + ModifyDbFileTest { test_case } + } + + pub fn test_case(&self) -> &CorruptedBaseTestCase { + &self.test_case + } + + pub fn modify_db_file(&self) { + // # 示例:破坏文件头(高风险操作) + // echo "Corrupted" | dd of=test.db bs=1 seek=0 count=10 conv=notrunc + let db_path = format!("target/tmp/{}", self.test_case.db_name()); + let cmd = format!( + "echo \"Corrupted\" | dd of={} bs=1 seek=0 count=10 conv=notrunc", + db_path + ); + + let _ = run_cmd(cmd.as_str()); + } +} + +#[cfg(test)] +pub mod modify_db_file_exception_test_case { + use crate::db_corrupted::modify_db_file_test::ModifyDbFileTest; + use wcdb::core::handle_operation::HandleOperationTrait; + + // #[test] // todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_modify_then_backup_exception() { + let db_name = "modify_then_backup_exception.db"; + let modify_db_file_test = ModifyDbFileTest::new(db_name, false); + modify_db_file_test.test_case().setup(); + + modify_db_file_test + .test_case() + .trace_exception("NotADatabase"); + + let data_num = 10; + modify_db_file_test.test_case().insert_objects(data_num); + + modify_db_file_test.modify_db_file(); + + // WCDBNormalException(Level: NoticeCode: NotADatabaseException { Message: "NotADatabase" }) + let _ = modify_db_file_test.test_case().database().backup(); + + assert_eq!( + modify_db_file_test.test_case().get_all_objects().len(), + data_num as usize + ); + modify_db_file_test.test_case().teardown(true); + } + + // #[test] // todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_modify_then_write_back_exception() { + let db_name = "modify_then_write_back_exception.db"; + let modify_db_file_test = ModifyDbFileTest::new(db_name, false); + modify_db_file_test.test_case().setup(); + + // WCDBCorruptOrIOException(Level: ErrorCode: IOErrorException { Message: "disk I/O error" }) + modify_db_file_test + .test_case() + .trace_exception("disk I/O error"); + + let data_num = 10; + modify_db_file_test.test_case().insert_objects(data_num); + + modify_db_file_test.modify_db_file(); + + let _ = modify_db_file_test + .test_case() + .database() + .execute_sql("PRAGMA wal_checkpoint(FULL);"); + + modify_db_file_test.test_case().teardown(true); + } + + // #[test] // todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_modify_then_retrieve_exception() { + let db_name = "modify_then_retrieve_exception.db"; + + let modify_db_file_test = ModifyDbFileTest::new(db_name, false); + modify_db_file_test.test_case().setup(); + + // WCDBNormalException(Level: NoticeCode: NotADatabaseException { Message: "NotADatabase" }) + modify_db_file_test + .test_case() + .trace_exception("NotADatabase"); + + let data_num = 10; + modify_db_file_test.test_case().insert_objects(data_num); + + modify_db_file_test.modify_db_file(); + + let _ = modify_db_file_test.test_case().database().retrieve(Some( + move |percentage: f64, increment: f64| { + println!( + "Database retrieve percentage:{} , increment:{}", + percentage, increment + ); + if percentage >= 1.0 { + println!("Database retrieve complete"); + } + true + }, + )); + modify_db_file_test.test_case().teardown(true); + } +} + +#[cfg(test)] +pub mod modify_then_backup_success_test_case { + use crate::db_corrupted::modify_db_file_test::ModifyDbFileTest; + + // 第一次运行,写入数据并手动备份 + // 第二次运行,修改数据库文件并恢复数据,再写入数据并验证数据库有效性 + // 该用例:单独运行两次可以成功,直接测试所有代码,第二次会失败 + // #[test] // todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test() { + let db_name = "test_modify_then_backup_success.db"; + let modify_db_file_test = ModifyDbFileTest::new(db_name, true); + modify_db_file_test.test_case().setup(); + + modify_db_file_test.test_case().trace_exception(""); + + let data_num = 10; + let has_back_up = modify_db_file_test.test_case().has_back_up(); + + if !has_back_up { + modify_db_file_test.test_case().insert_objects(data_num); + + modify_db_file_test.test_case().database().backup().unwrap(); + } else { + modify_db_file_test.modify_db_file(); + + let _ = modify_db_file_test.test_case().database().retrieve(Some( + move |percentage: f64, increment: f64| { + println!( + "Database retrieve percentage:{} , increment:{}", + percentage, increment + ); + if percentage >= 1.0 { + println!("Database retrieve complete"); + } + true + }, + )); + + modify_db_file_test.test_case().insert_objects(data_num); + + assert_eq!( + modify_db_file_test.test_case().get_all_objects().len() as i32, + data_num * 2 + ); + + modify_db_file_test.test_case().teardown(true); + } + } +} diff --git a/src/rust/examples/tests/db_corrupted/terminated_when_write_test.rs b/src/rust/examples/tests/db_corrupted/terminated_when_write_test.rs new file mode 100644 index 000000000..79c5f8c72 --- /dev/null +++ b/src/rust/examples/tests/db_corrupted/terminated_when_write_test.rs @@ -0,0 +1,142 @@ +use crate::db_corrupted::corrupted_base_test_case::CorruptedBaseTestCase; +use crate::db_corrupted::testclass::table_goods_object::{DbTableGoodsObject, TableGoodsObject}; +use crate::db_corrupted::utils::run_cmd; +use wcdb::core::handle_operation::HandleOperationTrait; +use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + +struct TerminatedWhenWriteTest { + test_case: CorruptedBaseTestCase, +} + +impl TerminatedWhenWriteTest { + pub fn new(db_name: &str, auto_backup: bool) -> Self { + let test_case = CorruptedBaseTestCase::new(db_name, auto_backup); + TerminatedWhenWriteTest { test_case } + } + + pub fn test_case(&self) -> &CorruptedBaseTestCase { + &self.test_case + } + + // 强制终止写操作,todo 是否可以恢复? + pub fn terminated_when_write(&self) { + let table_name = self.test_case().table_name(); + let sql = "BEGIN IMMEDIATE"; + self.test_case().database().execute_sql(sql).unwrap(); + let obj = TableGoodsObject::new(); + self.test_case() + .database() + .insert_object(obj, DbTableGoodsObject::all_fields(), table_name.as_str()) + .unwrap(); + let obj = TableGoodsObject::new(); + self.test_case() + .database() + .insert_object(obj, DbTableGoodsObject::all_fields(), table_name.as_str()) + .unwrap(); + + let sql = "PRAGMA journal_mode=DELETE;"; + self.test_case().database().execute_sql(sql).unwrap(); + let sql = "PRAGMA synchronous=OFF;"; + self.test_case().database().execute_sql(sql).unwrap(); + + let kill_cmd = "os.kill(os.getpid(), 9)"; + let _ = run_cmd(kill_cmd); + } +} + +pub mod terminated_when_write_test_exception { + use super::*; + + // 该用例比较特殊,执行完之后会强制终止进程,所以该用例无法恢复 + // 执行该用例,明确终止写操作并终止进程,再执行其他用户验证与恢复 + // #[test] // todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_terminated_when_write_operation() { + let db_name = "terminated_when_write_then_backup_exception.db"; + let terminated_when_write_test = TerminatedWhenWriteTest::new(db_name, false); + terminated_when_write_test.test_case().setup(); + + let data_num = 10; + terminated_when_write_test + .test_case() + .insert_objects(data_num); + + terminated_when_write_test.terminated_when_write(); + } + + // #[test] // todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_terminated_when_write_operation_check() { + let db_name = "terminated_when_write_then_backup_exception.db"; + let terminated_when_write_test = TerminatedWhenWriteTest::new(db_name, false); + terminated_when_write_test.test_case().setup(); + + let ret = terminated_when_write_test.test_case().get_all_objects(); + assert!(ret.len() > 0); + } + + // #[test] // todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_terminated_when_write_then_write_back_exception() { + let db_name = "terminated_when_write_then_backup_exception.db"; + let terminated_when_write_test = TerminatedWhenWriteTest::new(db_name, false); + terminated_when_write_test.test_case().setup(); + + // WCDBCorruptOrIOException(Level: ErrorCode: IOErrorException { Message: "disk I/O error" }) + let sql = "PRAGMA wal_checkpoint(FULL);"; + terminated_when_write_test + .test_case() + .database() + .execute_sql(sql) + .unwrap(); + } +} + +pub mod terminated_when_write_test_success { + use crate::db_corrupted::terminated_when_write_test::TerminatedWhenWriteTest; + use wcdb::core::handle_operation::HandleOperationTrait; + + // #[test] // todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_terminated_when_write_then_backup_success() { + let db_name = "terminated_when_write_then_backup_exception.db"; + let terminated_when_write_test = TerminatedWhenWriteTest::new(db_name, false); + terminated_when_write_test.test_case().setup(); + + terminated_when_write_test + .test_case() + .database() + .backup() + .unwrap(); + + let ret = terminated_when_write_test.test_case().get_all_objects(); + assert!(ret.len() > 0); + } + + // #[test] // todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_terminated_when_write_then_write_back_success() { + let db_name = "terminated_when_write_then_backup_exception.db"; + let terminated_when_write_test = TerminatedWhenWriteTest::new(db_name, false); + terminated_when_write_test.test_case().setup(); + + let _ = terminated_when_write_test + .test_case() + .database() + .retrieve(Some(move |percentage: f64, increment: f64| { + println!( + "Database retrieve percentage:{} , increment:{}", + percentage, increment + ); + if percentage >= 1.0 { + println!("Database retrieve complete"); + } + true + })); + + let sql = "PRAGMA wal_checkpoint(FULL);"; + terminated_when_write_test + .test_case() + .database() + .execute_sql(sql) + .unwrap(); + + let ret = terminated_when_write_test.test_case().get_all_objects(); + assert_eq!(ret.len(), 10); + } +} diff --git a/src/rust/examples/tests/db_corrupted/testclass/mod.rs b/src/rust/examples/tests/db_corrupted/testclass/mod.rs new file mode 100644 index 000000000..8db754c95 --- /dev/null +++ b/src/rust/examples/tests/db_corrupted/testclass/mod.rs @@ -0,0 +1 @@ +pub(crate) mod table_goods_object; diff --git a/src/rust/examples/tests/db_corrupted/testclass/table_goods_object.rs b/src/rust/examples/tests/db_corrupted/testclass/table_goods_object.rs new file mode 100644 index 000000000..bfb084cb6 --- /dev/null +++ b/src/rust/examples/tests/db_corrupted/testclass/table_goods_object.rs @@ -0,0 +1,40 @@ +use crate::base::random_tool::RandomTool; +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding)] +pub struct TableGoodsObject { + #[WCDBField(is_primary = true, is_auto_increment = true)] + pub id: i64, + #[WCDBField] + pub name: String, + #[WCDBField] + pub price: f64, + #[WCDBField] + pub desc: String, + #[WCDBField] + pub brand: String, +} + +impl TableGoodsObject { + pub fn new() -> Self { + let len = 1000; + TableGoodsObject { + id: 0, + name: format!("name-{}", RandomTool::string_by_length(len)), + price: 0.0, + desc: format!("desc-{}", RandomTool::string_by_length(len)), + brand: format!("brand-{}", RandomTool::string_by_length(len)), + } + } + + pub fn get_obj_vec(data_num: i32) -> Vec { + let mut obj_vec = vec![]; + for i in 0..data_num { + let mut obj = TableGoodsObject::new(); + obj.id = 0i64; + obj.price = (i * 10) as f64; + obj_vec.push(obj); + } + obj_vec + } +} diff --git a/src/rust/examples/tests/db_corrupted/truncate_file_test.rs b/src/rust/examples/tests/db_corrupted/truncate_file_test.rs new file mode 100644 index 000000000..acaf9c7b5 --- /dev/null +++ b/src/rust/examples/tests/db_corrupted/truncate_file_test.rs @@ -0,0 +1,190 @@ +use crate::db_corrupted::corrupted_base_test_case::CorruptedBaseTestCase; +use crate::db_corrupted::utils::run_cmd; +use wcdb::core::handle_orm_operation::HandleORMOperationTrait; +use wcdb::core::table_orm_operation::TableORMOperationTrait; + +struct TruncateFileTest { + test_case: CorruptedBaseTestCase, +} + +impl TruncateFileTest { + pub fn new(db_name: &str, auto_backup: bool) -> Self { + let test_case = CorruptedBaseTestCase::new(db_name, auto_backup); + TruncateFileTest { test_case } + } + + pub fn test_case(&self) -> &CorruptedBaseTestCase { + &self.test_case + } + + pub fn truncate_file(&self) { + // # 将文件截断为 1024 字节 + // truncate -s 1024 test.db + let db_path = format!("target/tmp/{}", self.test_case.db_name()); + let cmd = format!("truncate -s 1 {}", db_path); + let _ = run_cmd(cmd.as_str()); + } +} + +#[cfg(test)] +pub mod truncate_file_exception_test_case { + use crate::db_corrupted::truncate_file_test::TruncateFileTest; + use wcdb::core::handle_operation::HandleOperationTrait; + use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + use wcdb::core::table_orm_operation::TableORMOperationTrait; + + // #[test] // todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_truncate_file_then_backup_exception() { + let db_name = "truncate_file_then_backup_exception.db"; + let truncate_file_test = TruncateFileTest::new(db_name, true); + truncate_file_test.test_case().setup(); + + truncate_file_test + .test_case() + .trace_exception("file is not a database"); + + let data_num = 10; + truncate_file_test.test_case().insert_objects(data_num); + + // 必须强制回写,否则 db 文件内容为空,无法被损坏 + let sql = "PRAGMA wal_checkpoint(FULL);"; + truncate_file_test + .test_case() + .database() + .execute_sql(sql) + .unwrap(); + + truncate_file_test.truncate_file(); + + // WCDBCorruptOrIOException(Level: ErrorCode: NotADatabaseException { Message: "file is not a database" }) + // truncate_file_test.test_case().database().backup().unwrap(); + let _ = truncate_file_test.test_case().database().backup(); + + assert_eq!( + truncate_file_test.test_case().get_all_objects().len(), + data_num as usize + ); + + truncate_file_test.test_case().teardown(true); + } + + // #[test] // todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_truncate_file_then_write_back_exception() { + let db_name = "truncate_file_then_write_back_exception.db"; + let truncate_file_test = TruncateFileTest::new(db_name, true); + truncate_file_test.test_case().setup(); + + truncate_file_test + .test_case() + .trace_exception("file is not a database"); + + let data_num = 10; + truncate_file_test.test_case().insert_objects(data_num); + + // 必须强制回写,否则 db 文件内容为空,无法被损坏 + let sql = "PRAGMA wal_checkpoint(FULL);"; + truncate_file_test + .test_case() + .database() + .execute_sql(sql) + .unwrap(); + + truncate_file_test.truncate_file(); + + // 此处的强制回写竟然不报错???神奇 + let sql = "PRAGMA wal_checkpoint(FULL);"; + truncate_file_test + .test_case() + .database() + .execute_sql(sql) + .unwrap(); + + assert_eq!( + truncate_file_test.test_case().get_all_objects().len(), + data_num as usize + ); + + truncate_file_test.test_case().teardown(true); + } + + // #[test] // todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_truncate_file_then_retrieve_exception() { + let db_name = "truncate_file_then_retrieve_exception.db"; + let truncate_file_test = TruncateFileTest::new(db_name, true); + truncate_file_test.test_case().setup(); + + let data_num = 10; + truncate_file_test.test_case().insert_objects(10); + + // 必须强制回写,否则 db 文件内容为空,无法被损坏 + let sql = "PRAGMA wal_checkpoint(FULL);"; + truncate_file_test + .test_case() + .database() + .execute_sql(sql) + .unwrap(); + + truncate_file_test.truncate_file(); + + // 损坏之后再次回写。为什么不报错???神奇 + let sql = "PRAGMA wal_checkpoint(FULL);"; + truncate_file_test + .test_case() + .database() + .execute_sql(sql) + .unwrap(); + + assert_eq!( + truncate_file_test.test_case().get_all_objects().len(), + data_num as usize + ); + + truncate_file_test.test_case().teardown(true); + } + + // #[test] // todo qixinbing: 本地运行正常,ci 运行卡死,原因待查 + pub fn test_backup_truncate_retrieve_exception() { + let db_name = "truncate_file_success.db"; + let truncate_file_test = TruncateFileTest::new(db_name, true); + truncate_file_test.test_case().setup(); + + let data_num = 10; + let has_back_up = truncate_file_test.test_case().has_back_up(); + + if !has_back_up { + truncate_file_test.test_case().insert_objects(data_num); + + // 必须强制回写,确保备份成功 + let sql = "PRAGMA wal_checkpoint(FULL);"; + truncate_file_test + .test_case() + .database() + .execute_sql(sql) + .unwrap(); + + truncate_file_test.test_case().database().backup().unwrap(); + } else { + truncate_file_test.truncate_file(); + + let _ = truncate_file_test.test_case().database().retrieve(Some( + move |percentage: f64, increment: f64| { + println!( + "Database retrieve percentage:{} , increment:{}", + percentage, increment + ); + if percentage >= 1.0 { + println!("Database retrieve complete"); + } + true + }, + )); + + let database = truncate_file_test.test_case().database(); + let table_name = truncate_file_test.test_case().table_name(); + assert!(!database.table_exist(table_name.as_str()).unwrap()); + + // WCDBNormalException(Level: ErrorCode: ErrorException { Message: "no such table: table_goods_object" } + // truncate_file_test.test_case().insert_objects(data_num); + } + } +} diff --git a/src/rust/examples/tests/db_corrupted/utils.rs b/src/rust/examples/tests/db_corrupted/utils.rs new file mode 100644 index 000000000..67fc3b8e6 --- /dev/null +++ b/src/rust/examples/tests/db_corrupted/utils.rs @@ -0,0 +1,11 @@ +use std::process::{Command, Output}; + +// let output = run_cmd(cmd); +// println!("run cmd {}",String::from_utf8_lossy(&output.stdout)); +pub fn run_cmd(cmd: &str) -> Output { + Command::new("sh") + .arg("-c") + .arg(cmd) + .output() + .expect("failed to execute process") +} diff --git a/src/rust/examples/tests/lib.rs b/src/rust/examples/tests/lib.rs new file mode 100644 index 000000000..43a542770 --- /dev/null +++ b/src/rust/examples/tests/lib.rs @@ -0,0 +1,8 @@ +pub(crate) mod base; +pub(crate) mod core; +pub(crate) mod crud; +pub(crate) mod database; +pub(crate) mod db_corrupted; +pub(crate) mod orm; +pub(crate) mod sample; +pub(crate) mod winq; diff --git a/src/rust/examples/tests/orm/mod.rs b/src/rust/examples/tests/orm/mod.rs new file mode 100644 index 000000000..eabf791ec --- /dev/null +++ b/src/rust/examples/tests/orm/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod orm_test; +pub(crate) mod testclass; diff --git a/src/rust/examples/tests/orm/orm_test.rs b/src/rust/examples/tests/orm/orm_test.rs new file mode 100644 index 000000000..0eaf4fd45 --- /dev/null +++ b/src/rust/examples/tests/orm/orm_test.rs @@ -0,0 +1,556 @@ +use crate::base::base_test_case::TestCaseTrait; +use crate::base::database_test_case::{DatabaseTestCase, Expect, TestOperation}; +use crate::base::wrapped_value::WrappedValue; +use crate::orm::testclass::auto_add_column_object::{AutoAddColumnObject, DbAutoAddColumnObject}; +use crate::orm::testclass::column_rename_object::{ + ColumnRenameObjectOld, DbColumnRenameObjectNew, DbColumnRenameObjectOld, + DB_COLUMN_RENAME_OBJECT_NEW_INSTANCE, DB_COLUMN_RENAME_OBJECT_OLD_INSTANCE, +}; +use crate::orm::testclass::table_primary_key_object::{ + DbTablePrimaryKeyObjectOld, TablePrimaryKeyObjectOld, DB_TABLE_PRIMARY_KEY_OBJECT_NEW_INSTANCE, + DB_TABLE_PRIMARY_KEY_OBJECT_OLD_INSTANCE, +}; +use rand::Rng; +use std::cmp::PartialEq; +use wcdb::base::wcdb_exception::{WCDBException, WCDBResult}; +use wcdb::core::handle_operation::HandleOperationTrait; +use wcdb::core::handle_orm_operation::HandleORMOperationTrait; +use wcdb::core::table_orm_operation::TableORMOperationTrait; +use wcdb::orm::field::Field; +use wcdb::orm::table_binding::TableBinding; +use wcdb::winq::column_def::ColumnDef; +use wcdb::winq::column_type::ColumnType; +use wcdb::winq::expression::Expression; +use wcdb::winq::identifier::IdentifierTrait; +use wcdb::winq::statement_alter_table::StatementAlterTable; +use wcdb::winq::statement_create_table::StatementCreateTable; + +pub struct OrmTest { + database_test_case: DatabaseTestCase, + table_name: String, +} + +impl OrmTest { + pub fn new() -> Self { + OrmTest { + database_test_case: DatabaseTestCase::new(), + table_name: "testTable".to_string(), + } + } + + fn do_test_create_table_and_index_sqls_as_expected(&self, sqls: Vec, operation: CB) + where + CB: FnOnce() -> WCDBResult<()>, + { + assert!(sqls.len() > 0); + let mut new_sql_vec = vec![]; + new_sql_vec.push("BEGIN IMMEDIATE".to_string()); + new_sql_vec.extend(sqls); + new_sql_vec.push("COMMIT".to_string()); + let _ = self + .database_test_case + .do_test_sql_vec(new_sql_vec, operation); + } + + fn do_test_auto_add_column( + &self, + fake_table: &str, + remove_filed: &Field, + succeed: bool, + operation: TestOperation, + ) -> WCDBResult<()> { + let column_name = remove_filed.get_name(); + let statement = StatementCreateTable::new(); + let create_table = statement.create_table(fake_table); + let mut column_defs = vec![]; + DbAutoAddColumnObject::all_fields() + .iter() + .for_each(|field| { + if field.get_description().as_str() != column_name { + let column_def = ColumnDef::new((*field, ColumnType::Integer)); + column_defs.push(column_def); + } + }); + + let create_table = create_table.define(column_defs.iter().collect()); + let _ = self.database_test_case.execute(create_table); // todo qixinbing 直接 ? 会报错 + + let mut added = WrappedValue::new(); + added.bool_value = true; + self.database_test_case + .trace_exception(Some(move |exp: WCDBException| { + if exp.message() != "Auto add column".to_string() { + return; + } + // added.bool_value = true; + // todo qixinbing + })); + let mut has_error = false; + let operation_ret = operation(); + match operation_ret { + Ok(_) => {} + Err(exp) => { + let msg = exp.message(); + println!("qxb operation_ret {}", msg); + has_error = true; + } + } + assert_eq!(succeed, !has_error); + assert_eq!(succeed, added.bool_value); + self.database_test_case.drop_table(fake_table)?; + // self.database_test_case.get_database().read().unwrap().trace_exception(None); + + Ok(()) + } + + fn do_table_update_primary_key(&self, table_name: &str, table_name_back: &str, data_num: i32) { + // create old table + self.database_test_case + .get_database() + .read() + .unwrap() + .create_table(table_name, &*DB_TABLE_PRIMARY_KEY_OBJECT_OLD_INSTANCE) + .unwrap(); + + // insert db to old table + let obj_vec = TablePrimaryKeyObjectOld::get_old_obj_vec(data_num); + + self.database_test_case + .get_database() + .write() + .unwrap() + .insert_objects( + obj_vec, + DbTablePrimaryKeyObjectOld::all_fields(), + table_name, + ) + .unwrap(); + + // rename old table + let statement_alert_table = StatementAlterTable::new(); + self.database_test_case + .get_database() + .write() + .unwrap() + .execute( + statement_alert_table + .alter_table(table_name) + .rename_to(table_name_back), + ) + .unwrap(); + + // create new table + self.database_test_case + .get_database() + .read() + .unwrap() + .create_table(table_name, &*DB_TABLE_PRIMARY_KEY_OBJECT_NEW_INSTANCE) + .unwrap(); + + // insert old table to new table + let sql = format!( + "INSERT OR REPLACE INTO {} SELECT * FROM {};", + table_name, table_name_back + ); + self.database_test_case + .get_database() + .write() + .unwrap() + .execute_sql(sql.as_str()) + .unwrap(); + + // drop old table + self.database_test_case + .get_database() + .write() + .unwrap() + .drop_table(table_name_back) + .unwrap(); + } + + fn do_column_rename( + &self, + table_name: &str, + data_num: i32, + column_name_old: &str, + column_name_new: &str, + ) { + // create old table + self.database_test_case + .get_database() + .read() + .unwrap() + .create_table(table_name, &*DB_COLUMN_RENAME_OBJECT_OLD_INSTANCE) + .unwrap(); + + // insert test date to old table + let obj_vec = ColumnRenameObjectOld::get_old_obj_vec(data_num); + self.database_test_case + .get_database() + .write() + .unwrap() + .insert_objects(obj_vec, DbColumnRenameObjectOld::all_fields(), table_name) + .unwrap(); + + // old table column rename + let statement_alert_table = StatementAlterTable::new(); + let statement = statement_alert_table + .alter_table(table_name) + .rename_column(column_name_old) + .to_column(column_name_new); + self.database_test_case + .get_database() + .write() + .unwrap() + .execute(statement) + .unwrap(); + + // open new table + self.database_test_case + .get_database() + .read() + .unwrap() + .create_table(table_name, &*DB_COLUMN_RENAME_OBJECT_NEW_INSTANCE) + .unwrap(); + + // check + let ret = self + .database_test_case + .get_database() + .read() + .unwrap() + .get_all_objects( + DbColumnRenameObjectNew::all_fields(), + table_name, + None, + None, + None, + None, + ); + match ret { + Ok(new_obj_vec) => { + assert_eq!(new_obj_vec.len(), data_num as usize); + for new_obj in new_obj_vec { + assert!(new_obj.is_same()); + } + } + Err(err) => { + assert!(false); + } + } + } +} + +impl TestCaseTrait for OrmTest { + fn setup(&self) -> WCDBResult<()> { + self.database_test_case.setup()?; + self.database_test_case.set_expect_mode(Expect::SomeSQLs); + Ok(()) + } + + fn teardown(&self) -> WCDBResult<()> { + self.database_test_case.teardown() + } +} + +#[cfg(test)] +pub mod orm_test { + use super::*; + use crate::orm::testclass::all_type_object::{ + AllTypeObjectHelper, DbAllTypeObject, DB_ALL_TYPE_OBJECT_INSTANCE, + }; + use crate::orm::testclass::auto_add_column_object::{ + DbAutoAddColumnObject, DB_AUTO_ADD_COLUMN_OBJECT_INSTANCE, + }; + use crate::orm::testclass::field_object::DbFieldObject; + use crate::orm::testclass::primary_enable_auto_increment_object::{ + DbPrimaryEnableAutoIncrementObject, PrimaryEnableAutoIncrementObject, + DB_PRIMARY_ENABLE_AUTO_INCREMENT_OBJECT_INSTANCE, + }; + use crate::orm::testclass::primary_not_auto_increment_object::{ + DbPrimaryNotAutoIncrementObject, PrimaryNotAutoIncrementObject, + DB_PRIMARY_NOT_AUTO_INCREMENT_OBJECT_INSTANCE, + }; + use crate::orm::testclass::table_constraint_object::DB_TABLE_CONSTRAINT_OBJECT_INSTANCE; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + + fn setup(orm_test: &OrmTest) { + orm_test.setup().unwrap(); + } + + fn teardown(orm_test: &OrmTest) { + orm_test.teardown().unwrap(); + } + + #[test] + fn test_all_field() { + assert_eq!(DbFieldObject::all_fields().len(), 2); + + let orm_test = OrmTest::new(); + let binding = orm_test.database_test_case.get_database(); + let database_lock = binding.read().unwrap(); + let binding = DbFieldObject::all_fields(); + let first_field = binding.first().unwrap(); + assert_eq!(first_field.get_description(), "field"); + + let second_field = binding.last().unwrap(); + assert_eq!(second_field.get_description(), "differentName"); + } + + #[test] + fn test_all_type() { + let orm_test = OrmTest::new(); + setup(&orm_test); + + let table_name = "table_all_type".to_string(); + + let mut sql_vec = vec![]; + // sql_vec.push("CREATE TABLE IF NOT EXISTS table_all_type(field_type TEXT, a_bool INTEGER, a_byte INTEGER, a_short INTEGER, a_int INTEGER, a_long INTEGER, a_float REAL, a_double REAL, a_string TEXT)".to_string()); + sql_vec.push("CREATE TABLE IF NOT EXISTS table_all_type(field_type TEXT , a_bool INTEGER , a_bool2 INTEGER , a_byte INTEGER , a_byte2 INTEGER , a_short INTEGER , a_short2 INTEGER , a_int INTEGER , a_int2 INTEGER , a_long INTEGER , a_long2 INTEGER , a_float REAL , a_float2 REAL , a_double REAL , a_double2 REAL , a_string TEXT , a_string2 TEXT , a_blob BLOB , a_blob2 BLOB )".to_string()); + + orm_test.do_test_create_table_and_index_sqls_as_expected(sql_vec, || { + orm_test + .database_test_case + .create_table(table_name.as_str(), &*DB_ALL_TYPE_OBJECT_INSTANCE)?; + Ok(()) + }); + + let binding = orm_test.database_test_case.get_database(); + let database_lock = binding.read().unwrap(); + let table = database_lock.get_table(table_name.as_str(), &*DB_ALL_TYPE_OBJECT_INSTANCE); + + let max = AllTypeObjectHelper::max_object(); + let min = AllTypeObjectHelper::min_object(); + let random = AllTypeObjectHelper::random_object(); + let empty = AllTypeObjectHelper::empty_object(); + + let obj_vec = vec![max.clone(), min.clone(), random.clone(), empty.clone()]; + let _ = table.insert_objects(obj_vec, Some(DbAllTypeObject::all_fields())); + + let exp = Expression::new(DbAllTypeObject::field_type()).eq(max.field_type.as_str()); + let db_max_opt = table + .get_first_object(Some(DbAllTypeObject::all_fields()), Some(&exp), None, None) + .unwrap(); + assert!(max == db_max_opt.unwrap()); + + let exp = Expression::new(DbAllTypeObject::field_type()).eq(min.field_type.as_str()); + let db_min_opt = table + .get_first_object(Some(DbAllTypeObject::all_fields()), Some(&exp), None, None) + .unwrap(); + assert!(min == db_min_opt.unwrap()); + + let exp = Expression::new(DbAllTypeObject::field_type()).eq(empty.field_type.as_str()); + let db_empty_opt = table + .get_first_object(Some(DbAllTypeObject::all_fields()), Some(&exp), None, None) + .unwrap(); + assert!(empty == db_empty_opt.unwrap()); + + let exp = Expression::new(DbAllTypeObject::field_type()).eq(random.field_type.as_str()); + let db_random_opt = table + .get_first_object(Some(DbAllTypeObject::all_fields()), Some(&exp), None, None) + .unwrap(); + assert!(random == db_random_opt.unwrap()); + + teardown(&orm_test); + } + + #[test] + fn test_table_constraint() { + let orm_test = OrmTest::new(); + setup(&orm_test); + + let table_name = orm_test.table_name.as_str(); + + let mut sql_vec = vec![]; + sql_vec.push("CREATE TABLE IF NOT EXISTS testTable(multiPrimary1 INTEGER , multiPrimary2 INTEGER , multiPrimary3 INTEGER , multiUnique1 INTEGER , multiUnique2 INTEGER , multiUnique3 INTEGER , multiIndex1 INTEGER , multiIndex2 INTEGER , multiIndex3 INTEGER , UNIQUE(multiUnique1, multiUnique2, multiUnique3), PRIMARY KEY(multiPrimary1, multiPrimary2, multiPrimary3))".to_string()); + sql_vec.push("CREATE INDEX IF NOT EXISTS specifiedNameIndex ON testTable(multiIndex1, multiIndex2, multiIndex3)".to_string()); + sql_vec.push("CREATE INDEX IF NOT EXISTS testTable_multiIndex1_multiIndex2_index ON testTable(multiIndex1, multiIndex2)".to_string()); + + orm_test.do_test_create_table_and_index_sqls_as_expected(sql_vec, || { + orm_test + .database_test_case + .create_table(table_name, &*DB_TABLE_CONSTRAINT_OBJECT_INSTANCE) + .unwrap(); + Ok(()) + }); + + teardown(&orm_test); + } + + // #[test] + fn test_auto_add_column() { + let orm_test = OrmTest::new(); + setup(&orm_test); + + let fake_table = "fakeTable"; + let fake_schema = "notExistSchema"; + orm_test + .database_test_case + .create_table(fake_table, &*DB_AUTO_ADD_COLUMN_OBJECT_INSTANCE) + .unwrap(); + + let obj = DbAutoAddColumnObject::default(); + let mut insert_value = unsafe { &*obj.insert_value }; + DbAutoAddColumnObject::all_fields() + .iter() + .for_each(|field| { + if field.get_name() == "insertValue" { + insert_value = *field; + } + }); + + let database_test_case_clone = orm_test.database_test_case.clone(); + orm_test + .do_test_auto_add_column( + fake_table, + insert_value, + true, + Box::new(move || { + let self_table_name = fake_table.to_string(); + database_test_case_clone.insert_object( + AutoAddColumnObject::new(), + DbAutoAddColumnObject::all_fields(), + self_table_name.as_str(), + )?; + Ok(()) + }), + ) + .unwrap(); + + let mut update_value = unsafe { &*obj.update_value }; + DbAutoAddColumnObject::all_fields() + .iter() + .for_each(|field| { + if field.get_name() == "updateValue" { + update_value = *field; + } + }); + + orm_test + .do_test_auto_add_column( + fake_table, + update_value, + true, + Box::new(move || { + // todo qixinbing + Ok(()) + }), + ) + .unwrap(); + + // todo qixinbing + teardown(&orm_test); + } + + #[test] + fn test_primary_key_enable_auto_increment_for_existing_table() { + let orm_test = OrmTest::new(); + setup(&orm_test); + + let binding = orm_test.database_test_case.get_database(); + let database_lock = binding.read().unwrap(); + // let table_name = orm_test.table_name.as_str(); 见 DatabaseTestCase setup 方法 + let table_name = "testTable2"; + + database_lock + .create_table(table_name, &*DB_PRIMARY_NOT_AUTO_INCREMENT_OBJECT_INSTANCE) + .unwrap(); + let mut obj1 = PrimaryNotAutoIncrementObject::new(); + obj1.id = 1; + database_lock + .insert_object( + obj1, + DbPrimaryNotAutoIncrementObject::all_fields(), + table_name, + ) + .unwrap(); + + database_lock + .create_table( + table_name, + &*DB_PRIMARY_ENABLE_AUTO_INCREMENT_OBJECT_INSTANCE, + ) + .unwrap(); + database_lock + .delete_objects(table_name, None, None, None, None) + .unwrap(); + + let obj2 = PrimaryEnableAutoIncrementObject::new(); + database_lock + .insert_object( + obj2, + DbPrimaryEnableAutoIncrementObject::all_fields(), + table_name, + ) + .unwrap(); + let obj_vec = database_lock + .get_all_objects( + DbPrimaryEnableAutoIncrementObject::all_fields(), + table_name, + None, + None, + None, + None, + ) + .unwrap(); + assert_eq!(obj_vec.last().unwrap().id, 2); + + teardown(&orm_test); + } + + // 新增的升级单测,Java 没有该用例 + #[test] + fn test_table_update_primary_key() { + // todo qixinbing 上线需要配合版本控制 + let orm_test = OrmTest::new(); + orm_test.setup().unwrap(); + + let mut sql_vec = vec![]; + sql_vec.push("CREATE TABLE IF NOT EXISTS table_primary_key(id INTEGER , name TEXT , price REAL , desc TEXT , PRIMARY KEY(id))".to_string()); + sql_vec.push("ALTER TABLE table_primary_key RENAME TO table_primary_key_back".to_string()); + sql_vec.push("CREATE TABLE IF NOT EXISTS table_primary_key(id INTEGER , name TEXT , price REAL , desc TEXT , PRIMARY KEY(id, name))".to_string()); + sql_vec.push( + "INSERT OR REPLACE INTO table_primary_key SELECT * FROM table_primary_key_back;" + .to_string(), + ); + sql_vec.push("DROP TABLE IF EXISTS table_primary_key_back".to_string()); + + orm_test.do_test_create_table_and_index_sqls_as_expected(sql_vec, || { + let table_name = "table_primary_key"; + let table_name_back = "table_primary_key_back"; + let data_num = 15; + orm_test.do_table_update_primary_key(table_name, table_name_back, data_num); + Ok(()) + }); + + orm_test.teardown().unwrap(); + } + + // 新增的升级单测,Java 没有该用例 + #[test] + fn test_column_rename() { + let orm_test = OrmTest::new(); + orm_test.setup().unwrap(); + + let mut sql_vec = vec![]; + sql_vec.push("CREATE TABLE IF NOT EXISTS column_rename_object(category INTEGER , target_id TEXT , channel_id TEXT )".to_string()); + sql_vec.push( + "ALTER TABLE column_rename_object RENAME COLUMN category TO conversation_type" + .to_string(), + ); + sql_vec.push( + "SELECT conversation_type, target_id, channel_id FROM column_rename_object".to_string(), + ); + + orm_test.do_test_create_table_and_index_sqls_as_expected(sql_vec, || { + let table_name = "column_rename_object"; + let column_name_old = "category"; + let column_name_new = "conversation_type"; + let data_num = 8; + orm_test.do_column_rename(table_name, data_num, column_name_old, column_name_new); + Ok(()) + }); + + orm_test.teardown().unwrap(); + } +} diff --git a/src/rust/examples/tests/orm/testclass/all_type_object.rs b/src/rust/examples/tests/orm/testclass/all_type_object.rs new file mode 100644 index 000000000..e447db0e3 --- /dev/null +++ b/src/rust/examples/tests/orm/testclass/all_type_object.rs @@ -0,0 +1,201 @@ +use crate::base::random_tool::RandomTool; +use rand::Rng; +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding, PartialEq, Clone)] +pub struct AllTypeObject { + #[WCDBField] + pub field_type: String, + + // Integer + #[WCDBField] + pub a_bool: bool, + #[WCDBField] + pub a_bool2: Option, + #[WCDBField] + pub a_byte: i8, + #[WCDBField] + pub a_byte2: Option, + #[WCDBField] + pub a_short: i16, + #[WCDBField] + pub a_short2: Option, + #[WCDBField] + pub a_int: i32, + #[WCDBField] + pub a_int2: Option, + #[WCDBField] + pub a_long: i64, + #[WCDBField] + pub a_long2: Option, + + // Float + #[WCDBField] + pub a_float: f32, + #[WCDBField] + pub a_float2: Option, + #[WCDBField] + pub a_double: f64, + #[WCDBField] + pub a_double2: Option, + + // String + #[WCDBField] + pub a_string: String, + #[WCDBField] + pub a_string2: Option, + + // BLOB + #[WCDBField] + a_blob: Vec, + #[WCDBField] + pub a_blob2: Option>, +} + +impl AllTypeObject { + pub fn new() -> Self { + AllTypeObject { + field_type: "".to_string(), + a_bool: false, + a_bool2: None, + a_byte: 0, + a_byte2: None, + a_short: 0, + a_short2: None, + a_int: 0, + a_int2: None, + a_long: 0, + a_long2: None, + a_float: 0.0, + a_float2: None, + a_double: 0.0, + a_double2: None, + a_string: "".to_string(), + a_string2: None, + a_blob: Vec::new(), + a_blob2: None, + } + } + + pub fn equals(&self, other: &AllTypeObject) -> bool { + self.a_bool == other.a_bool + && self.a_byte == other.a_byte + && self.a_short == other.a_short + && self.a_int == other.a_int + && self.a_long == other.a_long + && self.a_float == other.a_float + && self.a_double == other.a_double + && self.a_string == other.a_string + && self.a_bool2 == other.a_bool2 + && self.a_byte2 == other.a_byte2 + && self.a_short2 == other.a_short2 + && self.a_int2 == other.a_int2 + && self.a_long2 == other.a_long2 + && self.a_float2 == other.a_float2 + && self.a_double2 == other.a_double2 + && self.a_string2 == other.a_string2 + && self.a_blob == other.a_blob + && self.a_blob2 == other.a_blob2 + } +} + +pub struct AllTypeObjectHelper {} + +impl AllTypeObjectHelper { + pub fn max_object() -> AllTypeObject { + AllTypeObject { + field_type: "max".to_string(), + a_bool: true, + a_bool2: Some(true), + a_byte: i8::MAX, + a_byte2: Some(i8::MAX), + a_short: i16::MAX, + a_short2: Some(i16::MAX), + a_int: i32::MAX, + a_int2: Some(i32::MAX), + a_long: i64::MAX, + a_long2: Some(i64::MAX), + a_float: f32::MAX, + a_float2: Some(f32::MAX), + a_double: f64::MAX, + a_double2: Some(f64::MAX), + a_string: RandomTool::string(), + a_string2: Some(RandomTool::string()), + a_blob: RandomTool::bytes(), + a_blob2: Some(RandomTool::bytes()), + } + } + + pub fn min_object() -> AllTypeObject { + AllTypeObject { + field_type: "min".to_string(), + a_bool: false, + a_bool2: Some(false), + a_byte: i8::MIN, + a_byte2: Some(i8::MIN), + a_short: i16::MIN, + a_short2: Some(i16::MIN), + a_int: i32::MIN, + a_int2: Some(i32::MIN), + a_long: i64::MIN, + a_long2: Some(i64::MIN), + a_float: f32::MIN, + a_float2: Some(f32::MIN), + a_double: f64::MIN, + a_double2: Some(f64::MIN), + a_string: RandomTool::string(), + a_string2: Some(RandomTool::string()), + a_blob: RandomTool::bytes(), + a_blob2: Some(RandomTool::bytes()), + } + } + + pub fn random_object() -> AllTypeObject { + let mut rng = rand::thread_rng(); + AllTypeObject { + field_type: "random".to_string(), + a_bool: rng.gen::(), + a_bool2: Some(rng.gen::()), + a_byte: rng.gen::(), + a_byte2: Some(rng.gen::()), + a_short: rng.gen::(), + a_short2: Some(rng.gen::()), + a_int: rng.gen::(), + a_int2: Some(rng.gen::()), + a_long: rng.gen::(), + a_long2: Some(rng.gen::()), + a_float: rng.gen::(), + a_float2: Some(rng.gen::()), + a_double: rng.gen::(), + a_double2: Some(rng.gen::()), + a_string: RandomTool::string(), + a_string2: Some(RandomTool::string()), + a_blob: RandomTool::bytes(), + a_blob2: Some(RandomTool::bytes()), + } + } + + pub fn empty_object() -> AllTypeObject { + AllTypeObject { + field_type: "empty".to_string(), + a_bool: false, + a_bool2: None, + a_byte: 0, + a_byte2: None, + a_short: 0, + a_short2: None, + a_int: 0, + a_int2: None, + a_long: 0, + a_long2: None, + a_float: 0.0, + a_float2: None, + a_double: 0.0, + a_double2: None, + a_string: RandomTool::string(), + a_string2: None, + a_blob: Vec::new(), + a_blob2: None, + } + } +} diff --git a/src/rust/examples/tests/orm/testclass/auto_add_column_object.rs b/src/rust/examples/tests/orm/testclass/auto_add_column_object.rs new file mode 100644 index 000000000..569f4ead9 --- /dev/null +++ b/src/rust/examples/tests/orm/testclass/auto_add_column_object.rs @@ -0,0 +1,36 @@ +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding)] +pub struct AutoAddColumnObject { + #[WCDBField(column_name = "primaryValue", is_primary = true)] + primary_value: i32, + #[WCDBField(column_name = "uniqueValue", is_unique = true)] + unique_value: i32, + #[WCDBField(column_name = "insertValue")] + insert_value: i32, + #[WCDBField(column_name = "updateValue")] + update_value: i32, + #[WCDBField(column_name = "selectValue")] + select_value: i32, + #[WCDBField(column_name = "multiSelectValue")] + multi_select_value: i32, + #[WCDBField(column_name = "deleteValue")] + delete_value: i32, + #[WCDBField(column_name = "indexValue")] + index_value: i32, +} + +impl AutoAddColumnObject { + pub fn new() -> Self { + Self { + primary_value: 0, + unique_value: 0, + insert_value: 0, + update_value: 0, + select_value: 0, + multi_select_value: 0, + delete_value: 0, + index_value: 0, + } + } +} diff --git a/src/rust/examples/tests/orm/testclass/column_rename_object.rs b/src/rust/examples/tests/orm/testclass/column_rename_object.rs new file mode 100644 index 000000000..d15f856b1 --- /dev/null +++ b/src/rust/examples/tests/orm/testclass/column_rename_object.rs @@ -0,0 +1,53 @@ +use crate::base::random_tool::RandomTool; +use wcdb_derive::WCDBTableCoding; + +const CATEGORY_DEFAULT_VALUE: i32 = 111; + +#[derive(WCDBTableCoding)] +pub struct ColumnRenameObjectOld { + #[WCDBField] + category: i32, + #[WCDBField] + target_id: String, + #[WCDBField] + channel_id: String, +} + +#[derive(WCDBTableCoding)] +pub struct ColumnRenameObjectNew { + #[WCDBField(column_name = "conversation_type")] + category: i32, + #[WCDBField] + target_id: String, + #[WCDBField] + channel_id: String, +} + +impl ColumnRenameObjectOld { + pub fn new() -> Self { + ColumnRenameObjectOld { + category: 0, + target_id: "".to_string(), + channel_id: "".to_string(), + } + } + + pub fn get_old_obj_vec(data_num: i32) -> Vec { + let mut obj_vec = vec![]; + for i in 0..data_num { + let mut obj = ColumnRenameObjectOld::new(); + obj.category = CATEGORY_DEFAULT_VALUE; + obj.target_id = format!("target_id-{}", RandomTool::string()); + obj.channel_id = format!("channel_id-{}", RandomTool::string()); + obj_vec.push(obj); + } + obj_vec + } +} + +impl ColumnRenameObjectNew { + pub fn is_same(&self) -> bool { + // 只需要关注列名变更后,值是否相同即可 + self.category == CATEGORY_DEFAULT_VALUE + } +} diff --git a/src/rust/examples/tests/orm/testclass/field_object.rs b/src/rust/examples/tests/orm/testclass/field_object.rs new file mode 100644 index 000000000..5606f8b86 --- /dev/null +++ b/src/rust/examples/tests/orm/testclass/field_object.rs @@ -0,0 +1,13 @@ +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding)] +pub struct FieldObject { + #[WCDBField] + field: i32, + #[WCDBField( + column_name = "differentName", + is_primary = true, + is_auto_increment = true + )] + field_with_different_name: i32, +} diff --git a/src/rust/examples/tests/orm/testclass/fts3_test_object.rs b/src/rust/examples/tests/orm/testclass/fts3_test_object.rs new file mode 100644 index 000000000..3722dc744 --- /dev/null +++ b/src/rust/examples/tests/orm/testclass/fts3_test_object.rs @@ -0,0 +1,19 @@ +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding, PartialEq, Clone)] +#[WCDBTable(fts_module(version = "FTS3", tokenizer = "wcdb_one_or_binary", tokenizer_parameters = ["skip_stemming"]))] +pub struct Fts3TestObject { + #[WCDBField] + id: i64, + #[WCDBField] + pub(crate) content: String, +} + +impl Fts3TestObject { + pub(crate) fn new(id: i64, content: &str) -> Self { + Self { + id, + content: String::from(content), + } + } +} diff --git a/src/rust/examples/tests/orm/testclass/fts5_test_object.rs b/src/rust/examples/tests/orm/testclass/fts5_test_object.rs new file mode 100644 index 000000000..d0815a7b7 --- /dev/null +++ b/src/rust/examples/tests/orm/testclass/fts5_test_object.rs @@ -0,0 +1,19 @@ +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding, PartialEq, Clone)] +#[WCDBTable(fts_module(version = "FTS5", tokenizer = "wcdb_verbatim", tokenizer_parameters = ["skip_stemming", "chinese_traditional_to_simplified"], external_table = "contentTable"))] +pub struct Fts5TestObject { + #[WCDBField] + pub(crate) id: i32, + #[WCDBField] + pub(crate) content: String, +} + +impl Fts5TestObject { + pub(crate) fn new(id: i32, content: &str) -> Self { + Self { + id, + content: String::from(content), + } + } +} diff --git a/src/rust/examples/tests/orm/testclass/mmicu_test_object.rs b/src/rust/examples/tests/orm/testclass/mmicu_test_object.rs new file mode 100644 index 000000000..357cd2b17 --- /dev/null +++ b/src/rust/examples/tests/orm/testclass/mmicu_test_object.rs @@ -0,0 +1,19 @@ +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding, PartialEq, Clone)] +#[WCDBTable(fts_module(version = "FTS3", tokenizer = "mmicu"))] +pub struct MmicuTestObject { + #[WCDBField] + id: i64, + #[WCDBField] + pub(crate) content: String, +} + +impl MmicuTestObject { + pub(crate) fn new(id: i64, content: &str) -> Self { + Self { + id, + content: String::from(content), + } + } +} diff --git a/src/rust/examples/tests/orm/testclass/mod.rs b/src/rust/examples/tests/orm/testclass/mod.rs new file mode 100644 index 000000000..af1a425f5 --- /dev/null +++ b/src/rust/examples/tests/orm/testclass/mod.rs @@ -0,0 +1,11 @@ +pub(crate) mod all_type_object; +pub(crate) mod auto_add_column_object; +pub(crate) mod column_rename_object; +pub(crate) mod field_object; +pub(crate) mod fts3_test_object; +pub(crate) mod fts5_test_object; +pub(crate) mod mmicu_test_object; +pub(crate) mod primary_enable_auto_increment_object; +pub(crate) mod primary_not_auto_increment_object; +pub(crate) mod table_constraint_object; +pub(crate) mod table_primary_key_object; diff --git a/src/rust/examples/tests/orm/testclass/primary_enable_auto_increment_object.rs b/src/rust/examples/tests/orm/testclass/primary_enable_auto_increment_object.rs new file mode 100644 index 000000000..8e93314ba --- /dev/null +++ b/src/rust/examples/tests/orm/testclass/primary_enable_auto_increment_object.rs @@ -0,0 +1,17 @@ +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding, Clone)] +pub struct PrimaryEnableAutoIncrementObject { + #[WCDBField( + is_primary = true, + is_auto_increment = true, + enable_auto_increment_for_existing_table = true + )] + pub(crate) id: i32, +} + +impl PrimaryEnableAutoIncrementObject { + pub fn new() -> Self { + PrimaryEnableAutoIncrementObject { id: 0 } + } +} diff --git a/src/rust/examples/tests/orm/testclass/primary_not_auto_increment_object.rs b/src/rust/examples/tests/orm/testclass/primary_not_auto_increment_object.rs new file mode 100644 index 000000000..c721ae122 --- /dev/null +++ b/src/rust/examples/tests/orm/testclass/primary_not_auto_increment_object.rs @@ -0,0 +1,13 @@ +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding)] +pub struct PrimaryNotAutoIncrementObject { + #[WCDBField(is_primary = true)] + pub id: i32, +} + +impl PrimaryNotAutoIncrementObject { + pub fn new() -> Self { + PrimaryNotAutoIncrementObject { id: 0 } + } +} diff --git a/src/rust/examples/tests/orm/testclass/table_constraint_object.rs b/src/rust/examples/tests/orm/testclass/table_constraint_object.rs new file mode 100644 index 000000000..5f3b6f336 --- /dev/null +++ b/src/rust/examples/tests/orm/testclass/table_constraint_object.rs @@ -0,0 +1,29 @@ +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding, Clone)] +#[WCDBTable( + multi_primaries(columns = ["multiPrimary1", "multiPrimary2", "multiPrimary3"]), + multi_unique(columns = ["multiUnique1", "multiUnique2", "multiUnique3"]), + multi_indexes(name = "specifiedNameIndex", columns = ["multiIndex1", "multiIndex2", "multiIndex3"]), + multi_indexes(columns = ["multiIndex1", "multiIndex2"]) +)] +pub struct TableConstraintObject { + #[WCDBField(column_name = "multiPrimary1")] + multi_primary1: i32, + #[WCDBField(column_name = "multiPrimary2")] + multi_primary2: i32, + #[WCDBField(column_name = "multiPrimary3")] + multi_primary: i32, + #[WCDBField(column_name = "multiUnique1")] + multi_unique1: i32, + #[WCDBField(column_name = "multiUnique2")] + multi_unique2: i32, + #[WCDBField(column_name = "multiUnique3")] + multi_unique: i32, + #[WCDBField(column_name = "multiIndex1")] + multi_index1: i32, + #[WCDBField(column_name = "multiIndex2")] + multi_index2: i32, + #[WCDBField(column_name = "multiIndex3")] + multi_index: i32, +} diff --git a/src/rust/examples/tests/orm/testclass/table_primary_key_object.rs b/src/rust/examples/tests/orm/testclass/table_primary_key_object.rs new file mode 100644 index 000000000..347fcb174 --- /dev/null +++ b/src/rust/examples/tests/orm/testclass/table_primary_key_object.rs @@ -0,0 +1,69 @@ +use crate::base::random_tool::RandomTool; +use wcdb_derive::WCDBTableCoding; + +#[derive(WCDBTableCoding)] +#[WCDBTable( + multi_primaries(columns = ["id"]), +)] +pub struct TablePrimaryKeyObjectOld { + #[WCDBField] + pub id: i64, + #[WCDBField] + pub name: String, + #[WCDBField] + pub price: f64, + #[WCDBField] + pub desc: String, +} + +impl TablePrimaryKeyObjectOld { + pub fn new() -> Self { + TablePrimaryKeyObjectOld { + id: 0, + name: "".to_string(), + price: 0.0, + desc: "".to_string(), + } + } +} + +impl TablePrimaryKeyObjectOld { + pub fn get_old_obj_vec(data_num: i32) -> Vec { + let mut obj_vec = vec![]; + for i in 0..data_num { + let mut obj = TablePrimaryKeyObjectOld::new(); + obj.id = i as i64; + obj.name = format!("name-{}", RandomTool::string()); + obj.price = i as f64; + obj.desc = format!("desc-{}", RandomTool::string()); + obj_vec.push(obj); + } + obj_vec + } +} + +#[derive(WCDBTableCoding)] +#[WCDBTable( + multi_primaries(columns = ["id","name"]), +)] +pub struct TablePrimaryKeyObjectNew { + #[WCDBField] + pub id: i64, + #[WCDBField] + pub name: String, + #[WCDBField] + pub price: f64, + #[WCDBField] + pub desc: String, +} + +impl TablePrimaryKeyObjectNew { + pub fn new() -> Self { + TablePrimaryKeyObjectNew { + id: 0, + name: "".to_string(), + price: 0.0, + desc: "".to_string(), + } + } +} diff --git a/src/rust/examples/tests/sample/mod.rs b/src/rust/examples/tests/sample/mod.rs new file mode 100644 index 000000000..7364e94f3 --- /dev/null +++ b/src/rust/examples/tests/sample/mod.rs @@ -0,0 +1 @@ +pub mod simple_sample; diff --git a/src/rust/examples/tests/sample/simple_sample.rs b/src/rust/examples/tests/sample/simple_sample.rs new file mode 100644 index 000000000..cbc0948d2 --- /dev/null +++ b/src/rust/examples/tests/sample/simple_sample.rs @@ -0,0 +1,116 @@ +#[cfg(test)] +pub mod simple_sample { + use crate::base::random_tool::RandomTool; + use crate::base::test_object::{DbTestObject, TestObject, DB_TEST_OBJECT_INSTANCE}; + use wcdb::core::database::Database; + use wcdb::core::handle::Handle; + use wcdb::core::handle_operation::HandleOperationTrait; + use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + use wcdb::core::table_orm_operation::TableORMOperationTrait; + use wcdb::winq::column::ColumnTrait; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + use wcdb::winq::ordering_term::Order; + + #[test] + pub fn sample() { + let database = Database::new("./target/tmp/demoDatabase.sqlite3", None); + // database.setCipherKey("abc".getBytes(), 4096, Database.CipherVersion.version4); + // database.setConfig("自定义配置名", new Database.Config() { + // @Override + // public void onInvocation(@NotNull Handle handle) thows WCDBException { + // // Pragma secure_delete = true + // handle.execute(new StatementPragma().pragma(Pragma.secureDelete).toValue(true)); + // } + // }); + // 建表,不用判断表是否存在,底下会判断 + database + .create_table("testTable", &*DB_TEST_OBJECT_INSTANCE) + .unwrap(); + let table = database.get_table("testTable", &*DB_TEST_OBJECT_INSTANCE); + + let test_table = TestObject::new(String::from("abc")); + table + .insert_object(test_table, Some(DbTestObject::all_fields())) + .unwrap(); + let mut messages = Vec::new(); + for x in 0..100 { + let test_table = TestObject::new(RandomTool::string_by_length(x)); + messages.push(test_table); + } + // 批量插入,自动开事务 + table + .insert_objects(messages, Some(DbTestObject::all_fields())) + .unwrap(); + let test_table = TestObject::new(String::from("updateContent")); + + // 更新,可以用一个数据、一行数据、一个对象为单位去更新,后面还可以跟 order,limit,offset 参数 + let test_table = TestObject::create_object(200, String::from("updateContent2")); + let id = DB_TEST_OBJECT_INSTANCE.id; + let filed_id = unsafe { &*id }; + let content = DB_TEST_OBJECT_INSTANCE.content; + let filed_content = unsafe { &*content }; + let express_content = filed_content.eq("updateContent"); + let express = filed_id.eq(100).and(&express_content); + let ret = table.update_object( + test_table, + Some(vec![filed_id]), + Some(&express), + None, + None, + None, + ); + match ret { + Ok(_) => {} + Err(error) => { + println!("update_object_by_field_expression error {:?}", error); + } + } + + // 删除 + let id = DB_TEST_OBJECT_INSTANCE.id; + let filed_id = unsafe { &*id }; + let express = filed_id.lt(10); + // table.delete_objects_by_expression(express).unwrap(); + let ordering_term = filed_id.order(Order::Desc); + let ret = table.delete_objects(None, Some(&ordering_term), Some(10), None); + match ret { + Ok(_) => {} + Err(error) => { + println!("delete_objects_by_order_limit error {:?}", error); + } + } + + // 读取 + let data = table + .get_all_objects(Some(DbTestObject::all_fields()), None, None, None, None) + .unwrap(); + // let id = DB_TEST_OBJECT_INSTANCE.id; + // let filed_id = unsafe { &*id }; + // let expression = filed_id.gt_int(100); + // table + // .get_all_objects_by_expression_order_limit( + // expression, + // filed_id.order(Order::Desc), + // 10, + // ) + // .unwrap(); + + // 执行事务 + let ret = database.run_transaction(move |handle: &Handle| { + let test_table = TestObject::new(String::from("run_transaction")); + table + .insert_object(test_table, Some(DbTestObject::all_fields())) + .unwrap(); + return true; //返回 false 回滚整个事务 + }); + match ret { + Ok(_) => {} + Err(error) => { + println!("run_transaction-->insert_object error {:?}", error); + } + } + database + .delete_objects("testTable", None, None, None, None) + .unwrap() + } +} diff --git a/src/rust/examples/tests/winq/bind_parameter_test.rs b/src/rust/examples/tests/winq/bind_parameter_test.rs new file mode 100644 index 000000000..be3244cc8 --- /dev/null +++ b/src/rust/examples/tests/winq/bind_parameter_test.rs @@ -0,0 +1,29 @@ +#[cfg(test)] +pub mod bind_parameter_test_test { + use crate::base::winq_tool::WinqTool; + use std::ops::Deref; + use wcdb::winq::bind_parameter; + use wcdb::winq::bind_parameter::BindParameter; + + #[test] + pub fn test() { + WinqTool::winq_equal(&BindParameter::new(1), "?1"); + WinqTool::winq_equal(&BindParameter::new("testName"), ":testName"); + WinqTool::winq_equal(&BindParameter::at("testName"), "@testName"); + WinqTool::winq_equal(&BindParameter::dollar("testName"), "$testName"); + WinqTool::winq_equal(&BindParameter::colon("testName"), ":testName"); + WinqTool::winq_equal(&BindParameter::bind_parameters(5)[4], "?5"); + + WinqTool::winq_equal(bind_parameter::DEF.deref(), "?"); + WinqTool::winq_equal(bind_parameter::_1.deref(), "?1"); + WinqTool::winq_equal(bind_parameter::_2.deref(), "?2"); + WinqTool::winq_equal(bind_parameter::_3.deref(), "?3"); + WinqTool::winq_equal(bind_parameter::_4.deref(), "?4"); + WinqTool::winq_equal(bind_parameter::_5.deref(), "?5"); + WinqTool::winq_equal(bind_parameter::_6.deref(), "?6"); + WinqTool::winq_equal(bind_parameter::_7.deref(), "?7"); + WinqTool::winq_equal(bind_parameter::_8.deref(), "?8"); + WinqTool::winq_equal(bind_parameter::_9.deref(), "?9"); + WinqTool::winq_equal(bind_parameter::_10.deref(), "?10"); + } +} diff --git a/src/rust/examples/tests/winq/column_constraint_test.rs b/src/rust/examples/tests/winq/column_constraint_test.rs new file mode 100644 index 000000000..87336f3b3 --- /dev/null +++ b/src/rust/examples/tests/winq/column_constraint_test.rs @@ -0,0 +1,54 @@ +#[cfg(test)] +pub mod column_constraint_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column_constraint::ColumnConstraint; + use wcdb::winq::conflict_action::ConflictAction; + + #[test] + pub fn test() { + WinqTool::winq_equal(ColumnConstraint::new(None).primary_key(), "PRIMARY KEY"); + WinqTool::winq_equal( + ColumnConstraint::new(Some("testColumnConstraint")).primary_key(), + "CONSTRAINT testColumnConstraint PRIMARY KEY", + ); + WinqTool::winq_equal( + ColumnConstraint::new(None).primary_key().auto_increment(), + "PRIMARY KEY AUTOINCREMENT", + ); + WinqTool::winq_equal( + ColumnConstraint::new(Some("testColumnConstraint")).not_null(), + "CONSTRAINT testColumnConstraint NOT NULL", + ); + + WinqTool::winq_equal( + ColumnConstraint::new(None) + .not_null() + .conflict(ConflictAction::Abort), + "NOT NULL ON CONFLICT ABORT", + ); + + WinqTool::winq_equal( + ColumnConstraint::new(Some("testColumnConstraint")).unique(), + "CONSTRAINT testColumnConstraint UNIQUE", + ); + WinqTool::winq_equal( + ColumnConstraint::new(Some("testColumnConstraint")).un_index(), + "CONSTRAINT testColumnConstraint UNINDEXED", + ); + WinqTool::winq_equal(ColumnConstraint::new(None).default_to(1), "DEFAULT 1"); + WinqTool::winq_equal( + ColumnConstraint::new(None).default_to(false), + "DEFAULT FALSE", + ); + WinqTool::winq_equal( + ColumnConstraint::new(None).default_to("abc"), + "DEFAULT 'abc'", + ); + // todo dengxudong 缺逻辑,重要,不紧急 + // WinqTool::winq_equal(ColumnConstraint::new().default_to(ExpressionConvertible), "DEFAULT NULL"); + WinqTool::winq_equal( + ColumnConstraint::new(None).collate("BINARY"), + "COLLATE BINARY", + ); + } +} diff --git a/src/rust/examples/tests/winq/column_def_test.rs b/src/rust/examples/tests/winq/column_def_test.rs new file mode 100644 index 000000000..376f1ff60 --- /dev/null +++ b/src/rust/examples/tests/winq/column_def_test.rs @@ -0,0 +1,71 @@ +#[cfg(test)] +pub mod column_def_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::Column; + use wcdb::winq::column_def::ColumnDef; + use wcdb::winq::column_type::ColumnType; + use wcdb::winq::foreign_key::ForeignKey; + use wcdb::winq::literal_value::LiteralValue; + + #[test] + pub fn test() { + WinqTool::winq_equal(&ColumnDef::new("testColumn"), "testColumn"); + WinqTool::winq_equal( + &ColumnDef::new(("testColumn", ColumnType::Integer)), + "testColumn INTEGER", + ); + WinqTool::winq_equal( + &ColumnDef::new(&Column::new("testColumn", None)), + "testColumn", + ); + WinqTool::winq_equal( + &ColumnDef::new((&Column::new("testColumn", None), ColumnType::Integer)), + "testColumn INTEGER", + ); + WinqTool::winq_equal( + &ColumnDef::new(("testColumn", ColumnType::Integer)), + "testColumn INTEGER", + ); + WinqTool::winq_equal( + gen_column_def().make_primary(None), + "testColumn INTEGER PRIMARY KEY", + ); + WinqTool::winq_equal( + gen_column_def().make_primary(Some(true)), + "testColumn INTEGER PRIMARY KEY AUTOINCREMENT", + ); + WinqTool::winq_equal( + gen_column_def().make_primary(Some(false)), + "testColumn INTEGER PRIMARY KEY", + ); + WinqTool::winq_equal( + gen_column_def().make_not_null(), + "testColumn INTEGER NOT NULL", + ); + WinqTool::winq_equal( + gen_column_def().make_default_to(&LiteralValue::current_date()), + "testColumn INTEGER DEFAULT CURRENT_DATE", + ); + WinqTool::winq_equal( + gen_column_def().make_default_to(&LiteralValue::current_time()), + "testColumn INTEGER DEFAULT CURRENT_TIME", + ); + WinqTool::winq_equal( + gen_column_def().make_default_to(&LiteralValue::current_time_stamp()), + "testColumn INTEGER DEFAULT CURRENT_TIMESTAMP", + ); + WinqTool::winq_equal( + gen_column_def().make_foreign_key(&ForeignKey::new()), + "testColumn INTEGER REFERENCES ", + ); + WinqTool::winq_equal(gen_column_def().make_unique(), "testColumn INTEGER UNIQUE"); + WinqTool::winq_equal( + gen_column_def().make_not_indexed(), + "testColumn INTEGER UNINDEXED", + ); + } + + fn gen_column_def() -> ColumnDef { + ColumnDef::new(("testColumn", ColumnType::Integer)) + } +} diff --git a/src/rust/examples/tests/winq/column_test.rs b/src/rust/examples/tests/winq/column_test.rs new file mode 100644 index 000000000..ef6460e3c --- /dev/null +++ b/src/rust/examples/tests/winq/column_test.rs @@ -0,0 +1,30 @@ +#[cfg(test)] +pub mod column_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::{Column, ColumnStaticTrait, ColumnTrait}; + use wcdb::winq::schema::Schema; + + #[test] + pub fn test() { + WinqTool::winq_equal(&Column::all(), "*"); + WinqTool::winq_equal(&Column::row_id(), "rowid"); + WinqTool::winq_equal(&Column::row_id().as_("rowidAlias"), "rowid AS rowidAlias"); + WinqTool::winq_equal(&Column::new("testColumn", None), "testColumn"); + WinqTool::winq_equal( + Column::new("testColumn", None).table("testTable"), + "testTable.testColumn", + ); + WinqTool::winq_equal( + Column::new("testColumn", None) + .table("testTable") + .of("testSchema"), + "testSchema.testTable.testColumn", + ); + WinqTool::winq_equal( + Column::new("testColumn", None) + .table("testTable") + .of(&Schema::new("testSchema")), + "testSchema.testTable.testColumn", + ); + } +} diff --git a/src/rust/examples/tests/winq/common_table_expression_test.rs b/src/rust/examples/tests/winq/common_table_expression_test.rs new file mode 100644 index 000000000..c8c806d3c --- /dev/null +++ b/src/rust/examples/tests/winq/common_table_expression_test.rs @@ -0,0 +1,33 @@ +#[cfg(test)] +pub mod common_table_expression_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::Column; + use wcdb::winq::common_table_expression::CommonTableExpression; + use wcdb::winq::statement_select::StatementSelect; + + #[test] + pub fn test() { + WinqTool::winq_equal( + CommonTableExpression::new("testTable") + .as_(StatementSelect::new().select(vec!["columnA"])), + "testTable AS(SELECT columnA)", + ); + + let column_a = Column::new("columnA", None); + let column_b = Column::new("columnB", None); + WinqTool::winq_equal( + CommonTableExpression::new("testTable") + .column(&column_a) + .as_(StatementSelect::new().select(vec!["columnB"])), + "testTable(columnA) AS(SELECT columnB)", + ); + + WinqTool::winq_equal( + CommonTableExpression::new("testTable") + .column(&column_a) + .column(&column_b) + .as_(StatementSelect::new().select(vec!["columnC", "columnD"])), + "testTable(columnA, columnB) AS(SELECT columnC, columnD)", + ); + } +} diff --git a/src/rust/examples/tests/winq/expression_test_case.rs b/src/rust/examples/tests/winq/expression_test_case.rs new file mode 100644 index 000000000..818dba76c --- /dev/null +++ b/src/rust/examples/tests/winq/expression_test_case.rs @@ -0,0 +1,685 @@ +#[cfg(test)] +pub mod expression_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::bind_parameter::BindParameter; + use wcdb::winq::column::{Column, ColumnStaticTrait}; + use wcdb::winq::column_type::ColumnType; + use wcdb::winq::expression::Expression; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + use wcdb::winq::identifier::IdentifierTrait; + use wcdb::winq::literal_value::LiteralValue; + use wcdb::winq::statement_select::StatementSelect; + use wcdb::winq::window_def::WindowDef; + + #[test] + pub fn test_expression() { + let column = Column::new("testColumn", None); + let expression = Expression::new(&LiteralValue::new(1)); + WinqTool::winq_equal(&expression, "1"); + let expression = Expression::new(&LiteralValue::new(1.1)); + WinqTool::winq_equal(&expression, "1.1000000000000001"); + let expression = Expression::new(&LiteralValue::new("abc")); + WinqTool::winq_equal(&expression, "'abc'"); + let expression = Expression::new(&LiteralValue::new(false)); + WinqTool::winq_equal(&expression, "FALSE"); + let expression = Expression::new(&LiteralValue::new(true)); + WinqTool::winq_equal(&expression, "TRUE"); + let expression = Expression::new(&column); + WinqTool::winq_equal(&expression, "testColumn"); + let expression = Expression::new(&BindParameter::new(1)); + WinqTool::winq_equal(&expression, "?1"); + + let binding = StatementSelect::new(); + let select = binding.select(vec![&Column::new("testColumn", None)]); + let expression = Expression::exists(select); + WinqTool::winq_equal(&expression, "EXISTS(SELECT testColumn)"); + + let binding = StatementSelect::new(); + let select = binding.select(vec![&Column::new("testColumn", None)]); + let expression = Expression::not_exists(select); + WinqTool::winq_equal(&expression, "NOT EXISTS(SELECT testColumn)"); + + let expression = Expression::cast("testColumn"); + expression.as_(ColumnType::Integer); + WinqTool::winq_equal(&expression, "CAST(testColumn AS INTEGER)"); + + let expression = Expression::cast(&column); + expression.as_(ColumnType::Integer); + WinqTool::winq_equal(&expression, "CAST(testColumn AS INTEGER)"); + + let column_row = Column::row_id().add(1).as_result_column("rowidAddOne"); + WinqTool::winq_equal(&column_row, "rowid + 1 AS rowidAddOne"); + + let expression = Expression::case_(); + expression + .when(&column.eq(1)) + .then("a") + .when(&column.eq(2)) + .then("b") + .else_("c"); + WinqTool::winq_equal( + &expression, + "CASE WHEN testColumn == 1 THEN 'a' WHEN testColumn == 2 THEN 'b' ELSE 'c' END", + ); + + let expression = Expression::case_(); + expression + .when(&column.eq("a")) + .then(1) + .when(&column.eq("b")) + .then(2) + .else_(3); + WinqTool::winq_equal( + &expression, + "CASE WHEN testColumn == 'a' THEN 1 WHEN testColumn == 'b' THEN 2 ELSE 3 END", + ); + + let expression = Expression::case(&column); + expression.when("a").then(1).when("b").then(2).else_(3); + WinqTool::winq_equal( + &expression, + "CASE testColumn WHEN 'a' THEN 1 WHEN 'b' THEN 2 ELSE 3 END", + ); + + let expression = Expression::case(&column); + expression.when(1).then("a").then(2).then("b").else_("c"); + WinqTool::winq_equal( + &expression, + "CASE testColumn WHEN 1 THEN 'a' WHEN 2 THEN 'b' ELSE 'c' END", + ); + + let expression = Expression::window_function("testWindowFunction"); + expression + .invoke() + .argument(&column) + .filter(&column.not_eq(0)); + WinqTool::winq_equal( + &expression, + "testWindowFunction(testColumn) FILTER(WHERE testColumn != 0)", + ); + + let expression = Expression::window_function("testWindowFunction"); + expression + .invoke() + .argument(&column) + .filter(&column.not_eq(0)) + .over("testWindow"); + WinqTool::winq_equal( + &expression, + "testWindowFunction(testColumn) FILTER(WHERE testColumn != 0) OVER testWindow", + ); + + let window_def = WindowDef::new(); + window_def.partition_by(vec![&column]); + let expression = Expression::window_function("testWindowFunction"); + expression + .invoke() + .argument(&column) + .filter(&column.not_eq(0)) + .over(&window_def); + WinqTool::winq_equal(&expression, "testWindowFunction(testColumn) FILTER(WHERE testColumn != 0) OVER(PARTITION BY testColumn)"); + } + + #[test] + pub fn test_unary_operation() { + let column = Column::new("testColumn", None); + let expression = Expression::new(&column); + WinqTool::winq_equal(&expression.is_null(), "testColumn ISNULL"); + WinqTool::winq_equal(&expression.not_null(), "testColumn NOTNULL"); + + WinqTool::winq_equal(&column.is_null(), "testColumn ISNULL"); + WinqTool::winq_equal(&column.not_null(), "testColumn NOTNULL"); + } + + #[test] + pub fn test_expression_binary_operation() { + let expression_left = Expression::new(&Column::new("left", None)); + let expression_right = Expression::new(&Column::new("right", None)); + } + + #[test] + pub fn test_binary_operation() { + let mut column_left = Column::new("left", None); + let column_right = Column::new("right", None); + + let desc = column_left.or(&column_right).get_description(); + assert_eq!(desc.as_str(), "left OR right"); + let desc = column_left.and(&column_right).get_description(); + assert_eq!(desc.as_str(), "left AND right"); + + // multiply assert + let desc = column_left.multiply(&column_right).get_description(); + assert_eq!(desc.as_str(), "left * right"); + let operand: i32 = 1; + let desc = column_left.multiply(operand).get_description(); + assert_eq!( + desc.as_str(), + "left * ".to_owned() + operand.to_string().as_str() + ); + let operand: f64 = 1.1; + let desc = column_left.multiply(operand).get_description(); + assert_eq!(desc.as_str(), "left * 1.1000000000000001"); + let operand: i8 = 1; + let desc = column_left.multiply(operand).get_description(); + assert_eq!( + desc.as_str(), + "left * ".to_owned() + operand.to_string().as_str() + ); + let operand: i16 = 1; + let desc = column_left.multiply(operand).get_description(); + assert_eq!( + desc.as_str(), + "left * ".to_owned() + operand.to_string().as_str() + ); + + // divide assert + let desc = column_left.divide(&column_right).get_description(); + assert_eq!(desc.as_str(), "left / right"); + let operand: i32 = 1; + let desc = column_left.divide(operand).get_description(); + assert_eq!( + desc.as_str(), + "left / ".to_owned() + operand.to_string().as_str() + ); + let operand: f64 = 1.1; + let desc = column_left.divide(operand).get_description(); + assert_eq!(desc.as_str(), "left / 1.1000000000000001"); + + // mod assert + let desc = column_left.mod_(&column_right).get_description(); + assert_eq!(desc.as_str(), "left % right"); + + let operand: i32 = 1; + let desc = column_left.mod_(operand).get_description(); + assert_eq!( + desc.as_str(), + "left % ".to_owned() + operand.to_string().as_str() + ); + + let operand: f64 = 1.1; + let desc = column_left.mod_(operand).get_description(); + assert_eq!(desc.as_str(), "left % 1.1000000000000001"); + + // add assert + let desc = column_left.add(&column_right).get_description(); + assert_eq!(desc.as_str(), "left + right"); + + let operand: i32 = 1; + let desc = column_left.add(operand).get_description(); + assert_eq!( + desc.as_str(), + "left + ".to_owned() + operand.to_string().as_str() + ); + + let operand: f64 = 1.1; + let desc = column_left.add(operand).get_description(); + assert_eq!(desc.as_str(), "left + 1.1000000000000001"); + + // minus assert + let desc = column_left.minus(&column_right).get_description(); + assert_eq!(desc.as_str(), "left - right"); + + let operand: i32 = 1; + let desc = column_left.minus(operand).get_description(); + assert_eq!( + desc.as_str(), + "left - ".to_owned() + operand.to_string().as_str() + ); + + let operand: f64 = 1.1; + let desc = column_left.minus(operand).get_description(); + assert_eq!(desc.as_str(), "left - 1.1000000000000001"); + + // left shift assert + let desc = column_left.left_shift(&column_right).get_description(); + assert_eq!(desc.as_str(), "left << right"); + + let operand: i32 = 1; + let desc = column_left.left_shift(operand).get_description(); + assert_eq!( + desc.as_str(), + "left << ".to_owned() + operand.to_string().as_str() + ); + + // right shift assert + let desc = column_left.right_shift(&column_right).get_description(); + assert_eq!(desc.as_str(), "left >> right"); + + let operand: i32 = 1; + let desc = column_left.right_shift(operand).get_description(); + assert_eq!( + desc.as_str(), + "left >> ".to_owned() + operand.to_string().as_str() + ); + + // bit and assert + let desc = column_left.bit_and(&column_right).get_description(); + assert_eq!(desc.as_str(), "left & right"); + + let operand: i32 = 1; + let desc = column_left.bit_and(operand).get_description(); + assert_eq!( + desc.as_str(), + "left & ".to_owned() + operand.to_string().as_str() + ); + + // bit or assert + let desc = column_left.bit_or(&column_right).get_description(); + assert_eq!(desc.as_str(), "left | right"); + + let operand: i32 = 1; + let desc = column_left.bit_or(operand).get_description(); + assert_eq!( + desc.as_str(), + "left | ".to_owned() + operand.to_string().as_str() + ); + + // lt or assert + let desc = column_left.lt(&column_right).get_description(); + assert_eq!(desc.as_str(), "left < right"); + + let operand: i32 = 1; + let desc = column_left.lt(operand).get_description(); + assert_eq!( + desc.as_str(), + "left < ".to_owned() + operand.to_string().as_str() + ); + + let desc = column_left.lt(1.1).get_description(); + assert_eq!(desc.as_str(), "left < 1.1000000000000001"); + + let desc = column_left.lt("abc").get_description(); + assert_eq!(desc.as_str(), "left < 'abc'"); + + // le or assert + let desc = column_left.le(&column_right).get_description(); + assert_eq!(desc.as_str(), "left <= right"); + + let operand: i32 = 1; + let desc = column_left.le(operand).get_description(); + assert_eq!( + desc.as_str(), + "left <= ".to_owned() + operand.to_string().as_str() + ); + + let desc = column_left.le(1.1).get_description(); + assert_eq!(desc.as_str(), "left <= 1.1000000000000001"); + + let desc = column_left.le("abc").get_description(); + assert_eq!(desc.as_str(), "left <= 'abc'"); + + // gt or assert + let desc = column_left.gt(&column_right).get_description(); + assert_eq!(desc.as_str(), "left > right"); + + let operand: i32 = 1; + let desc = column_left.gt(operand).get_description(); + assert_eq!( + desc.as_str(), + "left > ".to_owned() + operand.to_string().as_str() + ); + + let desc = column_left.gt(1.1).get_description(); + assert_eq!(desc.as_str(), "left > 1.1000000000000001"); + + let desc = column_left.gt("abc").get_description(); + assert_eq!(desc.as_str(), "left > 'abc'"); + + // ge or assert + let desc = column_left.ge(&column_right).get_description(); + assert_eq!(desc.as_str(), "left >= right"); + + let operand: i32 = 1; + let desc = column_left.ge(operand).get_description(); + assert_eq!( + desc.as_str(), + "left >= ".to_owned() + operand.to_string().as_str() + ); + + let desc = column_left.ge(1.1).get_description(); + assert_eq!(desc.as_str(), "left >= 1.1000000000000001"); + + let desc = column_left.ge("abc").get_description(); + assert_eq!(desc.as_str(), "left >= 'abc'"); + + // eq or assert + let desc = column_left.eq(&column_right).get_description(); + assert_eq!(desc.as_str(), "left == right"); + + let desc = column_left.eq(false).get_description(); + assert_eq!(desc.as_str(), "left == FALSE"); + + let operand: i32 = 1; + let desc = column_left.eq(operand).get_description(); + assert_eq!( + desc.as_str(), + "left == ".to_owned() + operand.to_string().as_str() + ); + + let desc = column_left.eq(1.1).get_description(); + assert_eq!(desc.as_str(), "left == 1.1000000000000001"); + + let desc = column_left.eq("abc").get_description(); + assert_eq!(desc.as_str(), "left == 'abc'"); + + //not eq + let desc = column_left.not_eq(&column_right).get_description(); + assert_eq!(desc.as_str(), "left != right"); + + let desc = column_left.not_eq(false).get_description(); + assert_eq!(desc.as_str(), "left != FALSE"); + + let operand: i32 = 1; + let desc = column_left.not_eq(operand).get_description(); + assert_eq!( + desc.as_str(), + "left != ".to_owned() + operand.to_string().as_str() + ); + + let desc = column_left.not_eq(1.1).get_description(); + assert_eq!(desc.as_str(), "left != 1.1000000000000001"); + + let desc = column_left.not_eq("abc").get_description(); + assert_eq!(desc.as_str(), "left != 'abc'"); + + // concat + let desc = column_left.concat(&column_right).get_description(); + assert_eq!(desc.as_str(), "left || right"); + + let operand: i32 = 1; + let desc = column_left.concat(operand).get_description(); + assert_eq!( + desc.as_str(), + "left || ".to_owned() + operand.to_string().as_str() + ); + + let desc = column_left.concat(1.1).get_description(); + assert_eq!(desc.as_str(), "left || 1.1000000000000001"); + + let desc = column_left.concat("abc").get_description(); + assert_eq!(desc.as_str(), "left || 'abc'"); + } + + #[test] + pub fn test_between_operation() { + let column = Column::new("testColumn", None); + let start = Column::new("start", None); + let end = Column::new("end", None); + + let desc = column.between(&start, &end).get_description(); + assert_eq!(desc.as_str(), "testColumn BETWEEN start AND end"); + let desc = column.between(&start, 1).get_description(); + assert_eq!(desc.as_str(), "testColumn BETWEEN start AND 1"); + let desc = column.between(&start, 1.1).get_description(); + assert_eq!( + desc.as_str(), + "testColumn BETWEEN start AND 1.1000000000000001" + ); + let desc = column.between(&start, "abc").get_description(); + assert_eq!(desc.as_str(), "testColumn BETWEEN start AND 'abc'"); + + let desc = column.between(1, &end).get_description(); + assert_eq!(desc.as_str(), "testColumn BETWEEN 1 AND end"); + let desc = column.between(1, 1).get_description(); + assert_eq!(desc.as_str(), "testColumn BETWEEN 1 AND 1"); + let desc = column.between(1, 1.1).get_description(); + assert_eq!(desc.as_str(), "testColumn BETWEEN 1 AND 1.1000000000000001"); + let desc = column.between(1, "abc").get_description(); + assert_eq!(desc.as_str(), "testColumn BETWEEN 1 AND 'abc'"); + + let desc = column.between("abc", &end).get_description(); + assert_eq!(desc.as_str(), "testColumn BETWEEN 'abc' AND end"); + let desc = column.between("abc", 1).get_description(); + assert_eq!(desc.as_str(), "testColumn BETWEEN 'abc' AND 1"); + let desc = column.between("abc", 1.1).get_description(); + assert_eq!( + desc.as_str(), + "testColumn BETWEEN 'abc' AND 1.1000000000000001" + ); + let desc = column.between("abc", "abc").get_description(); + assert_eq!(desc.as_str(), "testColumn BETWEEN 'abc' AND 'abc'"); + + let desc = column.not_between(&start, &end).get_description(); + assert_eq!(desc.as_str(), "testColumn NOT BETWEEN start AND end"); + let desc = column.not_between(&start, 1).get_description(); + assert_eq!(desc.as_str(), "testColumn NOT BETWEEN start AND 1"); + let desc = column.not_between(&start, 1.1).get_description(); + assert_eq!( + desc.as_str(), + "testColumn NOT BETWEEN start AND 1.1000000000000001" + ); + let desc = column.not_between(&start, "abc").get_description(); + assert_eq!(desc.as_str(), "testColumn NOT BETWEEN start AND 'abc'"); + + let desc = column.not_between(1, &end).get_description(); + assert_eq!(desc.as_str(), "testColumn NOT BETWEEN 1 AND end"); + let desc = column.not_between(1, 1).get_description(); + assert_eq!(desc.as_str(), "testColumn NOT BETWEEN 1 AND 1"); + let desc = column.not_between(1, 1.1).get_description(); + assert_eq!( + desc.as_str(), + "testColumn NOT BETWEEN 1 AND 1.1000000000000001" + ); + let desc = column.not_between(1, "abc").get_description(); + assert_eq!(desc.as_str(), "testColumn NOT BETWEEN 1 AND 'abc'"); + + let desc = column.not_between("abc", &end).get_description(); + assert_eq!(desc.as_str(), "testColumn NOT BETWEEN 'abc' AND end"); + let desc = column.not_between("abc", 1).get_description(); + assert_eq!(desc.as_str(), "testColumn NOT BETWEEN 'abc' AND 1"); + let desc = column.not_between("abc", 1.1).get_description(); + assert_eq!( + desc.as_str(), + "testColumn NOT BETWEEN 'abc' AND 1.1000000000000001" + ); + let desc = column.not_between("abc", "abc").get_description(); + assert_eq!(desc.as_str(), "testColumn NOT BETWEEN 'abc' AND 'abc'"); + } + + #[test] + pub fn test_in_operation() { + let column = Column::new("testColumn", None); + + let operands: Vec = vec![1, 2, 3]; + let desc = column.in_(operands).get_description(); + assert_eq!(desc.as_str(), "testColumn IN(1, 2, 3)"); + + let operands: Vec = vec![1, 2, 3]; + let desc = column.in_(operands).get_description(); + assert_eq!(desc.as_str(), "testColumn IN(1, 2, 3)"); + + let operands: Vec = vec![1, 2, 3]; + let desc = column.in_(operands).get_description(); + assert_eq!(desc.as_str(), "testColumn IN(1, 2, 3)"); + + let operands: Vec = vec![1.1f32, 2.1f32, 3.1f32]; + let desc = column.in_(operands).get_description(); + assert_eq!( + desc.as_str(), + "testColumn IN(1.1000000238418579, 2.0999999046325684, 3.0999999046325684)" + ); + + let operands: Vec = vec![1.1f64, 2.1f64, 3.1f64]; + let desc = column.in_(operands).get_description(); + assert_eq!( + desc.as_str(), + "testColumn IN(1.1000000000000001, 2.1000000000000001, 3.1000000000000001)" + ); + + let mut operands: Vec<&str> = Vec::new(); + operands.push("abc"); + operands.push("def"); + operands.push("ghi"); + let desc = column.in_(operands).get_description(); + assert_eq!(desc.as_str(), "testColumn IN('abc', 'def', 'ghi')"); + } + + #[test] + pub fn test_not_in_operation() { + let column = Column::new("testColumn", None); + + let operands: Vec = vec![1, 2, 3]; + let desc = column.not_in(operands).get_description(); + assert_eq!(desc.as_str(), "testColumn NOT IN(1, 2, 3)"); + + let operands: Vec = vec![1, 2, 3]; + let desc = column.not_in(operands).get_description(); + assert_eq!(desc.as_str(), "testColumn NOT IN(1, 2, 3)"); + + let operands: Vec = vec![1, 2, 3]; + let desc = column.not_in(operands).get_description(); + assert_eq!(desc.as_str(), "testColumn NOT IN(1, 2, 3)"); + + let operands: Vec = vec![1.1f32, 2.1f32, 3.1f32]; + let desc = column.not_in(operands).get_description(); + assert_eq!( + desc.as_str(), + "testColumn NOT IN(1.1000000238418579, 2.0999999046325684, 3.0999999046325684)" + ); + + let operands: Vec = vec![1.1f64, 2.1f64, 3.1f64]; + let desc = column.not_in(operands).get_description(); + assert_eq!( + desc.as_str(), + "testColumn NOT IN(1.1000000000000001, 2.1000000000000001, 3.1000000000000001)" + ); + + let mut operands: Vec<&str> = Vec::new(); + operands.push("abc"); + operands.push("def"); + operands.push("ghi"); + let desc = column.not_in(operands).get_description(); + assert_eq!(desc.as_str(), "testColumn NOT IN('abc', 'def', 'ghi')"); + } + + #[test] + pub fn test_collate() { + let column = Column::new("testColumn", None); + let desc = column.collate("BINARY").get_description(); + assert_eq!(desc.as_str(), "testColumn COLLATE BINARY"); + } + + #[test] + pub fn test_function() { + let left = Column::new("left", None); + let right: &str = "right"; + + let desc = left.substr(1, 2).get_description(); + assert_eq!(desc.as_str(), "SUBSTR(left, 1, 2)"); + + let desc = left.like(right).get_description(); + assert_eq!(desc.as_str(), "left LIKE 'right'"); + + let desc = left.glob(right).get_description(); + assert_eq!(desc.as_str(), "left GLOB 'right'"); + + let desc = left.match_(right).get_description(); + assert_eq!(desc.as_str(), "left MATCH 'right'"); + + let desc = left.regexp(right).get_description(); + assert_eq!(desc.as_str(), "left REGEXP 'right'"); + + let desc = left.not_like(right).get_description(); + assert_eq!(desc.as_str(), "left NOT LIKE 'right'"); + + let desc = left.not_glob(right).get_description(); + assert_eq!(desc.as_str(), "left NOT GLOB 'right'"); + + let desc = left.not_match(right).get_description(); + assert_eq!(desc.as_str(), "left NOT MATCH 'right'"); + + let desc = left.not_regexp(right).get_description(); + assert_eq!(desc.as_str(), "left NOT REGEXP 'right'"); + + let desc = left.like(right).escape("%").get_description(); + assert_eq!(desc.as_str(), "left LIKE 'right' ESCAPE '%'"); + + let desc = left.glob(right).escape("%").get_description(); + assert_eq!(desc.as_str(), "left GLOB 'right' ESCAPE '%'"); + + let desc = left.match_(right).escape("%").get_description(); + assert_eq!(desc.as_str(), "left MATCH 'right' ESCAPE '%'"); + + let desc = left.regexp(right).escape("%").get_description(); + assert_eq!(desc.as_str(), "left REGEXP 'right' ESCAPE '%'"); + + let desc = left.not_like(right).escape("%").get_description(); + assert_eq!(desc.as_str(), "left NOT LIKE 'right' ESCAPE '%'"); + + let desc = left.not_glob(right).escape("%").get_description(); + assert_eq!(desc.as_str(), "left NOT GLOB 'right' ESCAPE '%'"); + + let desc = left.not_match(right).escape("%").get_description(); + assert_eq!(desc.as_str(), "left NOT MATCH 'right' ESCAPE '%'"); + + let desc = left.not_regexp(right).escape("%").get_description(); + assert_eq!(desc.as_str(), "left NOT REGEXP 'right' ESCAPE '%'"); + + //is + let desc = left.is(right).get_description(); + assert_eq!(desc.as_str(), "left IS 'right'"); + + let desc = left.is_not(right).get_description(); + assert_eq!(desc.as_str(), "left IS NOT 'right'"); + + let desc = left.avg().get_description(); + assert_eq!(desc.as_str(), "AVG(left)"); + + let desc = left.count().distinct().get_description(); + assert_eq!(desc.as_str(), "COUNT(DISTINCT left)"); + + let desc = left.group_concat().get_description(); + assert_eq!(desc.as_str(), "GROUP_CONCAT(left)"); + + let desc = left.group_concat_string("-").distinct().get_description(); + assert_eq!(desc.as_str(), "GROUP_CONCAT(DISTINCT left, '-')"); + + let desc = left.max().get_description(); + assert_eq!(desc.as_str(), "MAX(left)"); + + let desc = left.min().get_description(); + assert_eq!(desc.as_str(), "MIN(left)"); + + let desc = left.sum().get_description(); + assert_eq!(desc.as_str(), "SUM(left)"); + + let desc = left.total().get_description(); + assert_eq!(desc.as_str(), "TOTAL(left)"); + + let desc = left.abs().get_description(); + assert_eq!(desc.as_str(), "ABS(left)"); + + let desc = left.hex().get_description(); + assert_eq!(desc.as_str(), "HEX(left)"); + + let desc = left.length().get_description(); + assert_eq!(desc.as_str(), "LENGTH(left)"); + + let desc = left.lower().get_description(); + assert_eq!(desc.as_str(), "LOWER(left)"); + + let desc = left.upper().get_description(); + assert_eq!(desc.as_str(), "UPPER(left)"); + + let desc = left.round().get_description(); + assert_eq!(desc.as_str(), "ROUND(left)"); + + let desc = left.match_info().get_description(); + assert_eq!(desc.as_str(), "matchInfo(left)"); + + let desc = left.offsets().get_description(); + assert_eq!(desc.as_str(), "offsets(left)"); + + let desc = left.snippet().get_description(); + assert_eq!(desc.as_str(), "snippet(left)"); + + let desc = left.bm25().get_description(); + assert_eq!(desc.as_str(), "bm25(left)"); + + let desc = left.highlight().get_description(); + assert_eq!(desc.as_str(), "highlight(left)"); + + let desc = left.substring_match_info().get_description(); + assert_eq!(desc.as_str(), "substring_match_info(left)"); + } +} diff --git a/src/rust/examples/tests/winq/foreign_key_test.rs b/src/rust/examples/tests/winq/foreign_key_test.rs new file mode 100644 index 000000000..3984d30d1 --- /dev/null +++ b/src/rust/examples/tests/winq/foreign_key_test.rs @@ -0,0 +1,54 @@ +#[cfg(test)] +pub mod foreign_key_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::Column; + use wcdb::winq::foreign_key::{Action, ForeignKey, Initially, Match}; + + #[test] + pub fn test() { + WinqTool::winq_equal( + gen_foreign_key().on_delete(Action::SetNull), + "REFERENCES testForeignKeyTable(column1, column2) ON DELETE SET NULL", + ); + + WinqTool::winq_equal( + gen_foreign_key().on_update(Action::SetDefault), + "REFERENCES testForeignKeyTable(column1, column2) ON UPDATE SET DEFAULT", + ); + WinqTool::winq_equal( + gen_foreign_key().on_delete(Action::Cascade), + "REFERENCES testForeignKeyTable(column1, column2) ON DELETE CASCADE", + ); + WinqTool::winq_equal( + gen_foreign_key().on_update(Action::Restrict), + "REFERENCES testForeignKeyTable(column1, column2) ON UPDATE RESTRICT", + ); + WinqTool::winq_equal( + gen_foreign_key().on_delete(Action::NoAction), + "REFERENCES testForeignKeyTable(column1, column2) ON DELETE NO ACTION", + ); + + WinqTool::winq_equal( + gen_foreign_key().deferrable(Initially::Deferred), + "REFERENCES testForeignKeyTable(column1, column2) DEFERRABLE INITIALLY DEFERRED", + ); + + WinqTool::winq_equal( + gen_foreign_key().not_deferrable(Initially::Immediate), + "REFERENCES testForeignKeyTable(column1, column2) NOT DEFERRABLE INITIALLY IMMEDIATE", + ); + WinqTool::winq_equal( + gen_foreign_key().match_(Match::Simple), + "REFERENCES testForeignKeyTable(column1, column2) MATCH SIMPLE", + ); + } + + pub fn gen_foreign_key() -> ForeignKey { + let column1 = Column::new("column1", None); + let column2 = Column::new("column2", None); + let key = ForeignKey::new(); + key.references("testForeignKeyTable"); + key.columns(&vec![column1, column2]); + key + } +} diff --git a/src/rust/examples/tests/winq/frame_spec_test.rs b/src/rust/examples/tests/winq/frame_spec_test.rs new file mode 100644 index 000000000..392816a08 --- /dev/null +++ b/src/rust/examples/tests/winq/frame_spec_test.rs @@ -0,0 +1,68 @@ +#[cfg(test)] +pub mod frame_spec_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::frame_spec::FrameSpec; + + #[test] + pub fn test() { + WinqTool::winq_equal( + FrameSpec::new().range().unbounded_preceding(), + "RANGE UNBOUNDED PRECEDING", + ); + WinqTool::winq_equal( + FrameSpec::new().rows().unbounded_preceding(), + "ROWS UNBOUNDED PRECEDING", + ); + WinqTool::winq_equal(FrameSpec::new().range().preceding(1), "RANGE 1 PRECEDING"); + WinqTool::winq_equal(FrameSpec::new().range().current_row(), "RANGE CURRENT ROW"); + WinqTool::winq_equal( + FrameSpec::new() + .range() + .between_unbounded_preceding() + .and_preceding(2), + "RANGE BETWEEN UNBOUNDED PRECEDING AND 2 PRECEDING", + ); + WinqTool::winq_equal( + FrameSpec::new() + .range() + .between_preceding(1) + .and_preceding(2), + "RANGE BETWEEN 1 PRECEDING AND 2 PRECEDING", + ); + WinqTool::winq_equal( + FrameSpec::new() + .range() + .between_current_row() + .and_preceding(2), + "RANGE BETWEEN CURRENT ROW AND 2 PRECEDING", + ); + WinqTool::winq_equal( + FrameSpec::new() + .range() + .between_following(1) + .and_preceding(2), + "RANGE BETWEEN 1 FOLLOWING AND 2 PRECEDING", + ); + WinqTool::winq_equal( + FrameSpec::new() + .range() + .between_unbounded_preceding() + .and_current_row(), + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW", + ); + WinqTool::winq_equal( + FrameSpec::new() + .range() + .between_unbounded_preceding() + .and_following(2), + "RANGE BETWEEN UNBOUNDED PRECEDING AND 2 FOLLOWING", + ); + WinqTool::winq_equal( + FrameSpec::new() + .range() + .between_unbounded_preceding() + .and_unbounded_following(), + "RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING", + ); + } +} diff --git a/src/rust/examples/tests/winq/join_test.rs b/src/rust/examples/tests/winq/join_test.rs new file mode 100644 index 000000000..418ad0c2d --- /dev/null +++ b/src/rust/examples/tests/winq/join_test.rs @@ -0,0 +1,495 @@ +use wcdb::winq::column::Column; +use wcdb::winq::identifier::IdentifierTrait; +use wcdb::winq::statement_select::StatementSelect; +use wcdb_derive::WCDBTableCoding; + +pub struct JoinTest {} +impl JoinTest { + pub fn new() -> JoinTest { + JoinTest {} + } + + fn select1(&self) -> StatementSelect { + let column = Column::new("column1", None); + let columns = vec![column]; + let ret = StatementSelect::new(); + ret.select(&columns).from(vec!["testTable1"]); + ret + } + + fn select1sql(&self) -> String { + let statement_select = self.select1(); + let mut ret: String = "".to_string(); + ret = format!("{}{}{}", "(", statement_select.get_description(), ")"); + ret + } + + fn select2(&self) -> StatementSelect { + let column = Column::new("column2", None); + let columns = vec![column]; + let ret = StatementSelect::new(); + ret.select(&columns).from(vec!["testTable2"]); + ret + } + + fn select2sql(&self) -> String { + let statement_select = self.select2(); + let mut ret: String = "".to_string(); + ret = format!("{}{}{}", "(", statement_select.get_description(), ")"); + ret + } +} + +#[derive(WCDBTableCoding)] +#[WCDBTable()] +pub struct MessageTagTable { + #[WCDBField] + tag_id: String, + #[WCDBField] + tag_name: String, + #[WCDBField] + create_time: i64, +} +impl MessageTagTable { + pub fn new() -> Self { + MessageTagTable { + tag_id: "".to_string(), + tag_name: "".to_string(), + create_time: 0, + } + } +} + +#[derive(WCDBTableCoding)] +#[WCDBTable()] +pub struct ConversationTagTable { + #[WCDBField] + tag_id: String, + #[WCDBField] + target_id: String, + #[WCDBField] + category_id: String, + #[WCDBField] + is_top: bool, +} + +impl ConversationTagTable { + pub fn new() -> Self { + ConversationTagTable { + tag_id: "".to_string(), + target_id: "".to_string(), + category_id: "".to_string(), + is_top: false, + } + } +} + +pub(crate) struct SelectResult { + pub message_tag_table: MessageTagTable, + pub conversation_tag_table: ConversationTagTable, +} + +impl SelectResult { + pub fn new() -> Self { + SelectResult { + message_tag_table: MessageTagTable::new(), + conversation_tag_table: ConversationTagTable::new(), + } + } +} + +#[cfg(test)] +pub mod join_test { + use crate::base::winq_tool::WinqTool; + use crate::winq::join_test::{ + ConversationTagTable, DbConversationTagTable, DbMessageTagTable, JoinTest, MessageTagTable, + SelectResult, DB_CONVERSATION_TAG_TABLE_INSTANCE, DB_MESSAGE_TAG_TABLE_INSTANCE, + }; + use wcdb::base::value::Value; + use wcdb::base::wcdb_exception::WCDBResult; + use wcdb::core::database::Database; + use wcdb::core::handle_orm_operation::HandleORMOperationTrait; + use wcdb::core::table_orm_operation::TableORMOperationTrait; + use wcdb::winq::column::{Column, ColumnStaticTrait}; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + use wcdb::winq::identifier::IdentifierTrait; + use wcdb::winq::join::Join; + use wcdb::winq::result_column::ResultColumn; + use wcdb::winq::statement_select::StatementSelect; + + #[test] + pub fn test() { + let test_table_name1 = "testTable1"; + let test_table_name2 = "testTable2"; + + let join_left = Join::new(test_table_name1); + let join_right = Join::new(test_table_name2); + WinqTool::winq_equal( + join_left.join(test_table_name2), + "testTable1 JOIN testTable2", + ); + + let join_test = JoinTest::new(); + let join_left = Join::new(&join_test.select1()); + let join_right = Join::new(&join_test.select2()); + let select1sql = join_test.select1sql(); + let select2sql = join_test.select2sql(); + WinqTool::winq_equal( + join_left.join(&join_test.select2()), + &*format!("{}{}{}", select1sql, " JOIN ", select2sql).to_string(), + ); + + let join_left = Join::new(test_table_name1); + let join_right = Join::new(test_table_name2); + WinqTool::winq_equal(join_left.with(test_table_name2), "testTable1, testTable2"); + + let join_test = JoinTest::new(); + let join_left = Join::new(&join_test.select1()); + let join_right = Join::new(&join_test.select2()); + let select1sql = join_test.select1sql(); + let select2sql = join_test.select2sql(); + WinqTool::winq_equal( + join_left.with(&join_test.select2()), + &*format!("{}{}{}", select1sql, ", ", select2sql).to_string(), + ); + + let join_left = Join::new(test_table_name1); + let join_right = Join::new(test_table_name2); + WinqTool::winq_equal( + join_left.left_join(test_table_name2), + "testTable1 LEFT JOIN testTable2", + ); + + let join_test = JoinTest::new(); + let join_left = Join::new(&join_test.select1()); + let join_right = Join::new(&join_test.select2()); + let select1sql = join_test.select1sql(); + let select2sql = join_test.select2sql(); + WinqTool::winq_equal( + join_left.left_join(&join_test.select2()), + &*format!("{}{}{}", select1sql, " LEFT JOIN ", select2sql).to_string(), + ); + + let join_left = Join::new(test_table_name1); + let join_right = Join::new(test_table_name2); + WinqTool::winq_equal( + join_left.left_outer_join(test_table_name2), + "testTable1 LEFT OUTER JOIN testTable2", + ); + + let join_test = JoinTest::new(); + let join_left = Join::new(&join_test.select1()); + let join_right = Join::new(&join_test.select2()); + let select1sql = join_test.select1sql(); + let select2sql = join_test.select2sql(); + WinqTool::winq_equal( + join_left.left_outer_join(&join_test.select2()), + &*format!("{}{}{}", select1sql, " LEFT OUTER JOIN ", select2sql).to_string(), + ); + + let join_left = Join::new(test_table_name1); + let join_right = Join::new(test_table_name2); + WinqTool::winq_equal( + join_left.inner_join(test_table_name2), + "testTable1 INNER JOIN testTable2", + ); + + let join_test = JoinTest::new(); + let join_left = Join::new(&join_test.select1()); + let join_right = Join::new(&join_test.select2()); + let select1sql = join_test.select1sql(); + let select2sql = join_test.select2sql(); + WinqTool::winq_equal( + join_left.inner_join(&join_test.select2()), + &*format!("{}{}{}", select1sql, " INNER JOIN ", select2sql).to_string(), + ); + + let join_left = Join::new(test_table_name1); + let join_right = Join::new(test_table_name2); + WinqTool::winq_equal( + join_left.cross_join(test_table_name2), + "testTable1 CROSS JOIN testTable2", + ); + + let join_test = JoinTest::new(); + let join_left = Join::new(&join_test.select1()); + let join_right = Join::new(&join_test.select2()); + let select1sql = join_test.select1sql(); + let select2sql = join_test.select2sql(); + WinqTool::winq_equal( + join_left.cross_join(&join_test.select2()), + &*format!("{}{}{}", select1sql, " CROSS JOIN ", select2sql).to_string(), + ); + + let join_left = Join::new(test_table_name1); + let join_right = Join::new(test_table_name2); + WinqTool::winq_equal( + join_left.natural_join(test_table_name2), + "testTable1 NATURAL JOIN testTable2", + ); + + let join_test = JoinTest::new(); + let join_left = Join::new(&join_test.select1()); + let join_right = Join::new(&join_test.select2()); + let select1sql = join_test.select1sql(); + let select2sql = join_test.select2sql(); + WinqTool::winq_equal( + join_left.natural_join(&join_test.select2()), + &*format!("{}{}{}", select1sql, " NATURAL JOIN ", select2sql).to_string(), + ); + + let join_left = Join::new(test_table_name1); + let join_right = Join::new(test_table_name2); + WinqTool::winq_equal( + join_left.natural_left_join(test_table_name2), + "testTable1 NATURAL LEFT JOIN testTable2", + ); + + let join_test = JoinTest::new(); + let join_left = Join::new(&join_test.select1()); + let join_right = Join::new(&join_test.select2()); + let select1sql = join_test.select1sql(); + let select2sql = join_test.select2sql(); + WinqTool::winq_equal( + join_left.natural_left_join(&join_test.select2()), + &*format!("{}{}{}", select1sql, " NATURAL LEFT JOIN ", select2sql).to_string(), + ); + + let join_left = Join::new(test_table_name1); + let join_right = Join::new(test_table_name2); + WinqTool::winq_equal( + join_left.natural_left_outer_join(test_table_name2), + "testTable1 NATURAL LEFT OUTER JOIN testTable2", + ); + + let join_test = JoinTest::new(); + let join_left = Join::new(&join_test.select1()); + let join_right = Join::new(&join_test.select2()); + let select1sql = join_test.select1sql(); + let select2sql = join_test.select2sql(); + WinqTool::winq_equal( + join_left.natural_left_outer_join(&join_test.select2()), + &*format!( + "{}{}{}", + select1sql, " NATURAL LEFT OUTER JOIN ", select2sql + ) + .to_string(), + ); + + let join_left = Join::new(test_table_name1); + let join_right = Join::new(test_table_name2); + WinqTool::winq_equal( + join_left.natural_inner_join(test_table_name2), + "testTable1 NATURAL INNER JOIN testTable2", + ); + + let join_test = JoinTest::new(); + let join_left = Join::new(&join_test.select1()); + let join_right = Join::new(&join_test.select2()); + let select1sql = join_test.select1sql(); + let select2sql = join_test.select2sql(); + WinqTool::winq_equal( + join_left.natural_inner_join(&join_test.select2()), + &*format!("{}{}{}", select1sql, " NATURAL INNER JOIN ", select2sql).to_string(), + ); + + let join_left = Join::new(test_table_name1); + let join_right = Join::new(test_table_name2); + WinqTool::winq_equal( + join_left.natural_cross_join(test_table_name2), + "testTable1 NATURAL CROSS JOIN testTable2", + ); + + let join_test = JoinTest::new(); + let join_left = Join::new(&join_test.select1()); + let join_right = Join::new(&join_test.select2()); + let select1sql = join_test.select1sql(); + let select2sql = join_test.select2sql(); + WinqTool::winq_equal( + join_left.natural_cross_join(&join_test.select2()), + &*format!("{}{}{}", select1sql, " NATURAL CROSS JOIN ", select2sql).to_string(), + ); + + let join_left = Join::new(test_table_name1); + let join_right = Join::new(test_table_name2); + let column1 = Column::new("column1", None); + let column2 = Column::new("column2", None); + WinqTool::winq_equal( + join_left.join(test_table_name2).on(&column1.eq(&column2)), + "testTable1 JOIN testTable2 ON column1 == column2", + ); + + let join_test = JoinTest::new(); + let join_left = Join::new(&join_test.select1()); + let join_right = Join::new(&join_test.select2()); + let column1 = Column::new("column1", None); + let column2 = Column::new("column2", None); + let select1sql = join_test.select1sql(); + let select2sql = join_test.select2sql(); + WinqTool::winq_equal( + join_left + .join(&join_test.select2()) + .on(&column1.eq(&column2)), + &*format!( + "{}{}{}{}", + select1sql, " JOIN ", select2sql, " ON column1 == column2" + ) + .to_string(), + ); + + let join_left = Join::new(test_table_name1); + let column1 = Column::new("column1", None); + let column2 = Column::new("column2", None); + let mut column_vec: Vec = Vec::new(); + column_vec.push(column1); + column_vec.push(column2); + WinqTool::winq_equal( + join_left.join(test_table_name2).using(&column_vec), + "testTable1 JOIN testTable2 USING(column1, column2)", + ); + + let join_test = JoinTest::new(); + let join_left = Join::new(&join_test.select1()); + let join_right = Join::new(&join_test.select2()); + let column1 = Column::new("column1", None); + let column2 = Column::new("column2", None); + let select1sql = join_test.select1sql(); + let select2sql = join_test.select2sql(); + let mut column_vec: Vec = Vec::new(); + column_vec.push(column1); + column_vec.push(column2); + WinqTool::winq_equal( + join_left.join(&join_test.select2()).using(&column_vec), + &*format!( + "{}{}{}{}", + select1sql, " JOIN ", select2sql, " USING(column1, column2)" + ), + ); + } + + // 新增的联表查询单测,Java 没有该用例 + #[test] + pub fn join_test1() { + let database = Database::new("./tests/winq/custom/JoinDatabase.sqlite3", None); + database + .create_table("MessageTagTable", &*DB_MESSAGE_TAG_TABLE_INSTANCE) + .unwrap(); + database + .create_table("ConversationTagTable", &*DB_CONVERSATION_TAG_TABLE_INSTANCE) + .unwrap(); + let message_tag_table = + database.get_table("MessageTagTable", &*DB_MESSAGE_TAG_TABLE_INSTANCE); + let conversation_tag_table = + database.get_table("ConversationTagTable", &*DB_CONVERSATION_TAG_TABLE_INSTANCE); + + // 插入数据 + let mut tag = MessageTagTable::new(); + tag.tag_id = "10001".to_string(); + tag.tag_name = "10001_name".to_string(); + tag.create_time = 1790000000; + let _ = message_tag_table.insert_object(tag, Some(DbMessageTagTable::all_fields())); + + let mut tag = MessageTagTable::new(); + tag.tag_id = "10002".to_string(); + tag.tag_name = "10002_name".to_string(); + tag.create_time = 1790000001; + let ret = message_tag_table.insert_object(tag, Some(DbMessageTagTable::all_fields())); + + let mut conversation = ConversationTagTable::new(); + conversation.tag_id = "10001".to_string(); + conversation.target_id = "target_id".to_string(); + conversation.category_id = "category_id".to_string(); + conversation.is_top = true; + let ret = conversation_tag_table + .insert_object(conversation, Some(DbConversationTagTable::all_fields())); + + let mut conversation = ConversationTagTable::new(); + conversation.tag_id = "20001".to_string(); + conversation.target_id = "target_id".to_string(); + conversation.category_id = "category_id".to_string(); + conversation.is_top = true; + let ret = conversation_tag_table + .insert_object(conversation, Some(DbConversationTagTable::all_fields())); + + let result_column1 = ResultColumn::new("tag_id"); + result_column1.as_("a_tag_id"); + let result_column2 = ResultColumn::new("tag_name"); + let result_column3 = ResultColumn::new("create_time"); + // 连表查询 + let column_vec = vec![result_column1, result_column2, result_column3]; + let binding = StatementSelect::new(); + let tag_statement = binding.select(&column_vec).from(vec!["MessageTagTable"]); + // conversation + let column_vec: Vec = vec![ + Column::new("tag_id", None), + Column::new("target_id", None), + Column::new("category_id", None), + Column::new("is_top", None), + ]; + let binding = StatementSelect::new(); + let conversation_statement = binding + .select(&column_vec) + .from(vec!["ConversationTagTable"]); + + // 构建 join + let column1 = Column::new("a_tag_id", None); + let column2 = Column::new("tag_id", None); + let join = Join::new(tag_statement); + join.left_join(conversation_statement) + .on(&column1.eq(&column2)); + + // 构建查询需要的 StatementSelect + let column_tag_id = Column::new("tag_id", None); + column_tag_id.in_table("MessageTagTable"); + let select = StatementSelect::new(); + select.select(&vec![Column::all()]).from(&vec![join]); + // .group_by_with_expression_convertible_trait(&vec![column_tag_id]); + + let sql = select.get_description(); + + assert_eq!(sql, + "SELECT * FROM ((SELECT tag_id AS a_tag_id, tag_name, create_time FROM MessageTagTable) LEFT JOIN (SELECT tag_id, target_id, category_id, is_top FROM ConversationTagTable) ON a_tag_id == tag_id)"); + + let ret: WCDBResult>> = database.get_all_rows_from_statement(&select); + let mut select_result_vec: Vec = Vec::new(); + match ret { + Ok(vals) => { + for x in vals { + let mut result = SelectResult::new(); + let mut tag = MessageTagTable::new(); + let mut conversation = ConversationTagTable::new(); + for v in x { + tag.tag_id = v.get_text(); + tag.tag_name = v.get_text(); + tag.create_time = v.get_i64(); + + conversation.tag_id = v.get_text(); + conversation.target_id = v.get_text(); + conversation.category_id = v.get_text(); + conversation.is_top = v.get_bool(); + } + result.message_tag_table = tag; + result.conversation_tag_table = conversation; + select_result_vec.push(result); + } + } + Err(err) => { + println!("Failed to get all rows from the statement.err: {:?}", err); + } + } + assert!(!select_result_vec.is_empty()); + + let value_opt = database.get_value_from_sql("SELECT COUNT(*) FROM MessageTagTable"); + match value_opt { + Ok(value) => { + assert!(value.get_i64() > 0); + } + Err(error) => { + println!("get_value_from_sql-->err: {:?}", error); + } + } + + database.remove_files().unwrap(); + database.close(Some(|| {})); + } +} diff --git a/src/rust/examples/tests/winq/mod.rs b/src/rust/examples/tests/winq/mod.rs new file mode 100644 index 000000000..9f6d0fe80 --- /dev/null +++ b/src/rust/examples/tests/winq/mod.rs @@ -0,0 +1,41 @@ +pub(crate) mod bind_parameter_test; +pub(crate) mod column_constraint_test; +pub(crate) mod column_def_test; +pub(crate) mod column_test; +pub(crate) mod common_table_expression_test; +pub(crate) mod expression_test_case; +pub(crate) mod foreign_key_test; +pub(crate) mod frame_spec_test; +pub(crate) mod join_test; +pub(crate) mod ordering_term_test; +pub(crate) mod pragma_test; +pub(crate) mod qualified_table_test; +pub(crate) mod result_column_test; +pub(crate) mod schema_test; +pub(crate) mod statement_alter_table_test; +pub(crate) mod statement_analyze_test; +pub(crate) mod statement_attach_test; +pub(crate) mod statement_begin_test; +pub(crate) mod statement_commit_test; +pub(crate) mod statement_create_index_test; +pub(crate) mod statement_create_table_test; +pub(crate) mod statement_create_trigger_test; +pub(crate) mod statement_create_view_test; +pub(crate) mod statement_create_virtual_table_test; +pub(crate) mod statement_delete_test; +pub(crate) mod statement_detach_test; +pub(crate) mod statement_drop_index_test; +pub(crate) mod statement_drop_table_test; +pub(crate) mod statement_drop_trigger_test; +pub(crate) mod statement_drop_view_test; +pub(crate) mod statement_explain_test; +pub(crate) mod statement_insert_test; +pub(crate) mod statement_pragma_test; +pub(crate) mod statement_reindex_test; +pub(crate) mod statement_release_test; +pub(crate) mod statement_rollback_test; +pub(crate) mod statement_select_test; +pub(crate) mod statement_update_test; +pub(crate) mod statement_vacuum_test; +pub(crate) mod upsert_test_case; +pub(crate) mod window_def_test; diff --git a/src/rust/examples/tests/winq/ordering_term_test.rs b/src/rust/examples/tests/winq/ordering_term_test.rs new file mode 100644 index 000000000..ef56aa432 --- /dev/null +++ b/src/rust/examples/tests/winq/ordering_term_test.rs @@ -0,0 +1,20 @@ +#[cfg(test)] +pub mod ordering_term_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::Column; + use wcdb::winq::ordering_term::{Order, OrderingTerm}; + + #[test] + pub fn test() { + let column = Column::new("testColumn", None); + WinqTool::winq_equal(&OrderingTerm::new(&column), "testColumn"); + WinqTool::winq_equal( + &OrderingTerm::new(&column).order(Order::Asc), + "testColumn ASC", + ); + WinqTool::winq_equal( + &OrderingTerm::new(&column).order(Order::Desc), + "testColumn DESC", + ); + } +} diff --git a/src/rust/examples/tests/winq/pragma_test.rs b/src/rust/examples/tests/winq/pragma_test.rs new file mode 100644 index 000000000..a3d7b06ed --- /dev/null +++ b/src/rust/examples/tests/winq/pragma_test.rs @@ -0,0 +1,103 @@ +#[cfg(test)] +pub mod pragma_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::pragma::Pragma; + + #[test] + pub fn test() { + WinqTool::winq_equal(&Pragma::new("testPragma"), "testPragma"); + WinqTool::winq_equal(&Pragma::application_id(), "application_id"); + WinqTool::winq_equal(&Pragma::auto_vacuum(), "auto_vacuum"); + WinqTool::winq_equal(&Pragma::automatic_index(), "automatic_index"); + WinqTool::winq_equal(&Pragma::busy_timeout(), "busy_timeout"); + WinqTool::winq_equal(&Pragma::cache_size(), "cache_size"); + WinqTool::winq_equal(&Pragma::cache_spill(), "cache_spill"); + WinqTool::winq_equal(&Pragma::case_sensitive_like(), "case_sensitive_like"); + WinqTool::winq_equal(&Pragma::cell_size_check(), "cell_size_check"); + WinqTool::winq_equal(&Pragma::checkpoint_fullfsync(), "checkpoint_fullfsync"); + WinqTool::winq_equal(&Pragma::cipher(), "cipher"); + WinqTool::winq_equal(&Pragma::cipher_add_random(), "cipher_add_random"); + WinqTool::winq_equal( + &Pragma::cipher_default_kdf_iter(), + "cipher_default_kdf_iter", + ); + WinqTool::winq_equal( + &Pragma::cipher_default_page_size(), + "cipher_default_page_size", + ); + WinqTool::winq_equal( + &Pragma::cipher_default_use_hmac(), + "cipher_default_use_hmac", + ); + WinqTool::winq_equal(&Pragma::cipher_migrate(), "cipher_migrate"); + WinqTool::winq_equal(&Pragma::cipher_profile(), "cipher_profile"); + WinqTool::winq_equal(&Pragma::cipher_provider(), "cipher_provider"); + WinqTool::winq_equal( + &Pragma::cipher_provider_version(), + "cipher_provider_version", + ); + WinqTool::winq_equal(&Pragma::cipher_use_hmac(), "cipher_use_hmac"); + WinqTool::winq_equal(&Pragma::cipher_version(), "cipher_version"); + WinqTool::winq_equal(&Pragma::cipher_page_size(), "cipher_page_size"); + WinqTool::winq_equal(&Pragma::collation_list(), "collation_list"); + WinqTool::winq_equal(&Pragma::data_version(), "data_version"); + WinqTool::winq_equal(&Pragma::database_list(), "database_list"); + WinqTool::winq_equal(&Pragma::defer_foreign_keys(), "defer_foreign_keys"); + WinqTool::winq_equal(&Pragma::encoding(), "encoding"); + WinqTool::winq_equal(&Pragma::foreign_key_check(), "foreign_key_check"); + WinqTool::winq_equal(&Pragma::foreign_key_list(), "foreign_key_list"); + WinqTool::winq_equal(&Pragma::foreign_keys(), "foreign_keys"); + WinqTool::winq_equal(&Pragma::freelist_count(), "freelist_count"); + WinqTool::winq_equal(&Pragma::fullfsync(), "fullfsync"); + WinqTool::winq_equal(&Pragma::function_list(), "function_list"); + WinqTool::winq_equal( + &Pragma::ignore_check_constraints(), + "ignore_check_constraints", + ); + WinqTool::winq_equal(&Pragma::incremental_vacuum(), "incremental_vacuum"); + WinqTool::winq_equal(&Pragma::index_info(), "index_info"); + WinqTool::winq_equal(&Pragma::index_list(), "index_list"); + WinqTool::winq_equal(&Pragma::index_x_info(), "index_xinfo"); + WinqTool::winq_equal(&Pragma::integrity_check(), "integrity_check"); + WinqTool::winq_equal(&Pragma::journal_mode(), "journal_mode"); + WinqTool::winq_equal(&Pragma::journal_size_limit(), "journal_size_limit"); + WinqTool::winq_equal(&Pragma::key(), "key"); + WinqTool::winq_equal(&Pragma::kdf_iter(), "kdf_iter"); + WinqTool::winq_equal(&Pragma::legacy_file_format(), "legacy_file_format"); + WinqTool::winq_equal(&Pragma::locking_mode(), "locking_mode"); + WinqTool::winq_equal(&Pragma::max_page_count(), "max_page_count"); + WinqTool::winq_equal(&Pragma::mmap_size(), "mmap_size"); + WinqTool::winq_equal(&Pragma::module_list(), "module_list"); + WinqTool::winq_equal(&Pragma::optimize(), "optimize"); + WinqTool::winq_equal(&Pragma::page_count(), "page_count"); + WinqTool::winq_equal(&Pragma::page_size(), "page_size"); + WinqTool::winq_equal(&Pragma::parser_trace(), "parser_trace"); + WinqTool::winq_equal(&Pragma::pragma_list(), "pragma_list"); + WinqTool::winq_equal(&Pragma::query_only(), "query_only"); + WinqTool::winq_equal(&Pragma::quick_check(), "quick_check"); + WinqTool::winq_equal(&Pragma::read_uncommitted(), "read_uncommitted"); + WinqTool::winq_equal(&Pragma::recursive_triggers(), "recursive_triggers"); + WinqTool::winq_equal(&Pragma::rekey(), "rekey"); + WinqTool::winq_equal( + &Pragma::reverse_unordered_selects(), + "reverse_unordered_selects", + ); + WinqTool::winq_equal(&Pragma::schema_version(), "schema_version"); + WinqTool::winq_equal(&Pragma::secure_delete(), "secure_delete"); + WinqTool::winq_equal(&Pragma::shrink_memory(), "shrink_memory"); + WinqTool::winq_equal(&Pragma::soft_heap_limit(), "soft_heap_limit"); + WinqTool::winq_equal(&Pragma::stats(), "stats"); + WinqTool::winq_equal(&Pragma::synchronous(), "synchronous"); + WinqTool::winq_equal(&Pragma::table_info(), "table_info"); + WinqTool::winq_equal(&Pragma::temp_store(), "temp_store"); + WinqTool::winq_equal(&Pragma::threads(), "threads"); + WinqTool::winq_equal(&Pragma::user_version(), "user_version"); + WinqTool::winq_equal(&Pragma::vdbe_addoptrace(), "vdbe_addoptrace"); + WinqTool::winq_equal(&Pragma::vdbe_debug(), "vdbe_debug"); + WinqTool::winq_equal(&Pragma::vdbe_listing(), "vdbe_listing"); + WinqTool::winq_equal(&Pragma::vdbe_trace(), "vdbe_trace"); + WinqTool::winq_equal(&Pragma::wal_autocheckpoint(), "wal_autocheckpoint"); + WinqTool::winq_equal(&Pragma::wal_checkpoint(), "wal_checkpoint"); + WinqTool::winq_equal(&Pragma::writable_schema(), "writable_schema"); + } +} diff --git a/src/rust/examples/tests/winq/qualified_table_test.rs b/src/rust/examples/tests/winq/qualified_table_test.rs new file mode 100644 index 000000000..5f8d5e935 --- /dev/null +++ b/src/rust/examples/tests/winq/qualified_table_test.rs @@ -0,0 +1,24 @@ +#[cfg(test)] +pub mod qualified_table_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::qualified_table::QualifiedTable; + + #[test] + pub fn test() { + WinqTool::winq_equal(&QualifiedTable::new("testTable"), "testTable"); + WinqTool::winq_equal( + QualifiedTable::new("testTable") + .of("testSchema") + .as_("testAlias"), + "testSchema.testTable AS testAlias", + ); + WinqTool::winq_equal( + QualifiedTable::new("testTable").indexed("testIndex"), + "testTable INDEXED BY testIndex", + ); + WinqTool::winq_equal( + QualifiedTable::new("testTable").not_indexed(), + "testTable NOT INDEXED", + ); + } +} diff --git a/src/rust/examples/tests/winq/result_column_test.rs b/src/rust/examples/tests/winq/result_column_test.rs new file mode 100644 index 000000000..58d99f80f --- /dev/null +++ b/src/rust/examples/tests/winq/result_column_test.rs @@ -0,0 +1,24 @@ +#[cfg(test)] +pub mod result_column_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::Column; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + use wcdb::winq::result_column::ResultColumn; + + #[test] + pub fn test() { + WinqTool::winq_equal(&ResultColumn::new("testColumn"), "testColumn"); + WinqTool::winq_equal( + &ResultColumn::new(&Column::new("testColumn", None)), + "testColumn", + ); + WinqTool::winq_equal( + ResultColumn::new(&Column::new("testColumn", None)).as_("testColumn2"), + "testColumn AS testColumn2", + ); + WinqTool::winq_equal( + ResultColumn::new(&Column::new("testColumn", None).sum()).as_("sum"), + "SUM(testColumn) AS sum", + ); + } +} diff --git a/src/rust/examples/tests/winq/schema_test.rs b/src/rust/examples/tests/winq/schema_test.rs new file mode 100644 index 000000000..a8a81f24c --- /dev/null +++ b/src/rust/examples/tests/winq/schema_test.rs @@ -0,0 +1,12 @@ +#[cfg(test)] +pub mod schema_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::schema::{Schema, SchemaTrait}; + + #[test] + pub fn test() { + WinqTool::winq_equal(&Schema::main(), "main"); + WinqTool::winq_equal(&Schema::temp(), "temp"); + WinqTool::winq_equal(&Schema::new("testSchema"), "testSchema"); + } +} diff --git a/src/rust/examples/tests/winq/statement_alter_table_test.rs b/src/rust/examples/tests/winq/statement_alter_table_test.rs new file mode 100644 index 000000000..8e4ef03f7 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_alter_table_test.rs @@ -0,0 +1,51 @@ +#[cfg(test)] +pub mod statement_alter_table_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::{Column, ColumnTrait}; + use wcdb::winq::column_type::ColumnType; + use wcdb::winq::statement_alter_table::StatementAlterTable; + + #[test] + pub fn test() { + WinqTool::winq_equal( + StatementAlterTable::new() + .alter_table("table1") + .rename_to("table2"), + "ALTER TABLE table1 RENAME TO table2", + ); + + WinqTool::winq_equal( + StatementAlterTable::new() + .alter_table("table1") + .of("testSchema") + .rename_to("table2"), + "ALTER TABLE testSchema.table1 RENAME TO table2", + ); + + let column_def = Column::new("column1", None).as_def(ColumnType::Float); + WinqTool::winq_equal( + StatementAlterTable::new() + .alter_table("table1") + .add_column(&column_def), + "ALTER TABLE table1 ADD COLUMN column1 REAL", + ); + + WinqTool::winq_equal( + StatementAlterTable::new() + .alter_table("table1") + .rename_column("column1") + .to_column("column2"), + "ALTER TABLE table1 RENAME COLUMN column1 TO column2", + ); + + let column1 = Column::new("column1", None); + let column2 = Column::new("column2", None); + WinqTool::winq_equal( + StatementAlterTable::new() + .alter_table("table1") + .rename_column(&column1) + .to_column(&column2), + "ALTER TABLE table1 RENAME COLUMN column1 TO column2", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_analyze_test.rs b/src/rust/examples/tests/winq/statement_analyze_test.rs new file mode 100644 index 000000000..3f2c85555 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_analyze_test.rs @@ -0,0 +1,32 @@ +#[cfg(test)] +pub mod statement_analyze_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_analyze::StatementAnalyze; + + #[test] + pub fn test() { + WinqTool::winq_equal(StatementAnalyze::new().analyze(), "ANALYZE"); + WinqTool::winq_equal( + StatementAnalyze::new().analyze().schema("testSchema"), + "ANALYZE testSchema", + ); + WinqTool::winq_equal( + StatementAnalyze::new() + .analyze() + .schema("testSchema") + .table("testTable"), + "ANALYZE testSchema.testTable", + ); + WinqTool::winq_equal( + StatementAnalyze::new().analyze().table("testTable"), + "ANALYZE testTable", + ); + WinqTool::winq_equal( + StatementAnalyze::new() + .analyze() + .schema("testSchema") + .index("testIndex"), + "ANALYZE testSchema.testIndex", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_attach_test.rs b/src/rust/examples/tests/winq/statement_attach_test.rs new file mode 100644 index 000000000..b94259d98 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_attach_test.rs @@ -0,0 +1,37 @@ +#[cfg(test)] +pub mod statement_attach_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::bind_parameter::BindParameter; + use wcdb::winq::statement_attach::StatementAttach; + + #[test] + pub fn test() { + WinqTool::winq_equal( + StatementAttach::new().attach("testPath").as_("testSchema"), + "ATTACH 'testPath' AS testSchema", + ); + + WinqTool::winq_equal( + StatementAttach::new() + .attach(&BindParameter::new(1)) + .as_("testSchema"), + "ATTACH ?1 AS testSchema", + ); + + WinqTool::winq_equal( + StatementAttach::new() + .attach("testPath") + .as_("testSchema") + .key("testKey"), + "ATTACH 'testPath' AS testSchema KEY 'testKey'", + ); + + WinqTool::winq_equal( + StatementAttach::new() + .attach(&BindParameter::new(1)) + .as_("testSchema") + .key(&BindParameter::new(2)), + "ATTACH ?1 AS testSchema KEY ?2", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_begin_test.rs b/src/rust/examples/tests/winq/statement_begin_test.rs new file mode 100644 index 000000000..44d0e5a4e --- /dev/null +++ b/src/rust/examples/tests/winq/statement_begin_test.rs @@ -0,0 +1,13 @@ +#[cfg(test)] +pub mod statement_begin_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_begin::StatementBegin; + + #[test] + pub fn test() { + WinqTool::winq_equal(&StatementBegin::new(None), "BEGIN DEFERRED"); + WinqTool::winq_equal(&StatementBegin::begin_deferred(), "BEGIN DEFERRED"); + WinqTool::winq_equal(&StatementBegin::begin_immediate(), "BEGIN IMMEDIATE"); + WinqTool::winq_equal(&StatementBegin::begin_exclusive(), "BEGIN EXCLUSIVE"); + } +} diff --git a/src/rust/examples/tests/winq/statement_commit_test.rs b/src/rust/examples/tests/winq/statement_commit_test.rs new file mode 100644 index 000000000..095f9808d --- /dev/null +++ b/src/rust/examples/tests/winq/statement_commit_test.rs @@ -0,0 +1,10 @@ +#[cfg(test)] +pub mod statement_commit_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_commit::StatementCommit; + + #[test] + pub fn test() { + WinqTool::winq_equal(&StatementCommit::new(), "COMMIT"); + } +} diff --git a/src/rust/examples/tests/winq/statement_create_index_test.rs b/src/rust/examples/tests/winq/statement_create_index_test.rs new file mode 100644 index 000000000..81acc81df --- /dev/null +++ b/src/rust/examples/tests/winq/statement_create_index_test.rs @@ -0,0 +1,76 @@ +#[cfg(test)] +pub mod statement_create_index_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::Column; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + use wcdb::winq::indexed_column::IndexedColumn; + use wcdb::winq::ordering_term::Order; + use wcdb::winq::statement_create_index::StatementCreateIndex; + + #[test] + pub fn test() { + let index1 = IndexedColumn::new("column1"); + let index2 = IndexedColumn::new("column2"); + index2.order(Order::Asc); + let index_name = "index1"; + let table_name = "table1"; + + let mut indexed_column_vec: Vec<&IndexedColumn> = Vec::new(); + indexed_column_vec.push(&index1); + indexed_column_vec.push(&index2); + WinqTool::winq_equal( + StatementCreateIndex::new() + .create_index(index_name) + .on(table_name) + .indexed_by(indexed_column_vec), + "CREATE INDEX index1 ON table1(column1, column2 ASC)", + ); + + let mut indexed_column_vec: Vec<&IndexedColumn> = Vec::new(); + indexed_column_vec.push(&index1); + indexed_column_vec.push(&index2); + WinqTool::winq_equal( + StatementCreateIndex::new() + .create_index(index_name) + .of("testSchema") + .on(table_name) + .indexed_by(indexed_column_vec), + "CREATE INDEX testSchema.index1 ON table1(column1, column2 ASC)", + ); + + let mut indexed_column_vec: Vec<&IndexedColumn> = Vec::new(); + indexed_column_vec.push(&index2); + WinqTool::winq_equal( + StatementCreateIndex::new() + .create_index(index_name) + .unique() + .on(table_name) + .indexed_by(indexed_column_vec), + "CREATE UNIQUE INDEX index1 ON table1(column2 ASC)", + ); + + let mut column_names: Vec = Vec::new(); + column_names.push("newColumn".to_string()); + WinqTool::winq_equal( + StatementCreateIndex::new() + .create_index(index_name) + .if_not_exist() + .on(table_name) + .indexed_by(column_names), + "CREATE INDEX IF NOT EXISTS index1 ON table1(newColumn)", + ); + + let mut column_names: Vec = Vec::new(); + column_names.push("column1".to_string()); + column_names.push("column2".to_string()); + let expression = Column::new("column1", None).ge(1); + WinqTool::winq_equal( + StatementCreateIndex::new() + .create_index(index_name) + .on(table_name) + .indexed_by(column_names) + .where_(&expression), + "CREATE INDEX index1 ON table1(column1, column2) WHERE column1 >= 1", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_create_table_test.rs b/src/rust/examples/tests/winq/statement_create_table_test.rs new file mode 100644 index 000000000..f5692e85e --- /dev/null +++ b/src/rust/examples/tests/winq/statement_create_table_test.rs @@ -0,0 +1,33 @@ +#[cfg(test)] +pub mod statement_create_table_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::{Column, ColumnTrait}; + use wcdb::winq::column_type::ColumnType; + use wcdb::winq::statement_create_table::StatementCreateTable; + use wcdb::winq::table_constraint::TableConstraint; + + #[test] + pub fn test() { + let column1 = Column::new("column1", None); + let column2 = Column::new("column2", None); + + let def1 = column1.as_def(ColumnType::Integer); + let def2 = column2.as_def(ColumnType::Text); + + let constraint1 = TableConstraint::new(Some("constraint1")) + .primary_key() + .indexed_by(&vec![column1]); + let constraint2 = TableConstraint::new(Some("constraint2")) + .unique() + .indexed_by(&vec![column2]); + + let table1 = "table1"; + + WinqTool::winq_equal( + StatementCreateTable::new() + .create_table(table1) + .define(vec![&def1, &def2]), + "CREATE TABLE table1(column1 INTEGER, column2 TEXT)", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_create_trigger_test.rs b/src/rust/examples/tests/winq/statement_create_trigger_test.rs new file mode 100644 index 000000000..a216d54a4 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_create_trigger_test.rs @@ -0,0 +1,220 @@ +#[cfg(test)] +pub mod statement_create_trigger_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::Column; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + use wcdb::winq::object::Object; + use wcdb::winq::statement_create_trigger::StatementCreateTrigger; + use wcdb::winq::statement_delete::StatementDelete; + use wcdb::winq::statement_insert::StatementInsert; + use wcdb::winq::statement_select::StatementSelect; + use wcdb::winq::statement_update::StatementUpdate; + + #[test] + pub fn test() { + let schema = "testSchema"; + let name = "testTrigger"; + let column1 = Column::new("column1", None); + let column2 = Column::new("column2", None); + let table = "testTable"; + let condition = column1.eq(1); + let binding_update = StatementUpdate::new(); + let update = binding_update.update(table).set(vec![&column1]).to(2); + let binding_insert = StatementInsert::new(); + let insert = binding_insert + .insert_into(table) + .values(Some(vec![Object::Int(1)])); + let binding_select = StatementSelect::new(); + let select = binding_select.select(vec![&column1]); + let binding_delete = StatementDelete::new(); + let delete = binding_delete.delete_from(table); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .before() + .delete() + .on_table(table) + .for_each_row() + .when(&condition) + .execute(update), + "CREATE TRIGGER testSchema.testTrigger BEFORE DELETE ON testTable FOR EACH ROW WHEN column1 == 1 BEGIN UPDATE testTable SET column1 = 2; END", + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_temp_trigger(name) + .before().delete() + .on_table(table) + .for_each_row().when(&condition) + .execute(update), + "CREATE TEMP TRIGGER testTrigger BEFORE DELETE ON testTable FOR EACH ROW WHEN column1 == 1 BEGIN UPDATE testTable SET column1 = 2; END"); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .if_not_exist().before().delete() + .on_table(table) + .for_each_row() + .when(&condition).execute(update) + , + "CREATE TRIGGER IF NOT EXISTS testSchema.testTrigger BEFORE DELETE ON testTable FOR EACH ROW WHEN column1 == 1 BEGIN UPDATE testTable SET column1 = 2; END"); + + WinqTool::winq_equal( + StatementCreateTrigger::new().create_trigger(name) + .before() + .delete().on_table(table) + .for_each_row().when(&condition).execute(update), + "CREATE TRIGGER testTrigger BEFORE DELETE ON testTable FOR EACH ROW WHEN column1 == 1 BEGIN UPDATE testTable SET column1 = 2; END" + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .after() + .delete().on_table(table) + .for_each_row().when(&condition).execute(update) + , + "CREATE TRIGGER testSchema.testTrigger AFTER DELETE ON testTable FOR EACH ROW WHEN column1 == 1 BEGIN UPDATE testTable SET column1 = 2; END" + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new().create_trigger(name) + .of_schema(schema) + .instead_of() + .delete() + .on_table(table).for_each_row() + .when(&condition) + .execute(update), + "CREATE TRIGGER testSchema.testTrigger INSTEAD OF DELETE ON testTable FOR EACH ROW WHEN column1 == 1 BEGIN UPDATE testTable SET column1 = 2; END" + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .before() + .insert() + .on_table(table) + .for_each_row() + .when(&condition) + .execute(update), + "CREATE TRIGGER testSchema.testTrigger BEFORE INSERT ON testTable FOR EACH ROW WHEN column1 == 1 BEGIN UPDATE testTable SET column1 = 2; END" + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .before().update().on_table(table) + .for_each_row().when(&condition).execute(update), + "CREATE TRIGGER testSchema.testTrigger BEFORE UPDATE ON testTable FOR EACH ROW WHEN column1 == 1 BEGIN UPDATE testTable SET column1 = 2; END" + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .before().update() + .of_columns(vec![&column1]) + .on_table(table) + .execute(update), + "CREATE TRIGGER testSchema.testTrigger BEFORE UPDATE OF column1 ON testTable BEGIN UPDATE testTable SET column1 = 2; END" + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .before() + .update() + .of_columns(vec![&column1]) + .on_table(table) + .execute(update), + "CREATE TRIGGER testSchema.testTrigger BEFORE UPDATE OF column1 ON testTable BEGIN UPDATE testTable SET column1 = 2; END" + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .before().update() + .of_columns(&vec![column1, column2]).on_table(table) + .execute(update), + "CREATE TRIGGER testSchema.testTrigger BEFORE UPDATE OF column1, column2 ON testTable BEGIN UPDATE testTable SET column1 = 2; END" + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .before().update() + .of_columns(vec![String::from("column1"), String::from("column2")]) + .on_table(table).execute(&binding_update), + "CREATE TRIGGER testSchema.testTrigger BEFORE UPDATE OF column1, column2 ON testTable BEGIN UPDATE testTable SET column1 = 2; END" + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .before() + .delete() + .on_table(table) + .for_each_row() + .when(&condition) + .execute(&binding_update), + "CREATE TRIGGER testSchema.testTrigger BEFORE DELETE ON testTable FOR EACH ROW WHEN column1 == 1 BEGIN UPDATE testTable SET column1 = 2; END", + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .before() + .delete().on_table(table) + .for_each_row() + .when(&condition) + .execute(insert), + "CREATE TRIGGER testSchema.testTrigger BEFORE DELETE ON testTable FOR EACH ROW WHEN column1 == 1 BEGIN INSERT INTO testTable VALUES(1); END" + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .before() + .delete() + .on_table(table) + .for_each_row() + .when(&condition).execute(delete), + "CREATE TRIGGER testSchema.testTrigger BEFORE DELETE ON testTable FOR EACH ROW WHEN column1 == 1 BEGIN DELETE FROM testTable; END" + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .before() + .delete() + .on_table(table) + .for_each_row() + .when(&condition).execute(select), + "CREATE TRIGGER testSchema.testTrigger BEFORE DELETE ON testTable FOR EACH ROW WHEN column1 == 1 BEGIN SELECT column1; END" + ); + + WinqTool::winq_equal( + StatementCreateTrigger::new() + .create_trigger(name) + .of_schema(schema) + .before().delete() + .on_table(table) + .for_each_row() + .when(&condition).execute(update).execute(insert).execute(delete).execute(select), + "CREATE TRIGGER testSchema.testTrigger BEFORE DELETE ON testTable FOR EACH ROW WHEN column1 == 1 BEGIN UPDATE testTable SET column1 = 2; INSERT INTO testTable VALUES(1); DELETE FROM testTable; SELECT column1; END" + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_create_view_test.rs b/src/rust/examples/tests/winq/statement_create_view_test.rs new file mode 100644 index 000000000..735897089 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_create_view_test.rs @@ -0,0 +1,44 @@ +#[cfg(test)] +pub mod statement_create_view_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::Column; + use wcdb::winq::statement_create_view::StatementCreateView; + use wcdb::winq::statement_select::StatementSelect; + + #[test] + pub fn test() { + let column1 = Column::new("column1", None); + let column2 = Column::new("column2", None); + let select = StatementSelect::new(); + select + .select(vec![&column1, &column2]) + .from(vec!["testTable"]); + let view = "testView"; + + WinqTool::winq_equal( + StatementCreateView::new() + .create_view("testView") + .with_columns(vec![&column1, &column2]) + .as_statement_select(&select), + "CREATE VIEW testView(column1, column2) AS SELECT column1, column2 FROM testTable", + ); + + WinqTool::winq_equal( + StatementCreateView::new() + .create_temp_view("testView") + .with_columns(vec![&column1, &column2]) + .as_statement_select(&select), + "CREATE TEMP VIEW testView(column1, column2) AS SELECT column1, column2 FROM testTable", + ); + + WinqTool::winq_equal( + StatementCreateView::new().create_view("testView").of("testSchema").with_columns(vec![&column1, &column2]).as_statement_select(&select), + "CREATE VIEW testSchema.testView(column1, column2) AS SELECT column1, column2 FROM testTable" + ); + + WinqTool::winq_equal( + StatementCreateView::new().create_view("testView").if_not_exist().with_columns(&vec![column1, column2]).as_statement_select(&select), + "CREATE VIEW IF NOT EXISTS testView(column1, column2) AS SELECT column1, column2 FROM testTable" + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_create_virtual_table_test.rs b/src/rust/examples/tests/winq/statement_create_virtual_table_test.rs new file mode 100644 index 000000000..844af71e0 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_create_virtual_table_test.rs @@ -0,0 +1,30 @@ +#[cfg(test)] +pub mod statement_create_virtual_table_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_create_virtual_table::StatementCreateVirtualTable; + + #[test] + pub fn test() { + let virtual_table1 = "virtualTable1"; + let tokenizer = "tokenize=WCDB"; + let virtual_table2 = "virtualTable2"; + let module = "testModule"; + let argument = "left=right"; + WinqTool::winq_equal( + StatementCreateVirtualTable::new() + .create_virtual_table(virtual_table1) + .if_not_exist() + .using_module("fts3") + .arguments(vec![tokenizer.to_string()]), + "CREATE VIRTUAL TABLE IF NOT EXISTS virtualTable1 USING fts3(tokenize=WCDB)", + ); + + WinqTool::winq_equal( + StatementCreateVirtualTable::new() + .create_virtual_table(virtual_table2) + .using_module(module) + .arguments(vec![argument.to_string()]), + "CREATE VIRTUAL TABLE virtualTable2 USING testModule(left=right)", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_delete_test.rs b/src/rust/examples/tests/winq/statement_delete_test.rs new file mode 100644 index 000000000..5d13b47a5 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_delete_test.rs @@ -0,0 +1,43 @@ +#[cfg(test)] +pub mod statement_delete_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::{Column, ColumnTrait}; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + use wcdb::winq::ordering_term::Order; + use wcdb::winq::statement_delete::StatementDelete; + + #[test] + pub fn test() { + let test_table = "testTable"; + let statement = StatementDelete::new(); + + let test = statement.delete_from(test_table); + WinqTool::winq_equal(test, "DELETE FROM testTable"); + + let column1 = Column::new("column1", None); + let test = statement.where_(&column1.gt(100)); + WinqTool::winq_equal(test, "DELETE FROM testTable WHERE column1 > 100"); + + let test = statement.limit(100); + WinqTool::winq_equal(test, "DELETE FROM testTable WHERE column1 > 100 LIMIT 100"); + + let column2 = Column::new("column2", None); + let order1 = column1.order(Order::Asc); + let order2 = column2.order(Order::Desc); + let order_vec = vec![&order1, &order2]; + let test = statement.order_by(order_vec); + WinqTool::winq_equal(test, "DELETE FROM testTable WHERE column1 > 100 ORDER BY column1 ASC, column2 DESC LIMIT 100"); + + let test = statement.offset(100); + WinqTool::winq_equal(test, "DELETE FROM testTable WHERE column1 > 100 ORDER BY column1 ASC, column2 DESC LIMIT 100 OFFSET 100"); + } + + #[test] + pub fn limit_from_to() { + let test_table = "testTable"; + let statement = StatementDelete::new(); + + let test = statement.delete_from(test_table).limit_range(100, 200); + WinqTool::winq_equal(test, "DELETE FROM testTable LIMIT 100, 200"); + } +} diff --git a/src/rust/examples/tests/winq/statement_detach_test.rs b/src/rust/examples/tests/winq/statement_detach_test.rs new file mode 100644 index 000000000..378b48529 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_detach_test.rs @@ -0,0 +1,13 @@ +#[cfg(test)] +pub mod statement_detach_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_detach::StatementDetach; + + #[test] + pub fn test() { + WinqTool::winq_equal( + StatementDetach::new().detach("testSchema"), + "DETACH testSchema", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_drop_index_test.rs b/src/rust/examples/tests/winq/statement_drop_index_test.rs new file mode 100644 index 000000000..ba3dd04f9 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_drop_index_test.rs @@ -0,0 +1,25 @@ +#[cfg(test)] +pub mod statement_drop_index_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_drop_index::StatementDropIndex; + + #[test] + pub fn test() { + WinqTool::winq_equal( + StatementDropIndex::new().drop_index("testIndex"), + "DROP INDEX testIndex", + ); + + WinqTool::winq_equal( + StatementDropIndex::new().drop_index("testIndex").if_exist(), + "DROP INDEX IF EXISTS testIndex", + ); + + WinqTool::winq_equal( + StatementDropIndex::new() + .drop_index("testIndex") + .of("testSchema"), + "DROP INDEX testSchema.testIndex", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_drop_table_test.rs b/src/rust/examples/tests/winq/statement_drop_table_test.rs new file mode 100644 index 000000000..c402fdada --- /dev/null +++ b/src/rust/examples/tests/winq/statement_drop_table_test.rs @@ -0,0 +1,25 @@ +#[cfg(test)] +pub mod statement_drop_table_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_drop_table::StatementDropTable; + + #[test] + pub fn test() { + WinqTool::winq_equal( + StatementDropTable::new().drop_table("testTable"), + "DROP TABLE testTable", + ); + + WinqTool::winq_equal( + StatementDropTable::new().drop_table("testTable").if_exist(), + "DROP TABLE IF EXISTS testTable", + ); + + WinqTool::winq_equal( + StatementDropTable::new() + .drop_table("testTable") + .of("testSchema"), + "DROP TABLE testSchema.testTable", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_drop_trigger_test.rs b/src/rust/examples/tests/winq/statement_drop_trigger_test.rs new file mode 100644 index 000000000..4cad6db49 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_drop_trigger_test.rs @@ -0,0 +1,25 @@ +#[cfg(test)] +pub mod statement_drop_trigger_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_drop_trigger::StatementDropTrigger; + + #[test] + pub fn test() { + WinqTool::winq_equal( + StatementDropTrigger::new().drop_trigger("testTrigger"), + "DROP TRIGGER testTrigger", + ); + WinqTool::winq_equal( + StatementDropTrigger::new() + .drop_trigger("testTrigger") + .if_exist(), + "DROP TRIGGER IF EXISTS testTrigger", + ); + WinqTool::winq_equal( + StatementDropTrigger::new() + .drop_trigger("testTrigger") + .of("testSchema"), + "DROP TRIGGER testSchema.testTrigger", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_drop_view_test.rs b/src/rust/examples/tests/winq/statement_drop_view_test.rs new file mode 100644 index 000000000..9635cd668 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_drop_view_test.rs @@ -0,0 +1,23 @@ +#[cfg(test)] +pub mod statement_drop_view_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_drop_view::StatementDropView; + + #[test] + pub fn test() { + WinqTool::winq_equal( + StatementDropView::new().drop_view("testView"), + "DROP VIEW testView", + ); + WinqTool::winq_equal( + StatementDropView::new().drop_view("testView").if_exist(), + "DROP VIEW IF EXISTS testView", + ); + WinqTool::winq_equal( + StatementDropView::new() + .drop_view("testView") + .of("testSchema"), + "DROP VIEW testSchema.testView", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_explain_test.rs b/src/rust/examples/tests/winq/statement_explain_test.rs new file mode 100644 index 000000000..dc713724b --- /dev/null +++ b/src/rust/examples/tests/winq/statement_explain_test.rs @@ -0,0 +1,21 @@ +#[cfg(test)] +pub mod statement_explain_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_explain::StatementExplain; + use wcdb::winq::statement_select::StatementSelect; + + #[test] + pub fn test() { + let select = StatementSelect::new(); + select.select(vec!["testColumn"]).from(vec!["testTable"]); + WinqTool::winq_equal( + StatementExplain::new().explain(&select), + "EXPLAIN SELECT testColumn FROM testTable", + ); + + WinqTool::winq_equal( + StatementExplain::new().explain_query_plan(&select), + "EXPLAIN QUERY PLAN SELECT testColumn FROM testTable", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_insert_test.rs b/src/rust/examples/tests/winq/statement_insert_test.rs new file mode 100644 index 000000000..017888528 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_insert_test.rs @@ -0,0 +1,101 @@ +#[cfg(test)] +pub mod statement_insert_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::Column; + use wcdb::winq::object::Object; + use wcdb::winq::statement_insert::StatementInsert; + use wcdb::winq::upsert::Upsert; + + #[test] + pub fn test() { + let test_table = "testTable"; + let statement = StatementInsert::new(); + + let test = statement + .insert_into(test_table) + .values_with_bind_parameters(1); + WinqTool::winq_equal(test, "INSERT INTO testTable VALUES(?1)"); + } + + #[test] + pub fn replace() { + let test_table = "testTable"; + let statement = StatementInsert::new(); + + let test = statement + .insert_into(test_table) + .values_with_bind_parameters(1) + .or_replace(); + WinqTool::winq_equal(test, "INSERT OR REPLACE INTO testTable VALUES(?1)"); + + let upsert = Upsert::new(); + upsert.on_conflict().do_no_thing(); + let statement = StatementInsert::new(); + statement + .insert_into("testTable") + .columns(&vec![Column::new("testColumn", None)]) + .values(Some(vec![Object::Int(1)])) + .upsert(&upsert); + WinqTool::winq_equal( + &statement, + "INSERT INTO testTable(testColumn) VALUES(1) ON CONFLICT DO NOTHING", + ); + } + + #[test] + pub fn ignore() { + let test_table = "testTable"; + let statement = StatementInsert::new(); + + let test = statement + .insert_into(test_table) + .values_with_bind_parameters(1) + .or_ignore(); + WinqTool::winq_equal(test, "INSERT OR IGNORE INTO testTable VALUES(?1)"); + } + + #[test] + pub fn fail() { + let test_table = "testTable"; + let statement = StatementInsert::new(); + + let test = statement + .insert_into(test_table) + .values_with_bind_parameters(1) + .or_fail(); + WinqTool::winq_equal(test, "INSERT OR FAIL INTO testTable VALUES(?1)"); + } + + #[test] + pub fn rollback() { + let test_table = "testTable"; + let statement = StatementInsert::new(); + + let test = statement + .insert_into(test_table) + .values_with_bind_parameters(1) + .or_rollback(); + WinqTool::winq_equal(test, "INSERT OR ROLLBACK INTO testTable VALUES(?1)"); + } + + #[test] + pub fn abort() { + let test_table = "testTable"; + let statement = StatementInsert::new(); + + let test = statement + .insert_into(test_table) + .values_with_bind_parameters(1) + .or_abort(); + WinqTool::winq_equal(test, "INSERT OR ABORT INTO testTable VALUES(?1)"); + } + + #[test] + pub fn default_values() { + let test_table = "testTable"; + let statement = StatementInsert::new(); + + let test = statement.insert_into(test_table).default_values(); + WinqTool::winq_equal(test, "INSERT INTO testTable DEFAULT VALUES"); + } +} diff --git a/src/rust/examples/tests/winq/statement_pragma_test.rs b/src/rust/examples/tests/winq/statement_pragma_test.rs new file mode 100644 index 000000000..0f10ec47d --- /dev/null +++ b/src/rust/examples/tests/winq/statement_pragma_test.rs @@ -0,0 +1,19 @@ +#[cfg(test)] +pub mod statement_pragma_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::pragma::Pragma; + use wcdb::winq::statement_pragma::StatementPragma; + + #[test] + pub fn test() { + let pragma = Pragma::new("page_size"); + let statement = StatementPragma::new(); + + let test = statement.pragma(pragma); + WinqTool::winq_equal(test, "PRAGMA page_size"); + + let pragma = Pragma::new("secureDelete"); + let test = statement.pragma(pragma).to_value(1); + WinqTool::winq_equal(test, "PRAGMA secureDelete = 1"); + } +} diff --git a/src/rust/examples/tests/winq/statement_reindex_test.rs b/src/rust/examples/tests/winq/statement_reindex_test.rs new file mode 100644 index 000000000..342bd4d9c --- /dev/null +++ b/src/rust/examples/tests/winq/statement_reindex_test.rs @@ -0,0 +1,27 @@ +#[cfg(test)] +pub mod statement_detach_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_reindex::StatementReindex; + + #[test] + pub fn test() { + WinqTool::winq_equal( + StatementReindex::new().reindex_table("testTable"), + "REINDEX testTable", + ); + WinqTool::winq_equal( + StatementReindex::new() + .reindex_table("testTable") + .of("testSchema"), + "REINDEX testSchema.testTable", + ); + WinqTool::winq_equal( + StatementReindex::new().reindex("testIndex"), + "REINDEX testIndex", + ); + WinqTool::winq_equal( + StatementReindex::new().reindex_collation("testCollation"), + "REINDEX testCollation", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_release_test.rs b/src/rust/examples/tests/winq/statement_release_test.rs new file mode 100644 index 000000000..bb0408a8d --- /dev/null +++ b/src/rust/examples/tests/winq/statement_release_test.rs @@ -0,0 +1,13 @@ +#[cfg(test)] +pub mod statement_release_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_release::StatementRelease; + + #[test] + pub fn test() { + WinqTool::winq_equal( + StatementRelease::new().release("testSavepoint"), + "RELEASE testSavepoint", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_rollback_test.rs b/src/rust/examples/tests/winq/statement_rollback_test.rs new file mode 100644 index 000000000..a31b32312 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_rollback_test.rs @@ -0,0 +1,14 @@ +#[cfg(test)] +pub mod statement_rollback_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_rollback::StatementRollback; + + #[test] + pub fn test() { + WinqTool::winq_equal(&StatementRollback::new(), "ROLLBACK"); + WinqTool::winq_equal( + StatementRollback::new().rollback_to("testSavepoint"), + "ROLLBACK TO testSavepoint", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_select_test.rs b/src/rust/examples/tests/winq/statement_select_test.rs new file mode 100644 index 000000000..cedb0de0a --- /dev/null +++ b/src/rust/examples/tests/winq/statement_select_test.rs @@ -0,0 +1,49 @@ +#[cfg(test)] +pub mod statement_select_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::{Column, ColumnTrait}; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + use wcdb::winq::ordering_term::Order; + use wcdb::winq::statement_select::StatementSelect; + + #[test] + pub fn test() { + let test_table = "testTable"; + let statement = StatementSelect::new(); + + let column = Column::new("column1", None); + let test = statement.from(vec![test_table]).select(vec![&column]); + WinqTool::winq_equal(test, "SELECT column1 FROM testTable"); + + let expression = column.gt(100); + let test = statement.where_(&expression); + WinqTool::winq_equal(test, "SELECT column1 FROM testTable WHERE column1 > 100"); + + let test = statement.limit(100); + WinqTool::winq_equal( + test, + "SELECT column1 FROM testTable WHERE column1 > 100 LIMIT 100", + ); + + let column2 = Column::new("column2", None); + let order_term = column2.order(Order::Desc); + let order_vec = vec![&order_term]; + let test = statement.order_by(order_vec); + WinqTool::winq_equal( + test, + "SELECT column1 FROM testTable WHERE column1 > 100 ORDER BY column2 DESC LIMIT 100", + ); + + let test = statement.offset(100); + WinqTool::winq_equal( + test, + "SELECT column1 FROM testTable WHERE column1 > 100 ORDER BY column2 DESC LIMIT 100 OFFSET 100", + ); + + let test = statement.group_by(vec!["column3"]); + WinqTool::winq_equal( + test, + "SELECT column1 FROM testTable WHERE column1 > 100 GROUP BY column3 ORDER BY column2 DESC LIMIT 100 OFFSET 100", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_update_test.rs b/src/rust/examples/tests/winq/statement_update_test.rs new file mode 100644 index 000000000..f1cb4886c --- /dev/null +++ b/src/rust/examples/tests/winq/statement_update_test.rs @@ -0,0 +1,132 @@ +#[cfg(test)] +pub mod statement_update_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::{Column, ColumnTrait}; + use wcdb::winq::expression_convertible::ExpressionConvertibleTrait; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + use wcdb::winq::ordering_term::Order; + use wcdb::winq::statement_update::StatementUpdate; + + #[test] + pub fn test() { + let column1 = Column::new("column1", None); + let column2 = Column::new("column2", None); + let test_table_str = "testTable"; + let column_vec = vec![Column::new("column1", None), Column::new("column2", None)]; + let column1_vec = vec![column1]; + let column2_vec = vec![column2]; + + WinqTool::winq_equal( + StatementUpdate::new() + .update(test_table_str) + .set(&column1_vec) + .to(1), + "UPDATE testTable SET column1 = 1", + ); + + WinqTool::winq_equal( + StatementUpdate::new() + .update(test_table_str) + .set(&column1_vec) + .to(true), + "UPDATE testTable SET column1 = TRUE", + ); + + WinqTool::winq_equal( + StatementUpdate::new() + .update(test_table_str) + .set(&column1_vec) + .to("abc".to_string()), + "UPDATE testTable SET column1 = 'abc'", + ); + + WinqTool::winq_equal( + StatementUpdate::new() + .update(test_table_str) + .set(&column1_vec) + .to(1.1), + "UPDATE testTable SET column1 = 1.1000000000000001", + ); + + WinqTool::winq_equal( + StatementUpdate::new() + .update(test_table_str) + .set(&column1_vec) + .to::>(None), + "UPDATE testTable SET column1 = NULL", + ); + + WinqTool::winq_equal( + StatementUpdate::new() + .update(test_table_str) + .or_replace() + .set(&column1_vec) + .to(1) + .set(&column2_vec) + .to("abc".to_string()), + "UPDATE OR REPLACE testTable SET column1 = 1, column2 = 'abc'", + ); + + WinqTool::winq_equal( + StatementUpdate::new() + .update(test_table_str) + .set_columns_to_bind_parameters(&column_vec), + "UPDATE testTable SET column1 = ?1, column2 = ?2", + ); + + // todo dengxudong 重要不紧急 + // winqEqual(new StatementUpdate().update("testTable").setColumnsToValues(new Column[]{column1, column2}, new Object[]{1, "abc"}), + // "UPDATE testTable SET column1 = 1, column2 = 'abc'"); + // winqEqual(new StatementUpdate().update("testTable").setColumnsToValues(new Column[]{column1, column2}, new Value[]{new Value(1), new Value("abc")}), + // "UPDATE testTable SET column1 = 1, column2 = 'abc'"); + + WinqTool::winq_equal( + StatementUpdate::new() + .update(test_table_str) + .set(&column1_vec) + .to(1) + .where_(&Column::new("column1", None).gt(1)), + "UPDATE testTable SET column1 = 1 WHERE column1 > 1", + ); + + WinqTool::winq_equal( + StatementUpdate::new() + .update(test_table_str) + .set(&column1_vec) + .to(1) + .order_by(vec![ + &Column::new("column1", None).order(Order::Asc), + &Column::new("column2", None).order(Order::Desc), + ]), + "UPDATE testTable SET column1 = 1 ORDER BY column1 ASC, column2 DESC", + ); + + WinqTool::winq_equal( + StatementUpdate::new() + .update(test_table_str) + .set(&column1_vec) + .to(1) + .limit(1), + "UPDATE testTable SET column1 = 1 LIMIT 1", + ); + + WinqTool::winq_equal( + StatementUpdate::new() + .update(test_table_str) + .set(&column1_vec) + .to(1) + .limit_range(1, 2), + "UPDATE testTable SET column1 = 1 LIMIT 1, 2", + ); + + WinqTool::winq_equal( + StatementUpdate::new() + .update(test_table_str) + .set(&column1_vec) + .to(1) + .limit(1) + .offset(3), + "UPDATE testTable SET column1 = 1 LIMIT 1 OFFSET 3", + ); + } +} diff --git a/src/rust/examples/tests/winq/statement_vacuum_test.rs b/src/rust/examples/tests/winq/statement_vacuum_test.rs new file mode 100644 index 000000000..fd21ee8b8 --- /dev/null +++ b/src/rust/examples/tests/winq/statement_vacuum_test.rs @@ -0,0 +1,14 @@ +#[cfg(test)] +pub mod statement_vacuum_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::statement_vacuum::StatementVacuum; + + #[test] + pub fn test() { + WinqTool::winq_equal(&StatementVacuum::new(), "VACUUM"); + WinqTool::winq_equal( + StatementVacuum::new().vacuum("testSchema"), + "VACUUM testSchema", + ); + } +} diff --git a/src/rust/examples/tests/winq/upsert_test_case.rs b/src/rust/examples/tests/winq/upsert_test_case.rs new file mode 100644 index 000000000..c538d1bfb --- /dev/null +++ b/src/rust/examples/tests/winq/upsert_test_case.rs @@ -0,0 +1,85 @@ +#[cfg(test)] +pub mod upsert_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::Column; + use wcdb::winq::expression_convertible::ExpressionConvertibleTrait; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + use wcdb::winq::upsert::Upsert; + + #[test] + pub fn test() { + WinqTool::winq_equal( + Upsert::new().on_conflict().do_no_thing(), + "ON CONFLICT DO NOTHING", + ); + WinqTool::winq_equal( + Upsert::new() + .on_conflict() + .indexed_by(vec![&Column::new("column1", None)]) + .do_no_thing(), + "ON CONFLICT(column1) DO NOTHING", + ); + WinqTool::winq_equal( + Upsert::new() + .on_conflict() + .indexed_by(vec![&Column::new("column1", None)]) + .where_(&Column::new("column1", None).eq(1)) + .do_no_thing(), + "ON CONFLICT(column1) WHERE column1 == 1 DO NOTHING", + ); + WinqTool::winq_equal( + Upsert::new() + .on_conflict() + .do_update() + .set(vec![&Column::new("column1", None)]) + .to::>(None), + "ON CONFLICT DO UPDATE SET column1 = NULL", + ); + WinqTool::winq_equal( + Upsert::new() + .on_conflict() + .do_update() + .set(vec![&Column::new("column1", None)]) + .to(true), + "ON CONFLICT DO UPDATE SET column1 = TRUE", + ); + WinqTool::winq_equal( + Upsert::new() + .on_conflict() + .do_update() + .set(vec![&Column::new("column1", None)]) + .to(1), + "ON CONFLICT DO UPDATE SET column1 = 1", + ); + WinqTool::winq_equal( + Upsert::new() + .on_conflict() + .do_update() + .set(vec![&Column::new("column1", None)]) + .to("abc"), + "ON CONFLICT DO UPDATE SET column1 = 'abc'", + ); + WinqTool::winq_equal( + Upsert::new() + .on_conflict() + .do_update() + .set(vec![&Column::new("column1", None)]) + .to(1) + .set(vec![ + &Column::new("column2", None), + &Column::new("column3", None), + ]) + .to(2), + "ON CONFLICT DO UPDATE SET column1 = 1, (column2, column3) = 2", + ); + WinqTool::winq_equal( + Upsert::new() + .on_conflict() + .do_update() + .set(vec![&Column::new("column1", None)]) + .to(1) + .where_(&Column::new("column1", None).eq(2)), + "ON CONFLICT DO UPDATE SET column1 = 1 WHERE column1 == 2", + ); + } +} diff --git a/src/rust/examples/tests/winq/window_def_test.rs b/src/rust/examples/tests/winq/window_def_test.rs new file mode 100644 index 000000000..9dd77535e --- /dev/null +++ b/src/rust/examples/tests/winq/window_def_test.rs @@ -0,0 +1,59 @@ +#[cfg(test)] +pub mod window_def_test { + use crate::base::winq_tool::WinqTool; + use wcdb::winq::column::{Column, ColumnTrait}; + use wcdb::winq::expression::Expression; + use wcdb::winq::expression_operable::ExpressionOperableTrait; + use wcdb::winq::frame_spec::FrameSpec; + use wcdb::winq::ordering_term::{Order, OrderingTerm}; + use wcdb::winq::window_def::WindowDef; + + #[test] + pub fn test() { + WinqTool::winq_equal(&WindowDef::new(), ""); + + let window_def = WindowDef::new(); + window_def.partition_by(vec!["column1"]); + WinqTool::winq_equal(&window_def, "(PARTITION BY column1)"); + + let window_def = WindowDef::new(); + window_def.partition_by(vec!["column1", "column2"]); + WinqTool::winq_equal(&window_def, "(PARTITION BY column1, column2)"); + + let column1 = Column::new("column1", None).add(1); + let window_def = WindowDef::new(); + window_def.partition_by(vec![&column1]); + WinqTool::winq_equal(&window_def, "(PARTITION BY column1 + 1)"); + + let column1 = Column::new("column1", None).add(1); + let column2 = Column::new("column2", None); + let expression = Expression::new(&column2); + let window_def = WindowDef::new(); + window_def.partition_by(vec![&column1, &expression]); + WinqTool::winq_equal(&window_def, "(PARTITION BY column1 + 1, column2)"); + + let ordering_term: OrderingTerm = Column::new("column1", None).order(Order::Asc); + let window_def = WindowDef::new(); + window_def.order_by(&vec![&ordering_term]); + WinqTool::winq_equal(&window_def, "(ORDER BY column1 ASC)"); + + let window_def = WindowDef::new(); + window_def.frame_spec(FrameSpec::new().range().unbounded_preceding()); + WinqTool::winq_equal(&window_def, "(RANGE UNBOUNDED PRECEDING)"); + + let column1 = Column::new("column1", None).add(1); + column1.add(1); + let column2 = Column::new("column2", None); + let expression = Expression::new(&column2); + let ordering_term: OrderingTerm = Column::new("column1", None).order(Order::Asc); + let window_def = WindowDef::new(); + window_def + .partition_by(vec![&column1, &expression]) + .order_by(&vec![&ordering_term]) + .frame_spec(&FrameSpec::new().range().unbounded_preceding()); + WinqTool::winq_equal( + &window_def, + "(PARTITION BY column1 + 1, column2 ORDER BY column1 ASC RANGE UNBOUNDED PRECEDING)", + ); + } +} diff --git a/src/rust/rustfmt.toml b/src/rust/rustfmt.toml new file mode 100644 index 000000000..758d4179d --- /dev/null +++ b/src/rust/rustfmt.toml @@ -0,0 +1 @@ +max_width = 100 diff --git a/src/rust/wcdb/Cargo.toml b/src/rust/wcdb/Cargo.toml new file mode 100644 index 000000000..25574d5de --- /dev/null +++ b/src/rust/wcdb/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "wcdb" +version = "0.1.0" +edition = "2021" + +[dependencies] +lazy_static = "1.5.0" +num-derive = "0.4" +num-traits = "0.2" +libc = "1.0.0-alpha.1" + +[build-dependencies] +num_cpus = "1.16.0" +cmake = "0.1.52" diff --git a/src/rust/wcdb/build.rs b/src/rust/wcdb/build.rs new file mode 100644 index 000000000..c85761c44 --- /dev/null +++ b/src/rust/wcdb/build.rs @@ -0,0 +1,232 @@ +use std::env; +use std::path::PathBuf; +use std::process::{Command, Output}; + +fn main() { + run_cmd("git submodule update --init openssl sqlcipher zstd"); + + let target = env::var("TARGET").unwrap(); + let dst = config_cmake(&target); + + println!("cargo:rerun-if-changed=cpp"); + println!("cargo:rustc-link-lib=static=sqlcipher"); + println!("cargo:rustc-link-lib=static=zstd"); + println!("cargo:rustc-link-lib=z"); + if target.contains("apple") { + println!("cargo:rustc-link-lib=c++"); + println!("cargo:rustc-link-lib=framework=CoreFoundation"); + println!("cargo:rustc-link-lib=framework=Security"); + println!( + "cargo:rustc-link-search=native={}/build/wcdb/", + dst.display() + ); + println!( + "cargo:rustc-link-search=framework={}/build/wcdb/", + dst.display() + ); + println!("cargo:rustc-link-lib=static=WCDB"); + } else if target.contains("ohos") || target.contains("android") || target.contains("linux") { + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let openssl_search_path = openssl_search_path_from_target(&target) + .expect(&format!("wcdb not support target: {}", target)); + let openssl_path = manifest_dir.join(openssl_search_path); + println!("cargo:rustc-link-lib=stdc++"); + println!("cargo:rustc-link-search=native={}", openssl_path.display()); + println!("cargo:rustc-link-lib=static=crypto"); + println!( + "cargo:rustc-link-search=native={}/build/wcdb/", + dst.display() + ); + println!("cargo:rustc-link-lib=static=WCDB"); + } else if target.contains("windows") { + // 根据工具链选择不同的 C++ 标准库 + if target.contains("msvc") { + // MSVC 工具链 + println!("cargo:rustc-link-lib=dylib=user32"); + println!("cargo:rustc-link-lib=dylib=advapi32"); + println!("cargo:rustc-link-lib=dylib=crypt32"); + + // 静态链接 C++ 标准库 (可选) + // println!("cargo:rustc-link-lib=static=libcmt"); + } else { + // GNU (MinGW) 工具链 + println!("cargo:rustc-link-lib=dylib=stdc++"); + println!("cargo:rustc-link-lib=dylib=gcc"); + println!("cargo:rustc-link-lib=dylib=winpthread"); + } + + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let openssl_search_path = openssl_search_path_from_target(&target) + .expect(&format!("wcdb not support target: {}", target)); + let openssl_path = manifest_dir.join(openssl_search_path); + println!("cargo:rustc-link-search=native={}", openssl_path.display()); + println!("cargo:rustc-link-lib=static=libcrypto"); + // Windows 平台配置 + println!( + "cargo:rustc-link-search=native={}/build/wcdb/", + dst.display() + ); + println!( + "cargo:rustc-link-search=native={}/build/wcdb/Debug", + dst.display() + ); + println!( + "cargo:rustc-link-search=native={}/build/wcdb/Release", + dst.display() + ); + println!("cargo:rustc-link-lib=static=WCDB"); + } +} + +fn config_cmake(target: &str) -> PathBuf { + let mut cmake = cmake::Config::new("../cpp"); + cmake.very_verbose(true); + + let (cc, cxx) = get_compiler_config(target); + cmake + .define("CMAKE_ASM_COMPILER", &cc) // 指定 asm 编译器:zstd huf_decompress_amd64.S 是汇编 + .define("CMAKE_ASM_FLAGS", "-x assembler-with-cpp") + .define("CMAKE_C_COMPILER", &cc) // 指定 C 编译器:openssl 主要是 C + .define("CMAKE_CXX_COMPILER", &cxx); // 指定 C++ 编译器:WCDB 主要是 C++ + + if target.contains("apple") { + let (system_name, sdk_name, arch_name) = ios_sdk_arch_from_target(target) + .expect(&format!("wcdb not support target: {}", target)); + + let output = run_cmd(&format!("xcrun --sdk {} --show-sdk-path", sdk_name)); + let sysroot = String::from_utf8(output.stdout).unwrap().replace("\n", ""); + + cmake + .define("CMAKE_SYSTEM_NAME", system_name) + .define("CMAKE_OSX_SYSROOT", &sysroot) + .define("CMAKE_SYSROOT", &sysroot) + .define("CMAKE_OSX_ARCHITECTURES", arch_name); + // support ios 9.0 + if target.contains("ios") { + cmake.define("CMAKE_OSX_DEPLOYMENT_TARGET", "9.0"); + } else if target.contains("darwin") { + cmake.define("CMAKE_OSX_DEPLOYMENT_TARGET", "10.15"); + } + } else if target.contains("android") { + let toolchain_file = env::var("CMAKE_TOOLCHAIN_FILE").expect(&format!( + "wcdb: {} is not set CMAKE_TOOLCHAIN_FILE", + &target + )); + cmake.define("CMAKE_TOOLCHAIN_FILE", toolchain_file); + + let abi = + android_abi_from_target(target).expect(&format!("wcdb: {} can not find abi", &target)); + cmake.define("ANDROID_ABI", abi); + + // ✅ 设置平台版本(例如 android-21) + cmake.define("ANDROID_PLATFORM", "android-21"); + } else if target.contains("windows") { + // cmake.generator("Visual Studio 17 2022"); + let abi = + windows_abi_from_target(target).expect(&format!("wcdb: {} can not find abi", &target)); + cmake.define("CMAKE_GENERATOR_PLATFORM", abi); + cmake + .define("WIN32", "ON") + .define("CMAKE_MSVC_RUNTIME_LIBRARY", "MultiThreaded") // MT/MTd/MD/MDd + .profile("Release"); // ← 明确设置为 Release + } + + cmake + .define("CMAKE_CXX_FLAGS", "-D_Nullable= -D_Nonnull=") + .define("CMAKE_C_FLAGS", "-D_Nullable= -D_Nonnull=") + .define("CMAKE_BUILD_TYPE", "Release") + .define("BUILD_FROM_CARGO", "ON"); + + if target.contains("windows") { + cmake.build_target("ALL_BUILD"); + } else { + cmake.build_arg(format!("-j{}", num_cpus::get())); + cmake.build_target("all"); + } + + cmake.build() +} + +fn get_compiler_config(target: &str) -> (String, String) { + let (default_cc, default_cxx) = match target { + t if t.contains("apple") => ("/usr/bin/clang", "/usr/bin/clang++"), + t if t.contains("windows") => ("cl.exe", "cl.exe"), + _ => { + return ( + env::var("CC").expect(&format!("wcdb: {} is not set CC", target)), + env::var("CXX").expect(&format!("wcdb: {} is not set CXX", target)), + ) + } + }; + + let cc = env::var("CC").unwrap_or_else(|_| default_cc.to_string()); + let cxx = env::var("CXX").unwrap_or_else(|_| default_cxx.to_string()); + + (cc, cxx) +} + +fn ios_sdk_arch_from_target(target: &str) -> Option<(&'static str, &'static str, &'static str)> { + match target { + // ios + "aarch64-apple-ios" => Some(("iOS", "iphoneos", "arm64")), + "aarch64-apple-ios-sim" => Some(("iOS", "iphonesimulator", "arm64")), + "x86_64-apple-ios" => Some(("iOS", "iphonesimulator", "x86_64")), + // mac + "aarch64-apple-darwin" => Some(("Darwin", "macosx", "arm64")), + "x86_64-apple-darwin" => Some(("Darwin", "macosx", "x86_64")), + _ => None, + } +} + +fn windows_abi_from_target(target: &str) -> Option<&'static str> { + match target { + // msvc + "x86_64-pc-windows-msvc" => Some("x64"), + "i686-pc-windows-msvc" => Some("Win32"), + // gnu + // "x86_64-pc-windows-gnu" => Some("x64"), + // "i686-pc-windows-gnu" => Some("Win32"), + _ => None, + } +} + +fn android_abi_from_target(target: &str) -> Option<&'static str> { + match target { + "aarch64-linux-android" => Some("arm64-v8a"), + "armv7-linux-androideabi" => Some("armeabi-v7a"), + "i686-linux-android" => Some("x86"), + "x86_64-linux-android" => Some("x86_64"), + _ => None, + } +} + +fn openssl_search_path_from_target(target: &str) -> Option<&'static str> { + match target { + // android + "aarch64-linux-android" => Some("../../../tools/prebuild/openssl/android/arm64-v8a"), + "armv7-linux-androideabi" => Some("../../../tools/prebuild/openssl/android/armeabi-v7a"), + "i686-linux-android" => Some("../../../tools/prebuild/openssl/android/x86"), + "x86_64-linux-android" => Some("../../../tools/prebuild/openssl/android/x86_64"), + // ohos + "aarch64-unknown-linux-ohos" => Some("../../../tools/prebuild/openssl/ohos/arm64-v8a"), + "x86_64-unknown-linux-ohos" => Some("../../../tools/prebuild/openssl/ohos/x86_64"), + // linux + "aarch64-unknown-linux-gnu" => Some("../../../tools/prebuild/openssl/linux/arm64"), + "x86_64-unknown-linux-gnu" => Some("../../../tools/prebuild/openssl/linux/x86_64"), + "loongarch64-unknown-linux-gnu" => { + Some("../../../tools/prebuild/openssl/linux/loongarch64") + } + // windows + "x86_64-pc-windows-msvc" => Some("..\\..\\..\\tools\\prebuild\\openssl\\windows\\win64"), + "i686-pc-windows-msvc" => Some("..\\..\\..\\tools\\prebuild\\openssl\\windows\\win32"), + _ => None, + } +} + +fn run_cmd(cmd: &str) -> Output { + Command::new("sh") + .arg("-c") + .arg(cmd) + .output() + .expect(&format!("wcdb : failed to execute command :{}", cmd)) +} diff --git a/src/rust/wcdb/src/base/cpp_object.rs b/src/rust/wcdb/src/base/cpp_object.rs new file mode 100644 index 000000000..2ff9436ce --- /dev/null +++ b/src/rust/wcdb/src/base/cpp_object.rs @@ -0,0 +1,82 @@ +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use std::ffi::c_void; +use std::ops::{Deref, DerefMut}; + +extern "C" { + pub(crate) fn WCDBRustBase_releaseObject(cpp_obj: *mut c_void); +} + +#[derive(Debug)] +pub struct CppObject { + pub(crate) cpp_obj: *mut c_void, +} + +impl Deref for CppObject { + type Target = *mut c_void; + + fn deref(&self) -> &Self::Target { + &self.cpp_obj + } +} + +impl DerefMut for CppObject { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cpp_obj + } +} + +impl Drop for CppObject { + fn drop(&mut self) { + let c_obj = self.cpp_obj; + self.cpp_obj = std::ptr::null_mut(); + if c_obj != std::ptr::null_mut() { + unsafe { WCDBRustBase_releaseObject(c_obj) }; + } + } +} + +unsafe impl Send for CppObject {} + +unsafe impl Sync for CppObject {} + +/// 供“继承类”直接操作 cpp_obj +pub trait CppObjectTrait: CppObjectConvertibleTrait { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void); + fn get_cpp_obj(&self) -> *mut c_void; + fn release_cpp_object(&mut self); +} + +impl CppObjectConvertibleTrait for CppObject { + fn as_cpp_object(&self) -> &CppObject { + self + } +} + +impl CppObjectTrait for CppObject { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.cpp_obj = cpp_obj; + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.cpp_obj + } + + fn release_cpp_object(&mut self) { + let c_obj = self.cpp_obj; + self.cpp_obj = std::ptr::null_mut(); + if c_obj != std::ptr::null_mut() { + unsafe { WCDBRustBase_releaseObject(c_obj) }; + } + } +} + +impl CppObject { + pub fn new(cpp_obj_opt: Option<*mut c_void>) -> Self { + let cpp_obj = cpp_obj_opt.unwrap_or_else(|| std::ptr::null_mut()); + CppObject { cpp_obj } + } + + pub fn get(obj: &T) -> *mut c_void { + obj.as_cpp_object().cpp_obj + } +} diff --git a/src/rust/wcdb/src/base/cpp_object_convertible.rs b/src/rust/wcdb/src/base/cpp_object_convertible.rs new file mode 100644 index 000000000..98d7449b9 --- /dev/null +++ b/src/rust/wcdb/src/base/cpp_object_convertible.rs @@ -0,0 +1,5 @@ +use crate::base::cpp_object::CppObject; + +pub trait CppObjectConvertibleTrait { + fn as_cpp_object(&self) -> &CppObject; +} diff --git a/src/rust/wcdb/src/base/mod.rs b/src/rust/wcdb/src/base/mod.rs new file mode 100644 index 000000000..d50c1d89e --- /dev/null +++ b/src/rust/wcdb/src/base/mod.rs @@ -0,0 +1,5 @@ +pub mod cpp_object; +pub mod cpp_object_convertible; +pub mod param; +pub mod value; +pub mod wcdb_exception; diff --git a/src/rust/wcdb/src/base/param/enum_basic_expression.rs b/src/rust/wcdb/src/base/param/enum_basic_expression.rs new file mode 100644 index 000000000..47ee2adc3 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_basic_expression.rs @@ -0,0 +1,167 @@ +use crate::base::cpp_object::CppObject; +use crate::utils::ToCString; +use crate::winq::column::Column; +use crate::winq::expression::Expression; +use crate::winq::expression_convertible::ExpressionConvertibleTrait; +use crate::winq::expression_operable::ExpressionOperable; +use crate::winq::identifier::{CPPType, Identifier}; +use crate::winq::literal_value::LiteralValue; +use libc::c_longlong; +use std::ffi::{c_double, c_void, CString}; + +/// support: +/// +/// ```text +/// bool, i8, i16, i32, i64, f32, f64, String, &str +/// &ExpressionConvertibleTrait Option<&ExpressionConvertibleTrait> +/// &ExpressionOperable Option<&ExpressionOperable> +/// &Expression Option<&Expression> +/// &Column Option<&Column> +/// &LiteralValue Option<&LiteralValue> +/// ``` +pub enum BasicExpression<'a> { + Bool(bool), + Int(i64), + Float(f64), + String(String), + ExpressionConvertible(Option<&'a dyn ExpressionConvertibleTrait>), +} + +impl BasicExpression<'_> { + pub(crate) fn get_params(self) -> (CPPType, c_longlong, c_double, Option) { + match self { + BasicExpression::Bool(value) => { + let value = if value { 1 } else { 0 }; + (CPPType::Bool, value as c_longlong, 0 as c_double, None) + } + BasicExpression::Int(value) => (CPPType::Int, value as c_longlong, 0 as c_double, None), + BasicExpression::Float(value) => { + (CPPType::Double, 0 as c_longlong, value as c_double, None) + } + BasicExpression::String(value) => { + let cstr = value.as_str().to_cstring(); + (CPPType::String, 0 as c_longlong, 0 as c_double, Some(cstr)) + } + BasicExpression::ExpressionConvertible(obj_opt) => { + let (cpp_type, cpp_obj) = match obj_opt { + None => (CPPType::Null, 0 as *mut c_void), + Some(obj) => (Identifier::get_cpp_type(obj), CppObject::get(obj)), + }; + (cpp_type, cpp_obj as c_longlong, 0 as c_double, None) + } + } + } +} + +impl<'a> From for BasicExpression<'a> { + fn from(value: bool) -> Self { + BasicExpression::Bool(value) + } +} + +/// 宏定义:为所有整数类型实现 From trait +macro_rules! impl_from_int_types { + ($($int_type:ty),* $(,)?) => { + $( + impl<'a> From<$int_type> for BasicExpression<'a> { + fn from(value: $int_type) -> Self { + BasicExpression::Int(value as i64) + } + } + )* + }; +} + +/// 使用宏为所有整数类型实现 From trait +impl_from_int_types!(i8, i16, i32, i64); + +/// 宏定义:为所有浮点类型实现 From trait +macro_rules! impl_from_float_types { + ($($float_type:ty),* $(,)?) => { + $( + impl<'a> From<$float_type> for BasicExpression<'a> { + fn from(value: $float_type) -> Self { + BasicExpression::Float(value as f64) + } + } + )* + }; +} + +/// 使用宏为所有浮点类型实现 From trait +impl_from_float_types!(f32, f64); + +impl<'a> From for BasicExpression<'a> { + fn from(value: String) -> Self { + BasicExpression::String(value) + } +} + +impl<'a> From<&'a str> for BasicExpression<'a> { + fn from(value: &'a str) -> Self { + BasicExpression::String(value.to_string()) + } +} + +impl<'a> From> for BasicExpression<'a> { + fn from(value: Option<&'a dyn ExpressionConvertibleTrait>) -> Self { + BasicExpression::ExpressionConvertible(value) + } +} + +impl<'a> From<&'a dyn ExpressionConvertibleTrait> for BasicExpression<'a> { + fn from(value: &'a dyn ExpressionConvertibleTrait) -> Self { + BasicExpression::ExpressionConvertible(Some(value)) + } +} + +impl<'a> From> for BasicExpression<'a> { + fn from(value: Option<&'a ExpressionOperable>) -> Self { + let v = value.map(|x| x as &dyn ExpressionConvertibleTrait).into(); + BasicExpression::ExpressionConvertible(v) + } +} + +impl<'a> From<&'a ExpressionOperable> for BasicExpression<'a> { + fn from(value: &'a ExpressionOperable) -> Self { + let v = value as &dyn ExpressionConvertibleTrait; + BasicExpression::ExpressionConvertible(Some(v)) + } +} + +impl<'a> From> for BasicExpression<'a> { + fn from(value: Option<&'a Expression>) -> Self { + let v = value.map(|x| x as &dyn ExpressionConvertibleTrait).into(); + BasicExpression::ExpressionConvertible(v) + } +} + +impl<'a> From<&'a Expression> for BasicExpression<'a> { + fn from(value: &'a Expression) -> Self { + BasicExpression::ExpressionConvertible(Some(value)) + } +} + +impl<'a> From> for BasicExpression<'a> { + fn from(value: Option<&'a Column>) -> Self { + value.map(|x| x as &dyn ExpressionConvertibleTrait).into() + } +} + +impl<'a> From<&'a Column> for BasicExpression<'a> { + fn from(value: &'a Column) -> Self { + BasicExpression::ExpressionConvertible(Some(value)) + } +} + +impl<'a> From> for BasicExpression<'a> { + fn from(value: Option<&'a LiteralValue>) -> Self { + value.map(|x| x as &dyn ExpressionConvertibleTrait).into() + } +} + +impl<'a> From<&'a LiteralValue> for BasicExpression<'a> { + fn from(value: &'a LiteralValue) -> Self { + BasicExpression::ExpressionConvertible(Some(value)) + } +} diff --git a/src/rust/wcdb/src/base/param/enum_expression_ref.rs b/src/rust/wcdb/src/base/param/enum_expression_ref.rs new file mode 100644 index 000000000..3a73469e4 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_expression_ref.rs @@ -0,0 +1,66 @@ +use crate::winq::column::Column; +use crate::winq::expression::Expression; +use crate::winq::expression_convertible::ExpressionConvertibleTrait; +use crate::winq::expression_operable::ExpressionOperable; + +/// support: +/// ```text +/// &ExpressionConvertibleTrait Option<&ExpressionConvertibleTrait> +/// &ExpressionOperable Option<&ExpressionOperable> +/// &Expression Option<&Expression> +/// &Column Option<&Column> +/// ``` +pub enum ExpressionRef<'a> { + ExpressionConvertible(Option<&'a dyn ExpressionConvertibleTrait>), +} + +impl<'a> From> for ExpressionRef<'a> { + fn from(value: Option<&'a dyn ExpressionConvertibleTrait>) -> Self { + ExpressionRef::ExpressionConvertible(value) + } +} + +impl<'a> From<&'a dyn ExpressionConvertibleTrait> for ExpressionRef<'a> { + fn from(value: &'a dyn ExpressionConvertibleTrait) -> Self { + ExpressionRef::ExpressionConvertible(Some(value)) + } +} + +impl<'a> From> for ExpressionRef<'a> { + fn from(value: Option<&'a ExpressionOperable>) -> Self { + let v = value.map(|x| x as &dyn ExpressionConvertibleTrait).into(); + ExpressionRef::ExpressionConvertible(v) + } +} + +impl<'a> From<&'a ExpressionOperable> for ExpressionRef<'a> { + fn from(value: &'a ExpressionOperable) -> Self { + let v = value as &dyn ExpressionConvertibleTrait; + ExpressionRef::ExpressionConvertible(Some(v)) + } +} + +impl<'a> From> for ExpressionRef<'a> { + fn from(value: Option<&'a Expression>) -> Self { + let v = value.map(|x| x as &dyn ExpressionConvertibleTrait).into(); + ExpressionRef::ExpressionConvertible(v) + } +} + +impl<'a> From<&'a Expression> for ExpressionRef<'a> { + fn from(value: &'a Expression) -> Self { + ExpressionRef::ExpressionConvertible(Some(value)) + } +} + +impl<'a> From> for ExpressionRef<'a> { + fn from(value: Option<&'a Column>) -> Self { + value.map(|x| x as &dyn ExpressionConvertibleTrait).into() + } +} + +impl<'a> From<&'a Column> for ExpressionRef<'a> { + fn from(value: &'a Column) -> Self { + ExpressionRef::ExpressionConvertible(Some(value)) + } +} diff --git a/src/rust/wcdb/src/base/param/enum_int_expression.rs b/src/rust/wcdb/src/base/param/enum_int_expression.rs new file mode 100644 index 000000000..9ca4f0999 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_int_expression.rs @@ -0,0 +1,57 @@ +use crate::base::cpp_object::CppObject; +use crate::winq::expression_convertible::ExpressionConvertibleTrait; +use crate::winq::identifier::{CPPType, Identifier}; +use std::ffi::c_longlong; + +/// support: +/// ```text +/// i8, i16, i32, i64 +/// &ExpressionConvertibleTrait Option<&ExpressionConvertibleTrait> +/// ``` +pub enum IntExpression<'a> { + Int(i64), + ExpressionConvertible(Option<&'a dyn ExpressionConvertibleTrait>), +} + +impl IntExpression<'_> { + pub(crate) fn get_params(self) -> (CPPType, c_longlong) { + match self { + IntExpression::Int(value) => (CPPType::Int, value), + IntExpression::ExpressionConvertible(value_opt) => match value_opt { + None => (CPPType::Null, 0), + Some(value) => ( + Identifier::get_cpp_type(value), + CppObject::get(value) as c_longlong, + ), + }, + } + } +} + +/// 宏定义:为所有整数类型实现 From trait +macro_rules! impl_from_int_types { + ($($int_type:ty),* $(,)?) => { + $( + impl<'a> From<$int_type> for IntExpression<'a> { + fn from(value: $int_type) -> Self { + IntExpression::Int(value as i64) + } + } + )* + }; +} + +/// 使用宏为所有整数类型实现 From trait +impl_from_int_types!(i8, i16, i32, i64); + +impl<'a> From> for IntExpression<'a> { + fn from(value: Option<&'a dyn ExpressionConvertibleTrait>) -> Self { + IntExpression::ExpressionConvertible(value) + } +} + +impl<'a> From<&'a dyn ExpressionConvertibleTrait> for IntExpression<'a> { + fn from(value: &'a dyn ExpressionConvertibleTrait) -> Self { + IntExpression::ExpressionConvertible(Some(value)) + } +} diff --git a/src/rust/wcdb/src/base/param/enum_string_bind_parameter.rs b/src/rust/wcdb/src/base/param/enum_string_bind_parameter.rs new file mode 100644 index 000000000..9fe522612 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_string_bind_parameter.rs @@ -0,0 +1,46 @@ +use crate::base::cpp_object::CppObject; +use crate::utils::ToCString; +use crate::winq::bind_parameter::BindParameter; +use crate::winq::identifier::{CPPType, Identifier}; +use std::ffi::{c_void, CString}; + +/// support: +/// ```text +/// String, &str +/// &BindParameter +/// ``` +pub enum StringBindParameter<'a> { + String(String), + BindParameter(&'a BindParameter), +} + +impl StringBindParameter<'_> { + pub(crate) fn get_params(self) -> (CPPType, *mut c_void, Option) { + match self { + StringBindParameter::String(value) => { + (CPPType::String, 0 as *mut c_void, Some(value.to_cstring())) + } + StringBindParameter::BindParameter(value) => { + (Identifier::get_cpp_type(value), CppObject::get(value), None) + } + } + } +} + +impl<'a> From for StringBindParameter<'a> { + fn from(value: String) -> Self { + StringBindParameter::String(value) + } +} + +impl<'a> From<&str> for StringBindParameter<'a> { + fn from(value: &str) -> Self { + StringBindParameter::String(value.to_string()) + } +} + +impl<'a> From<&'a BindParameter> for StringBindParameter<'a> { + fn from(value: &'a BindParameter) -> Self { + StringBindParameter::BindParameter(value) + } +} diff --git a/src/rust/wcdb/src/base/param/enum_string_column.rs b/src/rust/wcdb/src/base/param/enum_string_column.rs new file mode 100644 index 000000000..7adf39b16 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_string_column.rs @@ -0,0 +1,49 @@ +use crate::base::cpp_object::CppObject; +use crate::utils::ToCString; +use crate::winq::column::ColumnTrait; +use crate::winq::identifier::{CPPType, Identifier}; +use std::ffi::{c_void, CString}; + +/// support: +/// ```text +/// String, &str +/// &ColumnTrait +/// ``` +pub enum StringColumn<'a> { + String(String), + Column(&'a dyn ColumnTrait), +} + +impl StringColumn<'_> { + pub(crate) fn get_params(self) -> (CPPType, *mut c_void, Option) { + match self { + StringColumn::String(value) => { + let cstr = value.as_str().to_cstring(); + (CPPType::String, 0 as *mut c_void, Some(cstr)) + } + StringColumn::Column(column) => ( + Identifier::get_cpp_type(column), + CppObject::get(column), + None, + ), + } + } +} + +impl<'a> From for StringColumn<'a> { + fn from(value: String) -> Self { + StringColumn::String(value) + } +} + +impl<'a> From<&str> for StringColumn<'a> { + fn from(value: &str) -> Self { + StringColumn::String(value.to_string()) + } +} + +impl<'a, T: ColumnTrait> From<&'a T> for StringColumn<'a> { + fn from(value: &'a T) -> Self { + StringColumn::Column(value) + } +} diff --git a/src/rust/wcdb/src/base/param/enum_string_column_def.rs b/src/rust/wcdb/src/base/param/enum_string_column_def.rs new file mode 100644 index 000000000..f86e12735 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_string_column_def.rs @@ -0,0 +1,62 @@ +use crate::orm::field::Field; +use crate::winq::column::Column; +use crate::winq::column_type::ColumnType; + +pub enum StringColumnDef<'a> { + String(&'a str, Option), + Column(&'a Column, Option), +} + +impl<'a> From<&'a str> for StringColumnDef<'a> { + fn from(name: &'a str) -> Self { + StringColumnDef::String(name, None) + } +} + +impl<'a> From<(&'a str, ColumnType)> for StringColumnDef<'a> { + fn from((name, ty): (&'a str, ColumnType)) -> Self { + StringColumnDef::String(name, Some(ty)) + } +} + +impl<'a> From<(&'a str, Option)> for StringColumnDef<'a> { + fn from((name, ty_opt): (&'a str, Option)) -> Self { + StringColumnDef::String(name, ty_opt) + } +} + +impl<'a> From<&'a Column> for StringColumnDef<'a> { + fn from(col: &'a Column) -> Self { + StringColumnDef::Column(col, None) + } +} + +impl<'a> From<(&'a Column, ColumnType)> for StringColumnDef<'a> { + fn from((col, ty): (&'a Column, ColumnType)) -> Self { + StringColumnDef::Column(col, Some(ty)) + } +} + +impl<'a> From<(&'a Column, Option)> for StringColumnDef<'a> { + fn from((col, ty_opt): (&'a Column, Option)) -> Self { + StringColumnDef::Column(col, ty_opt) + } +} + +impl<'a, T> From<&'a Field> for StringColumnDef<'a> { + fn from(field: &'a Field) -> Self { + StringColumnDef::Column(field.get_column(), None) + } +} + +impl<'a, T> From<(&'a Field, ColumnType)> for StringColumnDef<'a> { + fn from((field, ty): (&'a Field, ColumnType)) -> Self { + StringColumnDef::Column(field.get_column(), Some(ty)) + } +} + +impl<'a, T> From<(&'a Field, Option)> for StringColumnDef<'a> { + fn from((field, ty_opt): (&'a Field, Option)) -> Self { + StringColumnDef::Column(field.get_column(), ty_opt) + } +} diff --git a/src/rust/wcdb/src/base/param/enum_string_expression.rs b/src/rust/wcdb/src/base/param/enum_string_expression.rs new file mode 100644 index 000000000..1781f1a50 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_string_expression.rs @@ -0,0 +1,96 @@ +use crate::base::cpp_object::CppObject; +use crate::utils::ToCString; +use crate::winq::column::Column; +use crate::winq::expression::Expression; +use crate::winq::expression_convertible::ExpressionConvertibleTrait; +use crate::winq::expression_operable::ExpressionOperable; +use crate::winq::identifier::{CPPType, Identifier}; +use std::ffi::{c_void, CString}; + +/// support: +/// ```text +/// String, &str +/// &ExpressionConvertibleTrait Option<&ExpressionConvertibleTrait> +/// &Column Option<&Column> +/// ``` +pub enum StringExpression<'a> { + String(String), + ExpressionConvertible(Option<&'a dyn ExpressionConvertibleTrait>), +} + +impl StringExpression<'_> { + pub(crate) fn get_params(self) -> (CPPType, *mut c_void, Option) { + match self { + StringExpression::String(str) => { + (CPPType::String, 0 as *mut c_void, Some(str.to_cstring())) + } + StringExpression::ExpressionConvertible(exp_opt) => match exp_opt { + None => (CPPType::Null, 0 as *mut c_void, None), + Some(exp) => (Identifier::get_cpp_type(exp), CppObject::get(exp), None), + }, + } + } +} + +impl<'a> From for StringExpression<'a> { + fn from(value: String) -> Self { + StringExpression::String(value) + } +} + +impl<'a> From<&str> for StringExpression<'a> { + fn from(value: &str) -> Self { + StringExpression::String(value.to_string()) + } +} + +impl<'a> From> for StringExpression<'a> { + fn from(value: Option<&'a dyn ExpressionConvertibleTrait>) -> Self { + StringExpression::ExpressionConvertible(value) + } +} + +impl<'a> From<&'a dyn ExpressionConvertibleTrait> for StringExpression<'a> { + fn from(value: &'a dyn ExpressionConvertibleTrait) -> Self { + StringExpression::ExpressionConvertible(Some(value)) + } +} + +impl<'a> From> for StringExpression<'a> { + fn from(value: Option<&'a ExpressionOperable>) -> Self { + let v = value.map(|x| x as &dyn ExpressionConvertibleTrait).into(); + StringExpression::ExpressionConvertible(v) + } +} + +impl<'a> From<&'a ExpressionOperable> for StringExpression<'a> { + fn from(value: &'a ExpressionOperable) -> Self { + let v = value as &dyn ExpressionConvertibleTrait; + StringExpression::ExpressionConvertible(Some(v)) + } +} + +impl<'a> From> for StringExpression<'a> { + fn from(value: Option<&'a Expression>) -> Self { + let v = value.map(|x| x as &dyn ExpressionConvertibleTrait).into(); + StringExpression::ExpressionConvertible(v) + } +} + +impl<'a> From<&'a Expression> for StringExpression<'a> { + fn from(value: &'a Expression) -> Self { + StringExpression::ExpressionConvertible(Some(value)) + } +} + +impl<'a> From> for StringExpression<'a> { + fn from(value: Option<&'a Column>) -> Self { + value.map(|x| x as &dyn ExpressionConvertibleTrait).into() + } +} + +impl<'a> From<&'a Column> for StringExpression<'a> { + fn from(value: &'a Column) -> Self { + StringExpression::ExpressionConvertible(Some(value)) + } +} diff --git a/src/rust/wcdb/src/base/param/enum_string_indexed_column.rs b/src/rust/wcdb/src/base/param/enum_string_indexed_column.rs new file mode 100644 index 000000000..209532d08 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_string_indexed_column.rs @@ -0,0 +1,47 @@ +use crate::base::cpp_object::CppObject; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier}; +use crate::winq::indexed_column_convertible::IndexedColumnConvertibleTrait; +use std::ffi::{c_void, CString}; + +/// support: +/// ```text +/// String, &str +/// &IndexedColumnConvertibleTrait +/// ``` +pub enum StringIndexedColumn<'a> { + String(String), + IndexedColumnConvertible(&'a dyn IndexedColumnConvertibleTrait), +} + +impl StringIndexedColumn<'_> { + pub(crate) fn get_params(self) -> (CPPType, *mut c_void, Option) { + match self { + StringIndexedColumn::String(value) => { + let cstr = value.as_str().to_cstring(); + (CPPType::String, 0 as *mut c_void, Some(cstr)) + } + StringIndexedColumn::IndexedColumnConvertible(value) => { + (Identifier::get_cpp_type(value), CppObject::get(value), None) + } + } + } +} + +impl<'a> From for StringIndexedColumn<'a> { + fn from(value: String) -> Self { + StringIndexedColumn::String(value) + } +} + +impl<'a> From<&'a str> for StringIndexedColumn<'a> { + fn from(value: &'a str) -> Self { + StringIndexedColumn::String(value.to_string()) + } +} + +impl<'a, T: IndexedColumnConvertibleTrait> From<&'a T> for StringIndexedColumn<'a> { + fn from(value: &'a T) -> Self { + StringIndexedColumn::IndexedColumnConvertible(value) + } +} diff --git a/src/rust/wcdb/src/base/param/enum_string_qualified_table.rs b/src/rust/wcdb/src/base/param/enum_string_qualified_table.rs new file mode 100644 index 000000000..a146d8c24 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_string_qualified_table.rs @@ -0,0 +1,29 @@ +use crate::winq::qualified_table::QualifiedTable; + +/// support: +/// ```text +/// String, &str +/// &QualifiedTable +/// ``` +pub enum StringQualifiedTable<'a> { + String(String), + QualifiedTable(&'a QualifiedTable), +} + +impl<'a> From for StringQualifiedTable<'a> { + fn from(value: String) -> Self { + StringQualifiedTable::String(value) + } +} + +impl<'a> From<&str> for StringQualifiedTable<'a> { + fn from(value: &str) -> Self { + StringQualifiedTable::String(value.to_string()) + } +} + +impl<'a> From<&'a QualifiedTable> for StringQualifiedTable<'a> { + fn from(value: &'a QualifiedTable) -> Self { + StringQualifiedTable::QualifiedTable(value) + } +} diff --git a/src/rust/wcdb/src/base/param/enum_string_result_column.rs b/src/rust/wcdb/src/base/param/enum_string_result_column.rs new file mode 100644 index 000000000..ac4f8bb01 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_string_result_column.rs @@ -0,0 +1,29 @@ +use crate::winq::result_column_convertible_trait::ResultColumnConvertibleTrait; + +/// support: +/// ```text +/// String, &str +/// &ResultColumnConvertibleTrait +/// ``` +pub enum StringResultColumn<'a> { + String(String), + ResultColumn(&'a dyn ResultColumnConvertibleTrait), +} + +impl<'a> From for StringResultColumn<'a> { + fn from(value: String) -> Self { + StringResultColumn::String(value) + } +} + +impl<'a> From<&str> for StringResultColumn<'a> { + fn from(value: &str) -> Self { + StringResultColumn::String(value.to_string()) + } +} + +impl<'a, T: ResultColumnConvertibleTrait> From<&'a T> for StringResultColumn<'a> { + fn from(value: &'a T) -> Self { + StringResultColumn::ResultColumn(value) + } +} diff --git a/src/rust/wcdb/src/base/param/enum_string_schema.rs b/src/rust/wcdb/src/base/param/enum_string_schema.rs new file mode 100644 index 000000000..b64a67865 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_string_schema.rs @@ -0,0 +1,53 @@ +use crate::base::cpp_object::CppObject; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier}; +use crate::winq::schema::Schema; +use std::ffi::{c_void, CString}; + +/// support: +/// ```text +/// String, &str +/// &Schema Option<&Schema> +/// ``` +pub enum StringSchema<'a> { + String(String), + Schema(Option<&'a Schema>), +} + +impl StringSchema<'_> { + pub(crate) fn get_params(self) -> (CPPType, *mut c_void, Option) { + match self { + StringSchema::String(str) => { + (CPPType::String, 0 as *mut c_void, Some(str.to_cstring())) + } + StringSchema::Schema(schema_opt) => match schema_opt { + None => (CPPType::Null, 0 as *mut c_void, None), + Some(sc) => (Identifier::get_cpp_type(sc), CppObject::get(sc), None), + }, + } + } +} + +impl<'a> From for StringSchema<'a> { + fn from(value: String) -> Self { + StringSchema::String(value) + } +} + +impl<'a> From<&str> for StringSchema<'a> { + fn from(value: &str) -> Self { + StringSchema::String(value.to_string()) + } +} + +impl<'a> From> for StringSchema<'a> { + fn from(value: Option<&'a Schema>) -> Self { + StringSchema::Schema(value) + } +} + +impl<'a> From<&'a Schema> for StringSchema<'a> { + fn from(value: &'a Schema) -> Self { + StringSchema::Schema(Some(value)) + } +} diff --git a/src/rust/wcdb/src/base/param/enum_string_table_or_subquery.rs b/src/rust/wcdb/src/base/param/enum_string_table_or_subquery.rs new file mode 100644 index 000000000..980195d38 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_string_table_or_subquery.rs @@ -0,0 +1,29 @@ +use crate::winq::table_or_subquery_convertible_trait::TableOrSubqueryConvertibleTrait; + +/// support +/// ```text +/// String, &str +/// &TableOrSubqueryConvertibleTrait +/// ``` +pub enum StringTableOrSubquery<'a> { + String(String), + TableOrSubquery(&'a dyn TableOrSubqueryConvertibleTrait), +} + +impl<'a> From for StringTableOrSubquery<'a> { + fn from(value: String) -> Self { + StringTableOrSubquery::String(value) + } +} + +impl<'a> From<&str> for StringTableOrSubquery<'a> { + fn from(value: &str) -> Self { + StringTableOrSubquery::String(value.to_string()) + } +} + +impl<'a, T: TableOrSubqueryConvertibleTrait + 'a> From<&'a T> for StringTableOrSubquery<'a> { + fn from(value: &'a T) -> Self { + StringTableOrSubquery::TableOrSubquery(value) + } +} diff --git a/src/rust/wcdb/src/base/param/enum_string_window_def.rs b/src/rust/wcdb/src/base/param/enum_string_window_def.rs new file mode 100644 index 000000000..4b62a9d13 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_string_window_def.rs @@ -0,0 +1,29 @@ +use crate::winq::window_def::WindowDef; + +/// support: +/// ```text +/// String, &str +/// &WindowDef +/// ``` +pub enum StringWindowDef<'a> { + String(String), + WindowDef(&'a WindowDef), +} + +impl<'a> From for StringWindowDef<'a> { + fn from(value: String) -> Self { + StringWindowDef::String(value) + } +} + +impl<'a> From<&str> for StringWindowDef<'a> { + fn from(value: &str) -> Self { + StringWindowDef::String(value.to_string()) + } +} + +impl<'a> From<&'a WindowDef> for StringWindowDef<'a> { + fn from(value: &'a WindowDef) -> Self { + StringWindowDef::WindowDef(value) + } +} diff --git a/src/rust/wcdb/src/base/param/enum_trigger_statement.rs b/src/rust/wcdb/src/base/param/enum_trigger_statement.rs new file mode 100644 index 000000000..bd81a9594 --- /dev/null +++ b/src/rust/wcdb/src/base/param/enum_trigger_statement.rs @@ -0,0 +1,35 @@ +use crate::winq::statement_delete::StatementDelete; +use crate::winq::statement_insert::StatementInsert; +use crate::winq::statement_select::StatementSelect; +use crate::winq::statement_update::StatementUpdate; + +pub enum TriggerStatement<'a> { + Insert(&'a StatementInsert), + Delete(&'a StatementDelete), + Update(&'a StatementUpdate), + Select(&'a StatementSelect), +} + +impl<'a> From<&'a StatementInsert> for TriggerStatement<'a> { + fn from(s: &'a StatementInsert) -> Self { + TriggerStatement::Insert(s) + } +} + +impl<'a> From<&'a StatementDelete> for TriggerStatement<'a> { + fn from(s: &'a StatementDelete) -> Self { + TriggerStatement::Delete(s) + } +} + +impl<'a> From<&'a StatementUpdate> for TriggerStatement<'a> { + fn from(s: &'a StatementUpdate) -> Self { + TriggerStatement::Update(s) + } +} + +impl<'a> From<&'a StatementSelect> for TriggerStatement<'a> { + fn from(s: &'a StatementSelect) -> Self { + TriggerStatement::Select(s) + } +} diff --git a/src/rust/wcdb/src/base/param/mod.rs b/src/rust/wcdb/src/base/param/mod.rs new file mode 100644 index 000000000..41b6152ce --- /dev/null +++ b/src/rust/wcdb/src/base/param/mod.rs @@ -0,0 +1,14 @@ +pub mod enum_basic_expression; +pub mod enum_expression_ref; +pub mod enum_int_expression; +pub mod enum_string_bind_parameter; +pub mod enum_string_column; +pub mod enum_string_column_def; +pub mod enum_string_expression; +pub mod enum_string_indexed_column; +pub mod enum_string_qualified_table; +pub mod enum_string_result_column; +pub mod enum_string_schema; +pub mod enum_string_table_or_subquery; +pub mod enum_string_window_def; +pub mod enum_trigger_statement; diff --git a/src/rust/wcdb/src/base/value.rs b/src/rust/wcdb/src/base/value.rs new file mode 100644 index 000000000..1d67ebfcd --- /dev/null +++ b/src/rust/wcdb/src/base/value.rs @@ -0,0 +1,160 @@ +use crate::winq::column_type::ColumnType; +use std::str::from_utf8; + +#[derive(Clone, Debug)] +pub enum ValueObject { + None, + Int(i64), + Double(f64), + String(String), + BLOB(Vec), + // todo qixinbing 处理 struct Value +} + +impl From for ValueObject { + fn from(value: bool) -> Self { + ValueObject::Int(if value { 1 } else { 0 }) + } +} + +impl From for ValueObject { + fn from(value: i8) -> Self { + ValueObject::Int(value as i64) + } +} + +impl From for ValueObject { + fn from(value: i16) -> Self { + ValueObject::Int(value as i64) + } +} + +impl From for ValueObject { + fn from(value: i32) -> Self { + ValueObject::Int(value as i64) + } +} + +impl From for ValueObject { + fn from(value: i64) -> Self { + ValueObject::Int(value) + } +} + +impl From for ValueObject { + fn from(value: f32) -> Self { + ValueObject::Double(value as f64) + } +} + +impl From for ValueObject { + fn from(value: f64) -> Self { + ValueObject::Double(value) + } +} + +impl From<&str> for ValueObject { + fn from(value: &str) -> Self { + ValueObject::String(value.to_string()) + } +} + +impl From for ValueObject { + fn from(value: String) -> Self { + ValueObject::String(value) + } +} + +impl From> for ValueObject { + fn from(value: Vec) -> Self { + ValueObject::BLOB(value) + } +} + +#[derive(Debug, Clone)] +pub struct Value { + value: ValueObject, +} + +impl Value { + // todo qixinbing 是否支持 None? + pub fn default() -> Self { + Value { + value: ValueObject::None, + } + } + pub fn new(value: T) -> Self + where + T: Into, + { + Value { + value: value.into(), + } + } + + pub fn get_type(&self) -> ColumnType { + match &self.value { + ValueObject::None => ColumnType::Null, + ValueObject::Int(_) => ColumnType::Integer, + ValueObject::Double(_) => ColumnType::Float, + ValueObject::String(_) => ColumnType::Text, + ValueObject::BLOB(_) => ColumnType::BLOB, + } + } + + pub fn get_bool(&self) -> bool { + self.get_i64() != 0 + } + + pub fn get_i8(&self) -> i8 { + self.get_i64() as i8 + } + + pub fn get_i16(&self) -> i16 { + self.get_i64() as i16 + } + + pub fn get_i32(&self) -> i32 { + self.get_i64() as i32 + } + + pub fn get_i64(&self) -> i64 { + match &self.value { + ValueObject::Int(val) => *val, + ValueObject::Double(val) => (*val).round() as i64, + ValueObject::String(val) => val.parse::().unwrap_or(0), + _ => 0, + } + } + + pub fn get_f32(&self) -> f32 { + self.get_f64() as f32 + } + + pub fn get_f64(&self) -> f64 { + match &self.value { + ValueObject::Double(val) => *val, + ValueObject::Int(val) => (*val) as f64, + ValueObject::String(val) => val.parse::().unwrap_or(0.0), + _ => 0.0, + } + } + + pub fn get_text(&self) -> String { + match &self.value { + ValueObject::String(val) => val.to_string(), + ValueObject::BLOB(val) => from_utf8(&val).unwrap_or_default().to_string(), + _ => "".to_string(), + } + } + + pub fn get_blob(&self) -> Vec { + match &self.value { + ValueObject::BLOB(val) => val.clone(), + _ => { + let string = self.get_text(); + string.as_bytes().to_vec() + } + } + } +} diff --git a/src/rust/wcdb/src/base/wcdb_exception.rs b/src/rust/wcdb/src/base/wcdb_exception.rs new file mode 100644 index 000000000..ad6d1c604 --- /dev/null +++ b/src/rust/wcdb/src/base/wcdb_exception.rs @@ -0,0 +1,434 @@ +use crate::utils::ToCow; +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; +use std::collections::HashMap; +use std::ffi::{c_char, c_void}; +use std::fmt::{Debug, Display, Formatter}; +use std::ops::Deref; + +extern "C" { + fn WCDBRustError_getLevel(cpp_obj: *mut c_void) -> i32; + fn WCDBRustError_getCode(cpp_obj: *mut c_void) -> i32; + fn WCDBRustError_getMessage(cpp_obj: *mut c_void) -> *const c_char; + fn WCDBRustError_enumerateInfo(map: *mut c_void, cpp_obj: *mut c_void); +} + +#[no_mangle] +pub extern "C" fn WCDBExceptionAddInfo( + key_values_raw: *mut c_void, + key: *const c_char, + value_type: i32, + int_value: i64, + double_value: f64, + string_value: *const c_char, +) { + let value = match value_type { + 3 => ExceptionObject::Long(int_value), + 5 => ExceptionObject::Double(double_value), + 6 => ExceptionObject::String(string_value.to_cow().to_string()), + _ => return, + }; + let key_values: &mut HashMap = + unsafe { &mut *(key_values_raw as *mut HashMap) }; + key_values.insert(key.to_cow().to_string(), value); +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ExceptionLevel { + Ignore, + Debug, + Notice, + Warning, + Error, + Fatal, + Unknown, +} + +impl ExceptionLevel { + pub fn value_of(value: i32) -> Self { + match value { + 1 => ExceptionLevel::Ignore, + 2 => ExceptionLevel::Debug, + 3 => ExceptionLevel::Notice, + 4 => ExceptionLevel::Warning, + 5 => ExceptionLevel::Error, + 6 => ExceptionLevel::Fatal, + _ => ExceptionLevel::Unknown, + } + } + + pub fn to_str(&self) -> &'static str { + match self { + ExceptionLevel::Ignore => "IGNORE", + ExceptionLevel::Debug => "DEBUG", + ExceptionLevel::Notice => "NOTICE", + ExceptionLevel::Warning => "WARNING", + ExceptionLevel::Error => "ERROR", + ExceptionLevel::Fatal => "FATAL", + ExceptionLevel::Unknown => "UNKNOWN", + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ExceptionCode { + OK = 0, + Error = 1, + Internal = 2, + Permission = 3, + Abort = 4, + Busy = 5, + Locked = 6, + NoMemory = 7, + Readonly = 8, + Interrupt = 9, + IOError = 10, + Corrupt = 11, + NotFound = 12, + Full = 13, + CantOpen = 14, + Protocol = 15, + Empty = 16, + Schema = 17, + Exceed = 18, + Constraint = 19, + Mismatch = 20, + Misuse = 21, + NoLargeFileSupport = 22, + Authorization = 23, + Format = 24, + Range = 25, + NotADatabase = 26, + Notice = 27, + Warning = 28, + Row = 100, + Done = 101, + Unknown = -1, +} + +impl ExceptionCode { + pub fn value_of(value: i32) -> Self { + match value { + 0 => ExceptionCode::OK, + 1 => ExceptionCode::Error, + 2 => ExceptionCode::Internal, + 3 => ExceptionCode::Permission, + 4 => ExceptionCode::Abort, + 5 => ExceptionCode::Busy, + 6 => ExceptionCode::Locked, + 7 => ExceptionCode::NoMemory, + 8 => ExceptionCode::Readonly, + 9 => ExceptionCode::Interrupt, + 10 => ExceptionCode::IOError, + 11 => ExceptionCode::Corrupt, + 12 => ExceptionCode::NotFound, + 13 => ExceptionCode::Full, + 14 => ExceptionCode::CantOpen, + 15 => ExceptionCode::Protocol, + 16 => ExceptionCode::Empty, + 17 => ExceptionCode::Schema, + 18 => ExceptionCode::Exceed, + 19 => ExceptionCode::Constraint, + 20 => ExceptionCode::Mismatch, + 21 => ExceptionCode::Misuse, + 22 => ExceptionCode::NoLargeFileSupport, + 23 => ExceptionCode::Authorization, + 24 => ExceptionCode::Format, + 25 => ExceptionCode::Range, + 26 => ExceptionCode::NotADatabase, + 27 => ExceptionCode::Notice, + 28 => ExceptionCode::Warning, + 100 => ExceptionCode::Row, + 101 => ExceptionCode::Done, + _ => ExceptionCode::Unknown, + } + } +} + +#[derive(Debug, PartialEq, FromPrimitive)] +pub enum ExceptionExtendCode { + ErrorMissingCollseq = 257, // CodeError | (1 << 8) + ErrorRetry = 513, // Code.Error | (2 << 8) + ErrorSnapshot = 769, // Code.Error | (3 << 8) + IOErrorRead = 266, // Code.IOError | (1 << 8) + IOErrorShortRead = 522, // Code.IOError | (2 << 8) + IOErrorWrite = 778, // Code.IOError | (3 << 8) + IOErrorFsync = 1034, // Code.IOError | (4 << 8) + IOErrorDirFsync = 1290, // Code.IOError | (5 << 8) + IOErrorTruncate = 1546, // Code.IOError | (6 << 8) + IOErrorFstat = 1802, // Code.IOError | (7 << 8) + IOErrorUnlock = 2058, // Code.IOError | (8 << 8) + IOErrorRdlock = 2314, // Code.IOError | (9 << 8) + IOErrorDelete = 2570, // Code.IOError | (10 << 8) + IOErrorBlocked = 2826, // Code.IOError | (11 << 8) + IOErrorNoMemory = 3082, // Code.IOError | (12 << 8) + IOErrorAccess = 3338, // Code.IOError | (13 << 8) + IOErrorCheckReservedLock = 3594, // Code.IOError | (14 << 8) + IOErrorLock = 3850, // Code.IOError | (15 << 8) + IOErrorClose = 4106, // Code.IOError | (16 << 8) + IOErrorDirClose = 4362, // Code.IOError | (17 << 8) + IOErrorShmOpen = 4618, // Code.IOError | (18 << 8) + IOErrorShmSize = 4874, // Code.IOError | (19 << 8) + IOErrorShmLock = 5130, // Code.IOError | (20 << 8) + IOErrorShmMap = 5386, // Code.IOError | (21 << 8) + IOErrorSeek = 5642, // Code.IOError | (22 << 8) + IOErrorDeleteNoEntry = 5898, // Code.IOError | (23 << 8) + IOErrorMmap = 6154, // Code.IOError | (24 << 8) + IOErrorGetTempPath = 6410, // Code.IOError | (25 << 8) + IOErrorConvPath = 6666, // Code.IOError | (26 << 8) + IOErrorVnode = 6922, // Code.IOError | (27 << 8) + IOErrorAuthorization = 7178, // Code.IOError | (28 << 8) + IOErrorBeginAtomic = 7434, // Code.IOError | (29 << 8) + IOErrorCommitAtomic = 7690, // Code.IOError | (30 << 8) + IOErrorRollbackAtomic = 7946, // Code.IOError | (31 << 8) + LockedSharedCache = 262, // Code.Locked | (1 << 8) + LockedVirtualTable = 518, // Code.Locked | (2 << 8) + BusyRecovery = 261, // Code.Busy | (1 << 8) + BusySnapshot = 517, // Code.Busy | (2 << 8) + CantOpenNoTempDir = 270, // Code.CantOpen | (1 << 8) + CantOpenIsDir = 526, // Code.CantOpen | (2 << 8) + CantOpenFullPath = 782, // Code.CantOpen | (3 << 8) + CantOpenConvPath = 1038, // Code.CantOpen | (4 << 8) + CantOpenDirtyWal = 1294, // Code.CantOpen | (5 << 8) + CorruptVirtualTable = 267, // Code.Corrupt | (1 << 8) + CorruptSequence = 523, // Code.Corrupt | (2 << 8) + ReadonlyRecovery = 264, // Code.Readonly | (1 << 8) + ReadonlyCantLock = 520, // Code.Readonly | (2 << 8) + ReadonlyRollback = 776, // Code.Readonly | (3 << 8) + ReadonlyDatabaseMoved = 1032, // Code.Readonly | (4 << 8) + ReadonlyCantInit = 1288, // Code.Readonly | (5 << 8) + ReadonlyDirectory = 1544, // Code.Readonly | (6 << 8) + AbortRollback = 516, // Code.Abort | (2 << 8) + ConstraintCheck = 275, // Code.Constraint | (1 << 8) + ConstraintCommitHook = 531, // Code.Constraint | (2 << 8) + ConstraintForeignKey = 787, // Code.Constraint | (3 << 8) + ConstraintFunction = 1043, // Code.Constraint | (4 << 8) + ConstraintNotNull = 1299, // Code.Constraint | (5 << 8) + ConstraintPrimaryKey = 1555, // Code.Constraint | (6 << 8) + ConstraintTrigger = 1811, // Code.Constraint | (7 << 8) + ConstraintUnique = 2067, // Code.Constraint | (8 << 8) + ConstraintVirtualTable = 2323, // Code.Constraint | (9 << 8) + ConstraintRowID = 2579, // Code.Constraint | (10 << 8) + NoticeRecoverWal = 283, // Code.Notice | (1 << 8) + NoticeRecoverRollback = 539, // Code.Notice | (2 << 8) + WarningAutoIndex = 284, // Code.Warning | (1 << 8) + AuthorizationUser = 279, // Code.Authorization | (1 << 8) + OKLoadPermanently = 256, // Code.OK | (1 << 8) + Unknown = -1, +} + +pub enum ExceptionKey { + Tag, + Path, + Type, + Source, + SQL, + ExtendedCode, + Message, +} + +impl Display for ExceptionKey { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ExceptionKey::Tag => write!(f, "Tag"), + ExceptionKey::Path => write!(f, "Path"), + ExceptionKey::Type => write!(f, "Type"), + ExceptionKey::Source => write!(f, "Source"), + ExceptionKey::SQL => write!(f, "SQL"), + ExceptionKey::ExtendedCode => write!(f, "ExtCode"), + ExceptionKey::Message => write!(f, "Message"), + } + } +} + +pub enum ExceptionObject { + Long(i64), + Double(f64), + String(String), +} + +pub type WCDBResult = Result; + +#[derive(Debug)] +pub enum WCDBException { + WCDBNormalException(ExceptionInner), + WCDBInterruptException(ExceptionInner), + WCDBCorruptOrIOException(ExceptionInner), +} + +impl WCDBException { + pub fn create_exception(cpp_obj: *mut c_void) -> Self { + let level = ExceptionLevel::value_of(unsafe { WCDBRustError_getLevel(cpp_obj) }); + let code = ExceptionCode::value_of(unsafe { WCDBRustError_getCode(cpp_obj) }); + if level != ExceptionLevel::Error { + WCDBException::WCDBNormalException(ExceptionInner::new(level, code, cpp_obj)) + } else if code == ExceptionCode::Interrupt { + WCDBException::WCDBInterruptException(ExceptionInner::new(level, code, cpp_obj)) + } else if code == ExceptionCode::Permission + || code == ExceptionCode::Readonly + || code == ExceptionCode::IOError + || code == ExceptionCode::Corrupt + || code == ExceptionCode::Full + || code == ExceptionCode::CantOpen + || code == ExceptionCode::NotADatabase + { + WCDBException::WCDBCorruptOrIOException(ExceptionInner::new(level, code, cpp_obj)) + } else { + WCDBException::WCDBNormalException(ExceptionInner::new(level, code, cpp_obj)) + } + } + + pub fn new_with_message(level: ExceptionLevel, code: ExceptionCode, message: String) -> Self { + println!( + "bugtags.new_with_message: {:?}, {:?}", + level, + message.clone() + ); + let mut key_values = HashMap::new(); + key_values.insert( + ExceptionKey::Message.to_string(), + ExceptionObject::String(message.clone()), + ); + WCDBException::WCDBNormalException(ExceptionInner::new_with_message( + level, + code, + message.clone(), + )) + } +} + +pub struct ExceptionInner { + pub level: ExceptionLevel, + pub code: ExceptionCode, + pub key_values: HashMap, +} + +impl Debug for ExceptionInner { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "Level: {:?}, ", self.level)?; + write!(f, "Code: {:?}, ", self.code)?; + let mut debug_struct = f.debug_struct("Exception"); + for (key, value) in &self.key_values { + match value { + ExceptionObject::Long(value) => debug_struct.field(key, value), + ExceptionObject::Double(value) => debug_struct.field(key, value), + ExceptionObject::String(value) => debug_struct.field(key, value), + }; + } + debug_struct.finish()?; + Ok(()) + } +} + +impl ExceptionInner { + pub fn new(level: ExceptionLevel, code: ExceptionCode, cpp_obj: *mut c_void) -> Self { + let mut key_values = HashMap::new(); + let message = unsafe { WCDBRustError_getMessage(cpp_obj) }; + key_values.insert( + ExceptionKey::Message.to_string(), + ExceptionObject::String(message.to_cow().to_string()), + ); + let key_values_ptr: *mut c_void = Box::into_raw(Box::new(key_values)) as *mut c_void; + unsafe { + WCDBRustError_enumerateInfo(key_values_ptr, cpp_obj); + } + let key_values_box = + unsafe { Box::from_raw(key_values_ptr as *mut HashMap) }; + let key_values = *key_values_box; + ExceptionInner { + level, + code, + key_values, + } + } + + pub fn new_with_message(level: ExceptionLevel, code: ExceptionCode, message: String) -> Self { + let mut map: HashMap = HashMap::new(); + map.insert( + ExceptionKey::Message.to_string(), + ExceptionObject::String(message.to_string()), + ); + ExceptionInner { + level, + code, + key_values: map, + } + } + + pub fn tag(&self) -> i64 { + match self.key_values.get(&ExceptionKey::Tag.to_string()) { + Some(obj) => match obj { + ExceptionObject::Long(value) => *value, + _ => 0, + }, + None => 0, + } + } + + pub fn extend_code(&self) -> ExceptionExtendCode { + match self.key_values.get(&ExceptionKey::ExtendedCode.to_string()) { + Some(obj) => match obj { + ExceptionObject::Long(value) => { + ExceptionExtendCode::from_i64(*value).unwrap_or(ExceptionExtendCode::Unknown) + } + _ => ExceptionExtendCode::Unknown, + }, + None => ExceptionExtendCode::Unknown, + } + } + + pub fn message(&self) -> String { + self.get_value_string_for_key(ExceptionKey::Message) + } + + pub fn sql(&self) -> String { + self.get_value_string_for_key(ExceptionKey::SQL) + } + + pub fn path(&self) -> String { + self.get_value_string_for_key(ExceptionKey::Path) + } + + pub fn get_description(&self) -> String { + let mut message = format!("Code: {}", self.code as i32); + for (key, value) in &self.key_values { + let value_string = match value { + ExceptionObject::Long(value) => value.to_string(), + ExceptionObject::Double(value) => value.to_string(), + ExceptionObject::String(value) => value.to_string(), + }; + message.push_str(&format!("; {}: {}", key, value_string)); + } + message + } + + fn get_value_string_for_key(&self, key: ExceptionKey) -> String { + match self.key_values.get(&key.to_string()) { + Some(obj) => match obj { + ExceptionObject::Long(value) => value.to_string(), + ExceptionObject::Double(value) => value.to_string(), + ExceptionObject::String(value) => value.to_string(), + }, + None => "".to_string(), + } + } + + pub fn to_string_for_log(&self) -> String { + format!( + "[WCDB] [{}] {}", + self.level.to_str(), + self.get_description() + ) + } +} +impl Deref for WCDBException { + type Target = ExceptionInner; + + fn deref(&self) -> &Self::Target { + match self { + WCDBException::WCDBNormalException(inner) => inner, + WCDBException::WCDBInterruptException(inner) => inner, + WCDBException::WCDBCorruptOrIOException(inner) => inner, + } + } +} diff --git a/src/rust/wcdb/src/chaincall/chain_call.rs b/src/rust/wcdb/src/chaincall/chain_call.rs new file mode 100644 index 000000000..d520d9763 --- /dev/null +++ b/src/rust/wcdb/src/chaincall/chain_call.rs @@ -0,0 +1,56 @@ +use crate::base::wcdb_exception::WCDBResult; +use crate::core::handle::Handle; +use crate::winq::statement::StatementTrait; +use std::cell::RefCell; + +pub(crate) struct ChainCall<'a, T: StatementTrait> { + pub(crate) handle: RefCell>, + changes: RefCell, + statement: T, + need_changes: RefCell, + auto_invalidate_handle: bool, +} + +pub trait ChainCallTrait { + fn update_changes(&self) -> WCDBResult<()>; + + fn get_statement(&self) -> &dyn StatementTrait; +} + +impl<'a, T: StatementTrait> ChainCallTrait for ChainCall<'a, T> { + fn update_changes(&self) -> WCDBResult<()> { + if *self.need_changes.borrow() { + *self.changes.borrow_mut() = self.handle.borrow().get_changes()?; + } + Ok(()) + } + + fn get_statement(&self) -> &dyn StatementTrait { + &self.statement + } +} + +impl<'a, T: StatementTrait> ChainCall<'a, T> { + pub(crate) fn new( + statement: T, + handle: Handle<'a>, + need_changes: bool, + auto_invalidate_handle: bool, + ) -> ChainCall<'a, T> { + ChainCall { + handle: RefCell::new(handle), + changes: RefCell::new(0), + statement, + need_changes: RefCell::new(need_changes), + auto_invalidate_handle, + } + } + + pub(crate) fn get_statement(&self) -> &T { + &self.statement + } + + pub(crate) fn invalidate_handle(&self) { + self.handle.borrow_mut().invalidate(); + } +} diff --git a/src/rust/wcdb/src/chaincall/delete.rs b/src/rust/wcdb/src/chaincall/delete.rs new file mode 100644 index 000000000..213d7b958 --- /dev/null +++ b/src/rust/wcdb/src/chaincall/delete.rs @@ -0,0 +1,72 @@ +use crate::base::wcdb_exception::WCDBResult; +use crate::chaincall::chain_call::{ChainCall, ChainCallTrait}; +use crate::core::handle::Handle; +use crate::core::handle_operation::HandleOperationTrait; +use crate::winq::expression::Expression; +use crate::winq::ordering_term::OrderingTerm; +use crate::winq::statement::StatementTrait; +use crate::winq::statement_delete::StatementDelete; +use std::fmt::Debug; + +pub struct Delete<'a> { + chain_call: ChainCall<'a, StatementDelete>, +} + +impl<'a> ChainCallTrait for Delete<'a> { + fn update_changes(&self) -> WCDBResult<()> { + self.chain_call.update_changes() + } + + fn get_statement(&self) -> &dyn StatementTrait { + self.chain_call.get_statement() + } +} + +impl<'a> Delete<'a> { + pub fn new(handle: Handle<'a>, need_changes: bool, auto_invalidate_handle: bool) -> Self { + Delete { + chain_call: ChainCall::new( + StatementDelete::new(), + handle, + need_changes, + auto_invalidate_handle, + ), + } + } + + pub fn from_table(&self, table_name: &str) -> &Self { + self.chain_call.get_statement().delete_from(table_name); + self + } + + pub fn where_(&self, condition: &Expression) -> &Self { + self.chain_call.get_statement().where_(condition); + self + } + + pub fn order_by(&self, orders: Vec<&OrderingTerm>) -> &Self { + self.chain_call.get_statement().order_by(orders); + self + } + + pub fn limit(&self, count: i64) -> &Self { + self.chain_call.get_statement().limit(count); + self + } + + pub fn offset(&self, offset: i64) -> &Self { + self.chain_call.get_statement().offset(offset); + self + } + + pub fn execute(&self) -> WCDBResult<&Self> { + let ret = self + .chain_call + .handle + .borrow() + .execute(self.chain_call.get_statement()); + self.chain_call.update_changes()?; + self.chain_call.invalidate_handle(); + ret.map(|_| self) + } +} diff --git a/src/rust/wcdb/src/chaincall/insert.rs b/src/rust/wcdb/src/chaincall/insert.rs new file mode 100644 index 000000000..4ecbaed2e --- /dev/null +++ b/src/rust/wcdb/src/chaincall/insert.rs @@ -0,0 +1,154 @@ +use crate::base::wcdb_exception::WCDBResult; +use crate::chaincall::chain_call::{ChainCall, ChainCallTrait}; +use crate::core::handle::Handle; +use crate::core::handle_operation::HandleOperationTrait; +use crate::orm::field::Field; +use crate::orm::table_binding::TableBinding; +use crate::winq::statement::StatementTrait; +use crate::winq::statement_insert::StatementInsert; +use std::cell::RefCell; +use std::fmt::Debug; + +pub struct Insert<'a, T> { + chain_call: ChainCall<'a, StatementInsert>, + has_conflict_action: RefCell, + fields: RefCell>>, + values: RefCell>, + last_insert_row_id: RefCell, +} + +impl<'a, T> Drop for Insert<'a, T> { + fn drop(&mut self) { + self.values.borrow_mut().clear(); + } +} + +impl<'a, T> ChainCallTrait for Insert<'a, T> { + fn update_changes(&self) -> WCDBResult<()> { + self.chain_call.update_changes() + } + + fn get_statement(&self) -> &dyn StatementTrait { + self.chain_call.get_statement() + } +} + +impl<'a, T> Insert<'a, T> { + pub fn new( + handle: Handle<'a>, + need_changes: bool, + auto_invalidate_handle: bool, + ) -> Insert<'a, T> { + Insert { + chain_call: ChainCall::new( + StatementInsert::new(), + handle, + need_changes, + auto_invalidate_handle, + ), + has_conflict_action: RefCell::new(false), + fields: RefCell::new(Vec::new()), + values: RefCell::new(Vec::new()), + last_insert_row_id: RefCell::new(0), + } + } + + pub fn or_replace(&self) -> &Self { + self.has_conflict_action.replace(true); + self.chain_call.get_statement().or_replace(); + self + } + + pub fn or_ignore(&self) -> &Self { + self.has_conflict_action.replace(true); + self.chain_call.get_statement().or_ignore(); + self + } + + pub fn into_table(&self, table_name: &str) -> &Self { + self.chain_call.get_statement().insert_into(table_name); + self + } + + pub fn on_fields(&self, fields: Vec<&'a Field>) -> &Self { + let fields_clone = fields.clone(); + let len = fields.len(); + self.fields.replace(fields); + self.chain_call + .get_statement() + .columns(fields_clone) + .values_with_bind_parameters(len); + self + } + + pub fn value(&self, object: T) -> &Self { + self.values.borrow_mut().clear(); + self.values.borrow_mut().push(object); + self + } + + pub fn values(&self, objects: Vec) -> &Self { + self.values.borrow_mut().clear(); + self.values.borrow_mut().extend(objects); + self + } + + pub fn execute(&self) -> WCDBResult<&Self> { + if self.values.borrow().is_empty() { + return Ok(self); + } + assert!(!self.fields.borrow().is_empty()); + if self.values.borrow().len() > 1 { + self.chain_call + .handle + .borrow() + .run_transaction(|handle| self.real_execute().is_ok())?; + } else { + self.real_execute()?; + } + Ok(self) + } + + pub fn get_last_insert_row_id(&self) -> i64 { + *self.last_insert_row_id.borrow() + } + + pub fn real_execute(&self) -> WCDBResult<()> { + let binding: &dyn TableBinding = Field::get_binding_from_fields(&self.fields.borrow()); + let prepared_statement = self + .chain_call + .handle + .borrow() + .prepared_with_main_statement(self.chain_call.get_statement())?; + *self.last_insert_row_id.borrow_mut() = 0; + let mut values = self.values.borrow_mut(); + for object in values.iter_mut() { + prepared_statement.reset(); + let mut index: usize = 1; + let has_conflict_action = *self.has_conflict_action.borrow(); + let is_auto_increment = !has_conflict_action && binding.is_auto_increment(object); + for field in &*self.fields.borrow() { + if is_auto_increment && field.is_auto_increment() { + prepared_statement.bind_null(index); + } else { + binding.bind_field(object, field, index, &prepared_statement); + } + index += 1; + } + prepared_statement.step()?; + if is_auto_increment { + binding.set_last_insert_row_id( + object, + self.chain_call.handle.borrow().get_last_inserted_row_id()?, + ); + } + } + if values.len() > 0 { + *self.last_insert_row_id.borrow_mut() = + self.chain_call.handle.borrow().get_last_inserted_row_id()?; + } + self.update_changes()?; + prepared_statement.finalize_statement(); + Ok(()) + } +} diff --git a/src/rust/wcdb/src/chaincall/mod.rs b/src/rust/wcdb/src/chaincall/mod.rs new file mode 100644 index 000000000..49e931aaa --- /dev/null +++ b/src/rust/wcdb/src/chaincall/mod.rs @@ -0,0 +1,5 @@ +pub mod chain_call; +pub mod delete; +pub mod insert; +pub mod select; +pub mod update; diff --git a/src/rust/wcdb/src/chaincall/select.rs b/src/rust/wcdb/src/chaincall/select.rs new file mode 100644 index 000000000..fc7fc5ee6 --- /dev/null +++ b/src/rust/wcdb/src/chaincall/select.rs @@ -0,0 +1,112 @@ +use crate::base::wcdb_exception::WCDBResult; +use crate::chaincall::chain_call::{ChainCall, ChainCallTrait}; +use crate::core::handle::Handle; +use crate::core::prepared_statement::PreparedStatement; +use crate::orm::field::Field; +use crate::winq::expression::Expression; +use crate::winq::ordering_term::OrderingTerm; +use crate::winq::statement::StatementTrait; +use crate::winq::statement_select::StatementSelect; +use std::cell::RefCell; +use std::sync::Arc; + +pub struct Select<'a, T> { + chain_call: ChainCall<'a, StatementSelect>, + fields: RefCell>>, +} + +impl<'a, T> ChainCallTrait for Select<'a, T> { + fn update_changes(&self) -> WCDBResult<()> { + self.chain_call.update_changes() + } + + fn get_statement(&self) -> &dyn StatementTrait { + self.chain_call.get_statement() + } +} + +impl<'a, T> Select<'a, T> { + pub fn new( + handle: Handle<'a>, + need_changes: bool, + auto_invalidate_handle: bool, + ) -> Select<'a, T> { + Select { + chain_call: ChainCall::new( + StatementSelect::new(), + handle, + need_changes, + auto_invalidate_handle, + ), + fields: RefCell::new(Vec::new()), + } + } + + pub fn select(&self, fields: Vec<&'a Field>) -> &Self { + self.fields.replace(fields); + self.chain_call + .get_statement() + .select(self.fields.borrow().iter().copied()); + self + } + + pub fn where_(&self, condition: &Expression) -> &Self { + self.chain_call.get_statement().where_(condition); + self + } + + pub fn order_by(&self, order_vec: Vec<&OrderingTerm>) -> &Self { + self.chain_call.get_statement().order_by(order_vec); + self + } + + pub fn limit(&self, count: i64) -> &Self { + self.chain_call.get_statement().limit(count); + self + } + + pub fn offset(&self, count: i64) -> &Self { + self.chain_call.get_statement().offset(count); + self + } + + pub fn from(&self, table_name: &str) -> &Self { + self.chain_call.get_statement().from(vec![table_name]); + self + } + + pub fn first_object(&self) -> WCDBResult> { + self.first_object_by_class() + } + + pub fn first_object_by_class(&self) -> WCDBResult> { + let prepared_statement = self.prepare_statement()?; + prepared_statement.step()?; + let mut ret = Ok(None); + if !prepared_statement.is_done() { + ret = prepared_statement.get_one_object(&self.fields.borrow()); + } + prepared_statement.finalize_statement(); + self.chain_call.invalidate_handle(); + ret + } + + pub fn all_objects(&self) -> WCDBResult> { + self.all_objects_by_class() + } + + pub fn all_objects_by_class(&self) -> WCDBResult> { + let prepared_statement = self.prepare_statement()?; + let ret = prepared_statement.get_all_objects(&self.fields.borrow()); + prepared_statement.finalize_statement(); + self.chain_call.invalidate_handle(); + ret + } + + fn prepare_statement(&self) -> WCDBResult> { + self.chain_call + .handle + .borrow() + .prepared_with_main_statement(self.chain_call.get_statement()) + } +} diff --git a/src/rust/wcdb/src/chaincall/update.rs b/src/rust/wcdb/src/chaincall/update.rs new file mode 100644 index 000000000..5a61f1d8d --- /dev/null +++ b/src/rust/wcdb/src/chaincall/update.rs @@ -0,0 +1,113 @@ +use crate::base::value::Value; +use crate::base::wcdb_exception::WCDBResult; +use crate::chaincall::chain_call::{ChainCall, ChainCallTrait}; +use crate::core::handle::Handle; +use crate::core::prepared_statement::PreparedStatement; +use crate::orm::field::Field; +use crate::winq::expression::Expression; +use crate::winq::ordering_term::OrderingTerm; +use crate::winq::statement::StatementTrait; +use crate::winq::statement_update::StatementUpdate; +use std::cell::RefCell; + +pub struct Update<'a, T> { + chain_call: ChainCall<'a, StatementUpdate>, + fields: RefCell>>, + object: RefCell>, + row: RefCell>, +} + +impl<'a, T> ChainCallTrait for Update<'a, T> { + fn update_changes(&self) -> WCDBResult<()> { + self.chain_call.update_changes() + } + + fn get_statement(&self) -> &dyn StatementTrait { + self.chain_call.get_statement() + } +} + +impl<'a, T> Update<'a, T> { + pub fn new( + handle: Handle<'a>, + need_changes: bool, + auto_invalidate_handle: bool, + ) -> Update<'a, T> { + Update { + chain_call: ChainCall::new( + StatementUpdate::new(), + handle, + need_changes, + auto_invalidate_handle, + ), + fields: RefCell::new(Vec::new()), + object: RefCell::new(None), + row: RefCell::new(Vec::new()), + } + } + + pub fn table(&self, table_name: &str) -> &Self { + self.chain_call.get_statement().update(table_name); + self + } + + pub fn set(&self, fields: Vec<&'a Field>) -> &Self { + self.fields.replace(fields.clone()); + self.chain_call + .get_statement() + .set_columns_to_bind_parameters(fields); + self + } + + pub fn where_(&self, condition: &Expression) -> &Self { + self.chain_call.get_statement().where_(condition); + self + } + + pub fn order_by(&self, orders: Vec<&OrderingTerm>) -> &Self { + self.chain_call.get_statement().order_by(orders); + self + } + + pub fn limit(&self, count: i64) -> &Self { + self.chain_call.get_statement().limit(count); + self + } + + pub fn offset(&self, offset: i64) -> &Self { + self.chain_call.get_statement().offset(offset); + self + } + + pub fn to_object(&self, object: T) -> &Self { + self.object.replace(Some(object)); + self + } + + pub fn execute(&self) -> WCDBResult<&Self> { + // let binding = Field::get_binding_from_fields(&self.fields); + let prepared_statement = self + .chain_call + .handle + .borrow() + .prepared_with_main_statement(self.chain_call.get_statement())?; + + if let Some(object) = self.object.take() { + PreparedStatement::bind_object_by_fields( + &prepared_statement, + object, + &*self.fields.borrow(), + ); + } else { + let row_vec = self.row.take(); + if !row_vec.is_empty() { + prepared_statement.bind_row(&row_vec); + } + } + prepared_statement.step()?; + self.chain_call.update_changes()?; + prepared_statement.finalize_statement(); + self.chain_call.invalidate_handle(); + Ok(self) + } +} diff --git a/src/rust/wcdb/src/core/database.rs b/src/rust/wcdb/src/core/database.rs new file mode 100644 index 000000000..0750bdf0a --- /dev/null +++ b/src/rust/wcdb/src/core/database.rs @@ -0,0 +1,1729 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait, WCDBRustBase_releaseObject}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::value::Value; +use crate::base::wcdb_exception::{ExceptionCode, ExceptionLevel, WCDBException, WCDBResult}; +use crate::chaincall::delete::Delete; +use crate::chaincall::insert::Insert; +use crate::chaincall::select::Select; +use crate::chaincall::update::Update; +use crate::core::handle::Handle; +use crate::core::handle_operation::HandleOperationTrait; +use crate::core::handle_orm_operation::{HandleORMOperation, HandleORMOperationTrait}; +use crate::core::table::Table; +use crate::orm::field::Field; +use crate::orm::table_binding::TableBinding; +use crate::utils::{ToCString, ToCow}; +use crate::winq::expression::Expression; +use crate::winq::ordering_term::OrderingTerm; +use crate::winq::statement::StatementTrait; +use lazy_static::lazy_static; +use std::cell::RefCell; +use std::collections::HashMap; +use std::ffi::{c_char, c_double, c_int, c_void, CStr, CString}; +use std::ptr::null_mut; +use std::sync::{Arc, Mutex}; + +// 定义性能跟踪回调的特性 +pub trait TracePerformanceCallbackTrait: +Fn(/*tag*/i64, /*path*/String, /*handleId*/i64, /*sql*/String, /*info*/PerformanceInfo) + Send {} + +pub type TracePerformanceCallback = Box; + +impl TracePerformanceCallbackTrait for T where + T: Fn( + /*tag*/ i64, + /*path*/ String, + /*handleId*/ i64, + /*sql*/ String, + /*info*/ PerformanceInfo, + ) + Send +{ +} + +// 定义 sql 执行回调的特性 +pub trait TraceSqlCallbackTrait: Fn(/*tag*/i64, /*path*/String, /*handleId*/i64, /*sql*/String, /*info*/String) + Send {} + +pub type TraceSqlCallback = Box; + +impl TraceSqlCallbackTrait for T where + T: Fn( + /*tag*/ i64, + /*path*/ String, + /*handleId*/ i64, + /*sql*/ String, + /*info*/ String, + ) + Send +{ +} + +// 定义异常回调的特性 +pub trait TraceExceptionCallbackTrait: Fn(WCDBException) + Send {} + +pub type TraceExceptionCallback = Box; + +impl TraceExceptionCallbackTrait for T where T: Fn(WCDBException) + Send {} + +// 定义损坏检测回调的特性 +pub trait CorruptionNotificationTrait: Fn(Database) + Send {} + +impl CorruptionNotificationTrait for T where T: Fn(Database) + Send {} + +pub type CorruptionNotificationCallback = Box; + +pub trait BackupFilterTrait { + fn table_should_be_backup(&self, table_name: &str) -> bool; +} + +// 定义备份回调的特性 +pub trait BackupFilterCallbackTrait: Fn(&str) -> bool + Send {} + +impl BackupFilterCallbackTrait for T where T: Fn(&str) -> bool + Send {} + +pub type BackupFilterCallback = Box; + +// return True to continue current operation. +pub trait ProgressMonitorTrait: Fn(/*percentage*/f64, /*increment*/f64) -> bool + Send {} + +impl ProgressMonitorTrait for T where T: Fn(/*percentage*/ f64, /*increment*/ f64) -> bool + Send {} + +pub type ProgressMonitorTraitCallback = Box; + +pub trait SetDatabaseConfigTrait: Fn(Handle) -> bool + Send + Sync {} + +pub type SetDatabaseConfigCallback = Box; + +impl SetDatabaseConfigTrait for T where T: Fn(Handle) -> bool + Send + Sync {} + +// 定义一个全局静态变量来存储闭包 +lazy_static! { + static ref GLOBAL_TRACE_PERFORMANCE_CALLBACK: Arc>> = + Arc::new(Mutex::new(None)); + static ref GLOBAL_TRACE_SQL_CALLBACK: Arc>> = + Arc::new(Mutex::new(None)); + static ref GLOBAL_TRACE_EXCEPTION_CALLBACK: Arc>> = + Arc::new(Mutex::new(None)); + static ref GLOBAL_CORRUPTION_NOTIFICATION_CALLBACK: Arc>> = + Arc::new(Mutex::new(None)); + static ref GLOBAL_BACKUP_FILTER_CALLBACK: Arc>> = + Arc::new(Mutex::new(None)); + static ref GLOBAL_PROGRESS_MONITOR_TRAIT_CALLBACK: Arc>> = + Arc::new(Mutex::new(None)); + static ref GLOBAL_VACUUM_PROGRESS_MONITOR_TRAIT_CALLBACK: Arc>> = + Arc::new(Mutex::new(None)); + static ref GLOBAL_INVOCATION_CONFIG_CALLBACK: Arc>> = + Arc::new(Mutex::new(None)); + static ref GLOBAL_UN_INVOCATION_CONFIG_CALLBACK: Arc>> = + Arc::new(Mutex::new(None)); +} + +pub type DatabaseCloseCallback = extern "C" fn(context: *mut c_void); + +extern "C" { + fn WCDBRustCore_createDatabase( + path: *const c_char, + readonly: bool, + in_memory: bool, + ) -> *mut c_void; + fn WCDBRustDatabase_getPath(cpp_obj: *mut c_void) -> *const c_char; + + fn WCDBRustDatabase_removeFiles(cpp_obj: *mut c_void) -> bool; + + fn WCDBRustDatabase_configCipher( + cpp_obj: *mut c_void, + key: *const u8, + key_len: usize, + page_size: c_int, + version: c_int, + ); + + fn WCDBRustCore_setDefaultCipherConfig(version: c_int); + + fn WCDBRustDatabase_close( + cpp_obj: *mut c_void, + context: *mut c_void, + cb: DatabaseCloseCallback, + ); + + pub(crate) fn WCDBRustDatabase_config( + cpp_obj: *mut c_void, + config_name: *const c_char, + invocation: *const c_void, + un_invocation: *const c_void, + priority: c_int, + ); + + fn WCDBRustDatabase_blockade(cpp_obj: *mut c_void); + + fn WCDBRustDatabase_unblockade(cpp_obj: *mut c_void); + + fn WCDBRustDatabase_isBlockaded(cpp_obj: *mut c_void) -> bool; + + fn WCDBRustDatabase_canOpen(cpp_obj: *mut c_void) -> bool; + + fn WCDBRustDatabase_isOpened(cpp_obj: *mut c_void) -> bool; + + fn WCDBRustDatabase_getHandle(cpp_obj: *mut c_void, write_hint: bool) -> *mut c_void; + + fn WCDBRustDatabase_vacuum(cpp_obj: *mut c_void, monitor: *const c_void) -> bool; + + fn WCDBRustDatabase_enableAutoVacuum(cpp_obj: *mut c_void, incremental: bool); + + fn WCDBRustDatabase_incrementalVacuum(cpp_obj: *mut c_void, pages: i32) -> bool; + + fn WCDBRustDatabase_getError(cpp_obj: *mut c_void) -> *mut c_void; + + fn WCDBRustDatabase_globalTracePerformance(global_trace_performance_callback: *mut c_void); + + fn WCDBRustDatabase_tracePerformance( + cpp_obj: *mut c_void, + trace_performance_callback: extern "C" fn( + cb_raw: *mut c_void, + tag: i64, + path: *const c_char, + handle_id: i64, + sql: *const c_char, + info: PerformanceInfo, + ), + cb_ptr: *mut c_void, + ); + + fn WCDBRustDatabase_globalTraceSQL(global_trace_sql_callback: *mut c_void); + + fn WCDBRustDatabase_traceSQL( + cpp_obj: *mut c_void, + trace_sql_callback: extern "C" fn( + cb_raw: *mut c_void, + tag: i64, + path: *const c_char, + handle_id: i64, + sql: *const c_char, + info: *const c_char, + ), + cb_ptr: *mut c_void, + ); + + fn WCDBRustDatabase_globalTraceException(global_trace_exception_callback: *mut c_void); + + fn WCDBRustDatabase_traceException( + cpp_obj: *mut c_void, + trace_exception_callback: extern "C" fn(cb_raw: *mut c_void, exp_cpp_obj: *mut c_void), + cb_ptr: *mut c_void, + ); + + fn WCDBRustDatabase_getTag(cpp_obj: *mut c_void) -> *mut c_void; + + fn WCDBRustDatabase_setTag(cpp_obj: *mut c_void, tag: i64); + + fn WCDBRustDatabase_addTokenizer(cpp_obj: *mut c_void, tokenizer: *const c_char); + + fn WCDBRustDatabase_configPinyinDict( + keys: *const *const c_char, + values: *const *const *const c_char, + values_len: *const usize, + keys_len: usize, + ); + + fn WCDBRustDatabase_configTraditionalChineseDict( + keys: *const *const c_char, + values: *const *const c_char, + len: usize, + ); + + fn WCDBRustDatabase_setNotificationWhenCorrupted( + cpp_obj: *mut c_void, + global_corruption_notification: *mut c_void, + ); + + fn WCDBRustDatabase_checkIfCorrupted(cpp_obj: *mut c_void) -> bool; + + fn WCDBRustDatabase_checkIfIsAlreadyCorrupted(cpp_obj: *mut c_void) -> bool; + + fn WCDBRustDatabase_enableAutoBackup(cpp_obj: *mut c_void, enable: bool); + + fn WCDBRustDatabase_backup(cpp_obj: *mut c_void) -> bool; + + fn WCDBRustDatabase_filterBackup(cpp_obj: *mut c_void, filter: *const c_void); + + fn WCDBRustDatabase_retrieve(cpp_obj: *mut c_void, monitor: *const c_void) -> c_double; + + fn WCDBRustDatabase_deposit(cpp_obj: *mut c_void) -> bool; + + fn WCDBRustDatabase_removeDepositedFiles(cpp_obj: *mut c_void) -> bool; + + fn WCDBRustDatabase_containDepositedFiles(cpp_obj: *mut c_void) -> bool; + + fn WCDBRustDatabase_truncateCheckpoint(cpp_obj: *mut c_void) -> bool; + + fn WCDBRustDatabase_setAutoCheckpointEnable(cpp_obj: *mut c_void, enable: bool); +} + +extern "C" fn close_callback_wrapper(context: *mut c_void) { + if !context.is_null() { + let boxed_cb: Box> = unsafe { Box::from_raw(context as *mut _) }; + boxed_cb(); + } +} + +extern "C" fn global_trace_performance_callback( + tag: i64, + path: *const c_char, + handle_id: i64, + sql: *const c_char, + info: PerformanceInfo, +) { + let global_callback = GLOBAL_TRACE_PERFORMANCE_CALLBACK.lock(); + match global_callback { + Ok(callback) => { + if let Some(cb) = &*callback { + cb( + tag, + path.to_cow().to_string(), + handle_id, + sql.to_cow().to_string(), + info, + ); + } else { + eprintln!("Method: global_trace_performance_callback, No callback found."); + } + } + Err(error) => { + eprintln!( + "Method: global_trace_performance_callback, Failed to acquire lock: {:?}", + error + ); + } + } +} + +extern "C" fn trace_performance_callback( + cb_raw: *mut c_void, + tag: i64, + path: *const c_char, + handle_id: i64, + sql: *const c_char, + info: PerformanceInfo, +) { + if cb_raw.is_null() { + return; + } + let closure = unsafe { &*(cb_raw as *mut Box) }; + closure( + tag, + path.to_cow().to_string(), + handle_id, + sql.to_cow().to_string(), + info, + ); +} + +extern "C" fn global_trace_sql_callback( + tag: i64, + path: *const c_char, + handle_id: i64, + sql: *const c_char, + info: *const c_char, +) { + let global_callback = GLOBAL_TRACE_SQL_CALLBACK.lock(); + match global_callback { + Ok(callback) => { + if let Some(cb) = &*callback { + cb( + tag, + path.to_cow().to_string(), + handle_id, + sql.to_cow().to_string(), + info.to_cow().to_string(), + ); + } else { + eprintln!("Method: global_trace_sql_callback, No callback found."); + } + } + Err(error) => { + eprintln!( + "Method: global_trace_sql_callback, Failed to acquire lock: {:?}", + error + ); + } + } +} + +extern "C" fn trace_sql_callback( + cb_raw: *mut c_void, + tag: i64, + path: *const c_char, + handle_id: i64, + sql: *const c_char, + info: *const c_char, +) { + if cb_raw.is_null() { + return; + } + let closure = unsafe { &*(cb_raw as *mut Box) }; + closure( + tag, + path.to_cow().to_string(), + handle_id, + sql.to_cow().to_string(), + info.to_cow().to_string(), + ); +} + +extern "C" fn global_trace_exception_callback(exp_cpp_obj: *mut c_void) { + if exp_cpp_obj.is_null() { + return; + } + let global_callback = GLOBAL_TRACE_EXCEPTION_CALLBACK.lock(); + match global_callback { + Ok(callback) => { + if let Some(cb) = &*callback { + let ex = WCDBException::create_exception(exp_cpp_obj); + cb(ex); + } else { + eprintln!("Method: global_trace_exception_callback, No callback found."); + } + } + Err(error) => { + eprintln!( + "Method: global_trace_exception_callback, Failed to acquire lock: {:?}", + error + ); + } + } +} + +extern "C" fn trace_exception_callback(cb_raw: *mut c_void, exp_cpp_obj: *mut c_void) { + if cb_raw.is_null() || exp_cpp_obj.is_null() { + return; + } + let closure = unsafe { &*(cb_raw as *mut Box) }; + let ex = WCDBException::create_exception(exp_cpp_obj); + closure(ex); + unsafe { WCDBRustBase_releaseObject(exp_cpp_obj) }; +} + +extern "C" fn global_corruption_notification_callback_wrapper(cpp_obj: *mut c_void) { + match GLOBAL_CORRUPTION_NOTIFICATION_CALLBACK.lock() { + Ok(callback) => { + if let Some(cb) = &*callback { + let database = Database::from(cpp_obj); + cb(database); + } else { + eprintln!("Method: retrieve_progress_monitor_trait_wrapper, No callback found."); + } + } + Err(error) => { + eprintln!( + "Method: global_corruption_notification_callback_wrapper, Failed to acquire lock: {:?}", + error + ); + } + } +} + +extern "C" fn backup_filter_callback_wrapper(table_name: *const c_char) -> bool { + let global_callback = GLOBAL_BACKUP_FILTER_CALLBACK.lock(); + match global_callback { + Ok(callback) => { + if let Some(cb) = &*callback { + let cstr = unsafe { CStr::from_ptr(table_name) }; + match cstr.to_str() { + Ok(str) => cb(str), + Err(error) => { + eprintln!( + "Method: backup_filter_callback_wrapper, CStr parsing error: {:?}", + error + ); + false + } + } + } else { + eprintln!("Method: backup_filter_callback_wrapper, No callback found."); + false + } + } + Err(error) => { + eprintln!( + "Method: backup_filter_callback_wrapper, Failed to acquire lock: {:?}", + error + ); + false + } + } +} + +// True to continue current operation. +extern "C" fn retrieve_progress_monitor_trait_wrapper( + percentage: c_double, + increment: c_double, +) -> bool { + let global_callback = GLOBAL_PROGRESS_MONITOR_TRAIT_CALLBACK.lock(); + match global_callback { + Ok(callback) => { + if let Some(cb) = &*callback { + cb(percentage as f64, increment as f64) + } else { + eprintln!("Method: retrieve_progress_monitor_trait_wrapper, No callback found."); + false + } + } + Err(error) => { + eprintln!( + "Method: retrieve_progress_monitor_trait_wrapper, Failed to acquire lock: {:?}", + error + ); + false + } + } +} + +// True to continue current operation. +extern "C" fn vacuum_progress_monitor_trait_wrapper( + percentage: c_double, + increment: c_double, +) -> bool { + let global_callback = GLOBAL_VACUUM_PROGRESS_MONITOR_TRAIT_CALLBACK.lock(); + match global_callback { + Ok(callback) => { + if let Some(cb) = &*callback { + cb(percentage as f64, increment as f64) + } else { + eprintln!("Method: vacuum_progress_monitor_trait_wrapper, No callback found."); + false + } + } + Err(error) => { + eprintln!( + "Method: vacuum_progress_monitor_trait_wrapper, Failed to acquire lock: {:?}", + error + ); + false + } + } +} + +extern "C" fn set_config_invocation_callback(cpp_handle: *mut c_void) -> bool { + let global_callback = GLOBAL_INVOCATION_CONFIG_CALLBACK.lock(); + match global_callback { + Ok(callback) => match &*callback { + None => true, + Some(cb) => { + let db = Database::create_invalid_database(); + let handle = Handle::new_with_obj(cpp_handle, &db); + cb(handle) + } + }, + Err(error) => { + eprintln!( + "Method: set_config_invocation_callback, Failed to acquire lock: {:?}", + error + ); + false + } + } +} + +extern "C" fn set_config_un_invocation_callback(cpp_handle: *mut c_void) -> bool { + let global_callback = GLOBAL_UN_INVOCATION_CONFIG_CALLBACK.lock(); + match global_callback { + Ok(callback) => match &*callback { + None => true, + Some(cb) => { + let db = Database::create_invalid_database(); + let handle = Handle::new_with_obj(cpp_handle, &db); + cb(handle) + } + }, + Err(error) => { + eprintln!( + "Method: set_config_un_invocation_callback, Failed to acquire lock: {:?}", + error + ); + false + } + } +} + +/// Priority of config. +/// The higher the priority, the earlier it will be executed. +/// Note that the highest priority is only for cipher config. +#[repr(i32)] +pub enum ConfigPriority { + Low, + Default, + High, + Highest, +} + +pub struct Database { + handle_orm_operation: HandleORMOperation, + close_callback: Arc>>>, + trace_callback_ref: Arc>, + trace_sql_ref: Arc>, + trace_exception_ref: Arc>, +} + +unsafe impl Send for Database {} + +unsafe impl Sync for Database {} + +impl Drop for Database { + fn drop(&mut self) { + let raw_ptr = *self.trace_callback_ref.borrow_mut(); + if !raw_ptr.is_null() { + unsafe { + let _ = Box::from_raw(raw_ptr); + } + } + } +} + +impl CppObjectTrait for Database { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.handle_orm_operation.set_cpp_obj(cpp_obj) + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.handle_orm_operation.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.handle_orm_operation.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for Database { + fn as_cpp_object(&self) -> &CppObject { + self.handle_orm_operation.as_cpp_object() + } +} + +impl HandleOperationTrait for Database { + fn get_handle(&self, write_hint: bool) -> Handle { + Handle::new(self, write_hint) + } + + fn auto_invalidate_handle(&self) -> bool { + true + } + + fn run_transaction bool>(&self, callback: F) -> WCDBResult<()> { + self.handle_orm_operation.run_transaction( + self.get_handle(true), + self.auto_invalidate_handle(), + callback, + ) + } + + fn execute(&self, statement: &T) -> WCDBResult<()> { + self.handle_orm_operation.execute( + self.get_handle(true), + self.auto_invalidate_handle(), + statement, + ) + } + + fn execute_sql(&self, sql: &str) -> WCDBResult<()> { + self.handle_orm_operation.execute_sql( + self.get_handle(true), + self.auto_invalidate_handle(), + sql, + ) + } +} + +impl HandleORMOperationTrait for Database { + fn create_table>( + &self, + table_name: &str, + binding: &R, + ) -> WCDBResult { + self.handle_orm_operation + .create_table(self.get_handle(true), table_name, binding) + } + + fn create_virtual_table>( + &self, + table_name: &str, + binding: &R, + ) -> WCDBResult { + self.handle_orm_operation + .create_virtual_table(self.get_handle(true), table_name, binding) + } + + fn table_exist(&self, table_name: &str) -> WCDBResult { + self.handle_orm_operation.table_exist( + self.get_handle(true), + self.auto_invalidate_handle(), + table_name, + ) + } + + fn drop_table(&self, table_name: &str) -> WCDBResult<()> { + self.handle_orm_operation.drop_table( + self.get_handle(true), + self.auto_invalidate_handle(), + table_name, + ) + } + + fn prepare_insert(&self) -> Insert { + self.handle_orm_operation + .prepare_insert(self.get_handle(true), self.auto_invalidate_handle()) + } + + fn prepare_update(&self) -> Update { + self.handle_orm_operation + .prepare_update(self.get_handle(true), self.auto_invalidate_handle()) + } + + fn prepare_select(&self) -> Select { + self.handle_orm_operation + .prepare_select(self.get_handle(true), self.auto_invalidate_handle()) + } + + fn prepare_delete(&self) -> Delete { + self.handle_orm_operation + .prepare_delete(self.get_handle(true), self.auto_invalidate_handle()) + } + + fn insert_object( + &self, + object: T, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + self.handle_orm_operation.insert_object( + self.get_handle(true), + self.auto_invalidate_handle(), + object, + fields, + table_name, + ) + } + + fn insert_or_replace_object( + &self, + object: T, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + self.handle_orm_operation.insert_or_replace_object( + self.get_handle(true), + self.auto_invalidate_handle(), + object, + fields, + table_name, + ) + } + + fn insert_or_ignore_object( + &self, + object: T, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + self.handle_orm_operation.insert_or_ignore_object( + self.get_handle(true), + self.auto_invalidate_handle(), + object, + fields, + table_name, + ) + } + + fn insert_objects( + &self, + objects: Vec, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + self.handle_orm_operation.insert_objects( + self.get_handle(true), + self.auto_invalidate_handle(), + objects, + fields, + table_name, + ) + } + + fn insert_or_replace_objects( + &self, + objects: Vec, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + self.handle_orm_operation.insert_or_replace_objects( + self.get_handle(true), + self.auto_invalidate_handle(), + objects, + fields, + table_name, + ) + } + + fn insert_or_ignore_objects( + &self, + objects: Vec, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + self.handle_orm_operation.insert_or_ignore_objects( + self.get_handle(true), + self.auto_invalidate_handle(), + objects, + fields, + table_name, + ) + } + + fn delete_objects( + &self, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + self.handle_orm_operation.delete_objects( + self.get_handle(true), + self.auto_invalidate_handle(), + table_name, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } + + fn update_object( + &self, + object: T, + fields: Vec<&Field>, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + self.handle_orm_operation.update_object( + self.get_handle(true), + self.auto_invalidate_handle(), + object, + fields, + table_name, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } + + fn get_first_object( + &self, + fields: Vec<&Field>, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + offset_opt: Option, + ) -> WCDBResult> { + self.handle_orm_operation.get_first_object( + self.get_handle(true), + self.auto_invalidate_handle(), + fields, + table_name, + condition_opt, + order_opt, + offset_opt, + ) + } + + fn get_all_objects( + &self, + fields: Vec<&Field>, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult> { + self.handle_orm_operation.get_all_objects( + self.get_handle(true), + self.auto_invalidate_handle(), + fields, + table_name, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } +} + +impl Database { + pub(crate) fn create_invalid_database() -> Self { + Database { + handle_orm_operation: HandleORMOperation::new(None), + close_callback: Arc::new(Mutex::new(None)), + trace_callback_ref: Arc::new(RefCell::new(null_mut())), + trace_sql_ref: Arc::new(RefCell::new(null_mut())), + trace_exception_ref: Arc::new(RefCell::new(null_mut())), + } + } + + pub fn new(path: &str, readonly_opt: Option) -> Self { + let c_path = CString::new(path).unwrap_or_default(); + let readonly = readonly_opt.unwrap_or(false); + let cpp_obj = unsafe { WCDBRustCore_createDatabase(c_path.as_ptr(), readonly, false) }; + Database { + handle_orm_operation: HandleORMOperation::new(Some(cpp_obj)), + close_callback: Arc::new(Mutex::new(None)), + trace_callback_ref: Arc::new(RefCell::new(null_mut())), + trace_sql_ref: Arc::new(RefCell::new(null_mut())), + trace_exception_ref: Arc::new(RefCell::new(null_mut())), + } + } + + pub fn create_in_memory_database() -> Self { + let c_path = CString::new("").unwrap_or_default(); + let cpp_obj = unsafe { WCDBRustCore_createDatabase(c_path.as_ptr(), false, true) }; + Database { + handle_orm_operation: HandleORMOperation::new(Some(cpp_obj)), + close_callback: Arc::new(Mutex::new(None)), + trace_callback_ref: Arc::new(RefCell::new(null_mut())), + trace_sql_ref: Arc::new(RefCell::new(null_mut())), + trace_exception_ref: Arc::new(RefCell::new(null_mut())), + } + } + + // todo qixinbing : Java 没有该方法,考虑删除 + pub fn from(cpp_obj: *mut c_void) -> Self { + Database { + handle_orm_operation: HandleORMOperation::new(Some(cpp_obj)), + close_callback: Arc::new(Mutex::new(None)), + trace_callback_ref: Arc::new(RefCell::new(null_mut())), + trace_sql_ref: Arc::new(RefCell::new(null_mut())), + trace_exception_ref: Arc::new(RefCell::new(null_mut())), + } + } + + pub fn get_path(&self) -> String { + let path = unsafe { WCDBRustDatabase_getPath(self.get_cpp_obj()) }; + path.to_cow().to_string() + } + + pub fn remove_files(&self) -> WCDBResult<()> { + let ret: bool = unsafe { WCDBRustDatabase_removeFiles(self.get_cpp_obj()) }; + if ret { + Ok(()) + } else { + Err(self.create_exception()) + } + } + + pub fn set_cipher_key( + &self, + key: &Vec, + page_size: Option, + version: Option, + ) { + let key_ptr = key.as_ptr(); + let key_len = key.len(); + let page_size = page_size.unwrap_or(4096); + let version = version.unwrap_or(CipherVersion::DefaultVersion); + unsafe { + WCDBRustDatabase_configCipher( + self.get_cpp_obj(), + key_ptr, + key_len, + page_size, + version as i32, + ); + } + } + + pub fn set_default_cipher_version(version: CipherVersion) { + unsafe { + WCDBRustCore_setDefaultCipherConfig(version as i32); + } + } + + pub fn set_config( + &self, + config_name: &str, + invocation: Option, + un_invocation: Option, + priority: ConfigPriority, + ) -> WCDBResult<()> + where + I: SetDatabaseConfigTrait + 'static, + U: SetDatabaseConfigTrait + 'static, + { + let mut cpp_priority: i32 = 0; + match priority { + ConfigPriority::Low => { + cpp_priority = 100; + } + ConfigPriority::Default => {} + ConfigPriority::High => { + cpp_priority = -100; + } + ConfigPriority::Highest => { + cpp_priority = -2147483648; + } + } + + let c_config_name = config_name.to_cstring(); + + let mut invocation_raw: *const c_void = set_config_invocation_callback as *mut c_void; + let mut un_invocation_raw: *const c_void = set_config_un_invocation_callback as *mut c_void; + { + match GLOBAL_INVOCATION_CONFIG_CALLBACK.lock() { + Ok(mut global_callback) => match invocation { + None => { + *global_callback = None; + invocation_raw = null_mut(); + } + Some(cb) => { + let callback_box = Box::new(cb) as SetDatabaseConfigCallback; + *global_callback = Some(callback_box); + } + }, + Err(error) => { + return Err(WCDBException::new_with_message( + ExceptionLevel::Error, + ExceptionCode::Error, + error.to_string(), + )); + } + } + } + { + match GLOBAL_UN_INVOCATION_CONFIG_CALLBACK.lock() { + Ok(mut global_callback) => match un_invocation { + None => { + *global_callback = None; + un_invocation_raw = null_mut(); + } + Some(cb) => { + let callback_box = Box::new(cb) as SetDatabaseConfigCallback; + *global_callback = Some(callback_box); + } + }, + Err(error) => { + return Err(WCDBException::new_with_message( + ExceptionLevel::Error, + ExceptionCode::Error, + error.to_string(), + )); + } + } + } + + unsafe { + WCDBRustDatabase_config( + self.get_cpp_obj(), + c_config_name.as_ptr(), + invocation_raw, + un_invocation_raw, + cpp_priority as c_int, + ); + } + Ok(()) + } + + pub fn set_config_with_invocation( + &self, + config_name: &str, + invocation: Option, + priority: ConfigPriority, + ) -> WCDBResult<()> + where + I: SetDatabaseConfigTrait + 'static, + U: SetDatabaseConfigTrait + 'static, + { + self.set_config::(config_name, invocation, None, priority) + } + + pub fn set_config_with_default_priority( + &self, + config_name: &str, + invocation: Option, + ) -> WCDBResult<()> + where + I: SetDatabaseConfigTrait + 'static, + U: SetDatabaseConfigTrait + 'static, + { + self.set_config::(config_name, invocation, None, ConfigPriority::Default) + } + + pub fn can_open(&self) -> bool { + unsafe { WCDBRustDatabase_canOpen(self.get_cpp_obj()) } + } + + pub fn is_opened(&self) -> bool { + unsafe { WCDBRustDatabase_isOpened(self.get_cpp_obj()) } + } + + pub fn get_table<'a, T, R: TableBinding>( + &'a self, + table_name: &str, + binding: &'a R, + ) -> Arc> { + assert!(!table_name.is_empty()); + Arc::new(Table::new(table_name, binding, self)) + } + + pub fn close(&self, cb_opt: Option) + where + CB: FnOnce() + Send + 'static, + { + match cb_opt { + None => unsafe { + WCDBRustDatabase_close(self.get_cpp_obj(), null_mut(), close_callback_wrapper) + }, + Some(cb) => { + let boxed_cb: Box> = Box::new(Box::new(cb)); + let context = Box::into_raw(boxed_cb) as *mut c_void; + unsafe { + WCDBRustDatabase_close(self.get_cpp_obj(), context, close_callback_wrapper) + } + } + } + } + + pub fn blockade(&self) { + unsafe { WCDBRustDatabase_blockade(self.get_cpp_obj()) } + } + + pub fn un_blockade(&self) { + unsafe { WCDBRustDatabase_unblockade(self.get_cpp_obj()) } + } + + pub fn is_blockaded(&self) -> bool { + unsafe { WCDBRustDatabase_isBlockaded(self.get_cpp_obj()) } + } + + pub(crate) fn get_handle_raw(cpp_obj: *mut c_void, write_hint: bool) -> *mut c_void { + unsafe { WCDBRustDatabase_getHandle(cpp_obj, write_hint) } + } + + pub fn vacuum(&self, monitor: Option) -> WCDBResult<()> + where + CB: ProgressMonitorTrait + 'static, + { + let mut ret: bool = false; + let mut cb_raw: *mut c_void = null_mut(); + { + match GLOBAL_VACUUM_PROGRESS_MONITOR_TRAIT_CALLBACK.lock() { + Ok(mut global_callback) => match monitor { + None => { + *global_callback = None; + cb_raw = null_mut(); + } + Some(cb) => { + let callback_box = Box::new(cb) as ProgressMonitorTraitCallback; + *global_callback = Some(callback_box); + cb_raw = vacuum_progress_monitor_trait_wrapper as *mut c_void; + } + }, + Err(error) => { + return Err(WCDBException::new_with_message( + ExceptionLevel::Error, + ExceptionCode::Error, + error.to_string(), + )); + } + } + } + ret = unsafe { WCDBRustDatabase_vacuum(self.get_cpp_obj(), cb_raw) }; + if ret { + Ok(()) + } else { + Err(self.create_exception()) + } + } + + pub fn enable_auto_vacuum(&self, incremental: bool) { + unsafe { WCDBRustDatabase_enableAutoVacuum(self.get_cpp_obj(), incremental) } + } + + pub fn incremental_vacuum(&self, pages: i32) -> WCDBResult<()> { + let ret: bool = unsafe { WCDBRustDatabase_incrementalVacuum(self.get_cpp_obj(), pages) }; + if ret { + Ok(()) + } else { + Err(self.create_exception()) + } + } + + pub(crate) fn create_exception(&self) -> WCDBException { + WCDBException::create_exception(unsafe { WCDBRustDatabase_getError(self.get_cpp_obj()) }) + } + + pub fn global_trace_performance(cb_opt: Option) -> WCDBResult<()> + where + CB: TracePerformanceCallbackTrait + 'static, + { + let mut cb_raw: *mut c_void = null_mut(); + { + match GLOBAL_TRACE_PERFORMANCE_CALLBACK.lock() { + Ok(mut global_callback) => match cb_opt { + None => { + *global_callback = None; + cb_raw = global_trace_performance_callback as *mut c_void; + } + Some(cb) => { + let callback_box = Box::new(cb) as TracePerformanceCallback; + *global_callback = Some(callback_box); + cb_raw = global_trace_performance_callback as *mut c_void; + } + }, + Err(error) => { + return Err(WCDBException::new_with_message( + ExceptionLevel::Error, + ExceptionCode::Error, + error.to_string(), + )); + } + } + } + unsafe { + WCDBRustDatabase_globalTracePerformance(cb_raw); + } + Ok(()) + } + + pub fn trace_performance(&self, cb_opt: Option) -> WCDBResult<()> + where + CB: TracePerformanceCallbackTrait + 'static, + { + let mut closure_raw = null_mut(); + if let Some(cb) = cb_opt { + let closure_box = Box::new(Box::new(cb) as Box); + closure_raw = Box::into_raw(closure_box) as *mut c_void; + let mut value = self.trace_callback_ref.borrow_mut(); + *value = closure_raw; + } + unsafe { + WCDBRustDatabase_tracePerformance( + self.get_cpp_obj(), + trace_performance_callback, + closure_raw, + ); + } + Ok(()) + } + + pub fn global_trace_sql(cb_opt: Option) -> WCDBResult<()> + where + CB: TraceSqlCallbackTrait + 'static, + { + let mut cb_raw: *mut c_void = null_mut(); + { + match GLOBAL_TRACE_SQL_CALLBACK.lock() { + Ok(mut global_callback) => match cb_opt { + None => { + *global_callback = None; + cb_raw = global_trace_sql_callback as *mut c_void; + } + Some(cb) => { + let callback_box = Box::new(cb) as TraceSqlCallback; + *global_callback = Some(callback_box); + cb_raw = global_trace_sql_callback as *mut c_void; + } + }, + Err(error) => { + return Err(WCDBException::new_with_message( + ExceptionLevel::Error, + ExceptionCode::Error, + error.to_string(), + )); + } + } + } + unsafe { + WCDBRustDatabase_globalTraceSQL(cb_raw); + } + Ok(()) + } + + pub fn trace_sql(&self, cb_opt: Option) -> WCDBResult<()> + where + CB: TraceSqlCallbackTrait + 'static, + { + let mut closure_raw = null_mut(); + if let Some(cb) = cb_opt { + let closure_box = Box::new(Box::new(cb) as Box); + closure_raw = Box::into_raw(closure_box) as *mut c_void; + let mut value = self.trace_sql_ref.borrow_mut(); + *value = closure_raw; + } + unsafe { + WCDBRustDatabase_traceSQL(self.get_cpp_obj(), trace_sql_callback, closure_raw); + } + Ok(()) + } + + pub fn global_trace_exception(cb_opt: Option) -> WCDBResult<()> + where + CB: TraceExceptionCallbackTrait + 'static, + { + let mut cb_raw: *mut c_void = null_mut(); + { + match GLOBAL_TRACE_EXCEPTION_CALLBACK.lock() { + Ok(mut global_callback) => match cb_opt { + None => { + *global_callback = None; + cb_raw = global_trace_exception_callback as *mut c_void; + } + Some(cb) => { + let callback_box = Box::new(cb) as TraceExceptionCallback; + *global_callback = Some(callback_box); + cb_raw = global_trace_exception_callback as *mut c_void; + } + }, + Err(error) => { + return Err(WCDBException::new_with_message( + ExceptionLevel::Error, + ExceptionCode::Error, + error.to_string(), + )); + } + } + } + unsafe { + WCDBRustDatabase_globalTraceException(cb_raw); + } + Ok(()) + } + + pub fn trace_exception(&self, cb_opt: Option) -> WCDBResult<()> + where + CB: TraceExceptionCallbackTrait + 'static, + { + let mut closure_raw = null_mut(); + if let Some(cb) = cb_opt { + let closure_box = Box::new(Box::new(cb) as Box); + closure_raw = Box::into_raw(closure_box) as *mut c_void; + let mut value = self.trace_exception_ref.borrow_mut(); + *value = closure_raw; + } + unsafe { + WCDBRustDatabase_traceException( + self.get_cpp_obj(), + trace_exception_callback, + closure_raw, + ); + }; + Ok(()) + } + + pub fn set_tag(&self, tag: i64) { + unsafe { WCDBRustDatabase_setTag(self.get_cpp_obj(), tag) } + } + + /// Get the tag of the database. Tag is 0 by default. + pub fn get_tag(&self) -> i64 { + unsafe { WCDBRustDatabase_getTag(self.get_cpp_obj()) as i64 } + } + + pub fn get_all_rows_from_statement( + &self, + statement: &T, + ) -> WCDBResult>> { + let mut handle = self.get_handle(false); + let result = handle.prepared_with_main_statement(statement); + match result { + Ok(val) => { + let prepared_statement = Arc::clone(&val); + let result = prepared_statement.get_multi_rows(); + prepared_statement.finalize_statement(); + if self.auto_invalidate_handle() { + handle.invalidate(); + } + match result { + Ok(values) => { + let mut rows: Vec> = Vec::with_capacity(values.len()); + for x in values { + let mut item: Vec = Vec::with_capacity(x.len()); + for v in x { + item.push(v.clone()); + } + rows.push(item) + } + Ok(rows) + } + Err(error) => Err(error), + } + } + Err(error) => Err(error), + } + } + + pub fn get_value_from_statement(&self, statement: &T) -> WCDBResult { + let mut handle = self.get_handle(false); + let mut ret = Value::default(); + let result = handle.prepared_with_main_statement(statement); + match result { + Ok(val) => { + let prepared_statement = Arc::clone(&val); + prepared_statement.step()?; + if !prepared_statement.is_done() { + ret = prepared_statement.get_value(0); + } + prepared_statement.finalize_statement(); + if self.auto_invalidate_handle() { + handle.invalidate(); + } + Ok(ret) + } + Err(error) => { + if self.auto_invalidate_handle() { + handle.invalidate(); + } + Err(error) + } + } + } + + pub fn get_value_from_sql(&self, sql: &str) -> WCDBResult { + let mut handle = self.get_handle(false); + let result = handle.prepared_with_main_statement_and_sql(sql); + match result { + Ok(val) => { + let prepared_statement = Arc::clone(&val); + prepared_statement.step()?; + if !prepared_statement.is_done() { + let ret = prepared_statement.get_value(0); + prepared_statement.finalize_statement(); + if self.auto_invalidate_handle() { + handle.invalidate(); + } + Ok(ret) + } else { + Ok(Value::default()) + } + } + Err(error) => Err(error), + } + } + + pub fn get_values_from_sql(&self, sql: &str) -> WCDBResult>> { + let mut handle = self.get_handle(false); + let result = handle.prepared_with_main_statement_and_sql(sql); + match result { + Ok(val) => { + let mut ret_vec = Vec::new(); + let prepared_statement = Arc::clone(&val); + if !prepared_statement.is_done() { + let ret = prepared_statement.get_all_values()?; + prepared_statement.finalize_statement(); + if self.auto_invalidate_handle() { + handle.invalidate(); + } + ret_vec = ret; + } + Ok(ret_vec) + } + Err(error) => Err(error), + } + } + + pub fn get_objects_from_sql(&self, fields: Vec<&Field>, sql: &str) -> WCDBResult> { + let mut handle = self.get_handle(false); + let result = handle.prepared_with_main_statement_and_sql(sql); + match result { + Ok(val) => { + let mut ret_vec: Vec = Vec::new(); + let prepared_statement = Arc::clone(&val); + if prepared_statement.step().is_ok() { + while !prepared_statement.is_done() { + let ret = prepared_statement.get_one_object(&fields); + prepared_statement.finalize_statement(); + if self.auto_invalidate_handle() { + handle.invalidate(); + } + if let Ok(Some(val)) = ret { + ret_vec.push(val); + } + if prepared_statement.step().is_err() { + break; + } + } + } + Ok(ret_vec) + } + Err(error) => Err(error), + } + } + + pub fn add_tokenizer(&self, tokenizer: &str) { + let c_tokenizer = CString::new(tokenizer).unwrap_or_default(); + unsafe { + WCDBRustDatabase_addTokenizer(self.get_cpp_obj(), c_tokenizer.as_ptr()); + } + } + + pub fn config_pinyin_dict(pinyin_dict: &HashMap<&str, Vec<&str>>) { + if pinyin_dict.keys().len() == 0 { + return; + } + + let mut c_keys: Vec = Vec::new(); + let mut c_keys_ptr: Vec<*const c_char> = Vec::new(); + + let mut c_values: Vec> = Vec::new(); + let mut row_ptr_vec: Vec> = Vec::new(); + let mut c_values_ptr: Vec<*const *const c_char> = Vec::new(); + let mut values_len: Vec = Vec::new(); + + for (key, vals) in pinyin_dict { + let ck = key.to_cstring(); + c_keys_ptr.push(ck.as_ptr()); + c_keys.push(ck); + + let mut row_cstrings: Vec = Vec::new(); + let mut row_ptrs: Vec<*const c_char> = Vec::new(); + + for v in vals { + let cv = v.to_cstring(); + row_ptrs.push(cv.as_ptr()); + row_cstrings.push(cv); + } + + values_len.push(row_ptrs.len()); + row_ptr_vec.push(row_ptrs); + c_values.push(row_cstrings); + } + + for row_ptr in &row_ptr_vec { + c_values_ptr.push(row_ptr.as_ptr()); + } + + unsafe { + WCDBRustDatabase_configPinyinDict( + c_keys_ptr.as_ptr(), + c_values_ptr.as_ptr(), + values_len.as_ptr(), + c_keys_ptr.len(), + ); + } + } + + pub fn config_traditional_chinese_dict(traditional_chinese_dict: &HashMap<&str, &str>) { + if traditional_chinese_dict.keys().len() == 0 { + return; + } + + let mut key_buf: Vec = Vec::new(); + let mut value_buf: Vec = Vec::new(); + + let mut key_ptrs: Vec<*const c_char> = Vec::new(); + let mut value_ptrs: Vec<*const c_char> = Vec::new(); + for (k, v) in traditional_chinese_dict { + let ck = k.to_cstring(); + let cv = v.to_cstring(); + + key_ptrs.push(ck.as_ptr()); + value_ptrs.push(cv.as_ptr()); + + key_buf.push(ck); + value_buf.push(cv); + } + let len = key_ptrs.len(); + + unsafe { + WCDBRustDatabase_configTraditionalChineseDict( + key_ptrs.as_ptr(), + value_ptrs.as_ptr(), + len, + ); + } + } + + pub fn set_notification_when_corrupted(&self, monitor: Option) -> WCDBResult<()> + where + CB: CorruptionNotificationTrait + 'static, + { + let mut cb_raw: *mut c_void = null_mut(); + { + match GLOBAL_CORRUPTION_NOTIFICATION_CALLBACK.lock() { + Ok(mut global_callback) => match monitor { + None => { + *global_callback = None; + cb_raw = null_mut(); + } + Some(cb) => { + let callback_box = Box::new(cb) as CorruptionNotificationCallback; + *global_callback = Some(callback_box); + cb_raw = global_corruption_notification_callback_wrapper as *mut c_void; + } + }, + Err(error) => { + return Err(WCDBException::new_with_message( + ExceptionLevel::Error, + ExceptionCode::Error, + error.to_string(), + )); + } + } + } + unsafe { + WCDBRustDatabase_setNotificationWhenCorrupted(self.get_cpp_obj(), cb_raw); + } + Ok(()) + } + + pub fn check_if_corrupted(&self) -> bool { + unsafe { WCDBRustDatabase_checkIfCorrupted(self.get_cpp_obj()) } + } + + pub fn check_if_is_already_corrupted(&self) -> bool { + unsafe { WCDBRustDatabase_checkIfIsAlreadyCorrupted(self.get_cpp_obj()) } + } + + pub fn enable_auto_backup(&self, enable: bool) { + unsafe { WCDBRustDatabase_enableAutoBackup(self.get_cpp_obj(), enable) } + } + + pub fn backup(&self) -> WCDBResult<()> { + let ret = unsafe { WCDBRustDatabase_backup(self.get_cpp_obj()) }; + if ret { + Ok(()) + } else { + Err(self.create_exception()) + } + } + + pub fn check_table_should_be_backup( + &self, + filter: &T, + table_name: &str, + ) -> bool { + filter.table_should_be_backup(table_name) + } + + pub fn filter_backup(&self, filter: Option) -> WCDBResult<()> + where + CB: BackupFilterCallbackTrait + 'static, + { + let mut cb_raw: *const c_void = null_mut(); + { + match GLOBAL_BACKUP_FILTER_CALLBACK.lock() { + Ok(mut global_callback) => match filter { + None => { + *global_callback = None; + cb_raw = null_mut(); + } + Some(cb) => { + let callback_box = Box::new(cb) as BackupFilterCallback; + *global_callback = Some(callback_box); + cb_raw = backup_filter_callback_wrapper as *const c_void; + } + }, + Err(error) => { + return Err(WCDBException::new_with_message( + ExceptionLevel::Error, + ExceptionCode::Error, + error.to_string(), + )); + } + } + } + unsafe { WCDBRustDatabase_filterBackup(self.get_cpp_obj(), cb_raw) } + Ok(()) + } + + pub fn retrieve(&self, monitor: Option) -> WCDBResult + where + CB: ProgressMonitorTrait + 'static, + { + let mut score: f64 = 0f64; + let mut cb_raw: *const c_void = null_mut(); + { + match GLOBAL_PROGRESS_MONITOR_TRAIT_CALLBACK.lock() { + Ok(mut global_callback) => match monitor { + None => { + *global_callback = None; + cb_raw = null_mut(); + } + Some(cb) => { + let callback_box = Box::new(cb) as ProgressMonitorTraitCallback; + *global_callback = Some(callback_box); + cb_raw = retrieve_progress_monitor_trait_wrapper as *mut c_void; + } + }, + Err(error) => { + return Err(WCDBException::new_with_message( + ExceptionLevel::Error, + ExceptionCode::Error, + error.to_string(), + )); + } + } + } + + score = unsafe { WCDBRustDatabase_retrieve(self.get_cpp_obj(), cb_raw) as f64 }; + + if score < 0f64 { + Err(self.create_exception()) + } else { + Ok(score) + } + } + + pub fn deposit(&self) -> WCDBResult<()> { + let ret: bool = unsafe { WCDBRustDatabase_deposit(self.get_cpp_obj()) }; + if ret { + Ok(()) + } else { + Err(self.create_exception()) + } + } + + pub fn remove_deposited_files(&self) -> WCDBResult<()> { + let ret: bool = unsafe { WCDBRustDatabase_removeDepositedFiles(self.get_cpp_obj()) }; + if ret { + Ok(()) + } else { + Err(self.create_exception()) + } + } + + pub fn contain_deposited_files(&self) -> bool { + unsafe { WCDBRustDatabase_containDepositedFiles(self.get_cpp_obj()) } + } + + pub fn truncate_check_point(&self) -> WCDBResult<()> { + let ret: bool = unsafe { WCDBRustDatabase_truncateCheckpoint(self.get_cpp_obj()) }; + if ret { + Ok(()) + } else { + Err(self.create_exception()) + } + } + + pub fn set_auto_check_point_enable(&self, enable: bool) { + unsafe { WCDBRustDatabase_setAutoCheckpointEnable(self.get_cpp_obj(), enable) } + } +} + +#[derive(Debug, Default)] +#[repr(C)] +pub struct PerformanceInfo { + pub table_page_read_count: i32, + pub table_page_write_count: i32, + pub index_page_read_count: i32, + pub index_page_write_count: i32, + pub overflow_page_read_count: i32, + pub overflow_page_write_count: i32, + pub cost_in_nanoseconds: u64, +} + +pub enum CipherVersion { + DefaultVersion = 0, + Version1 = 1, + Version2 = 2, + Version3 = 3, + Version4 = 4, +} diff --git a/src/rust/wcdb/src/core/handle.rs b/src/rust/wcdb/src/core/handle.rs new file mode 100644 index 000000000..0f70fecea --- /dev/null +++ b/src/rust/wcdb/src/core/handle.rs @@ -0,0 +1,605 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::wcdb_exception::{ExceptionCode, ExceptionLevel, WCDBException, WCDBResult}; +use crate::chaincall::delete::Delete; +use crate::chaincall::insert::Insert; +use crate::chaincall::select::Select; +use crate::chaincall::update::Update; +use crate::core::database::Database; +use crate::core::handle_operation::HandleOperationTrait; +use crate::core::handle_orm_operation::{HandleORMOperation, HandleORMOperationTrait}; +use crate::core::prepared_statement::PreparedStatement; +use crate::orm::field::Field; +use crate::orm::table_binding::TableBinding; +use crate::winq::expression::Expression; +use crate::winq::ordering_term::OrderingTerm; +use crate::winq::statement::StatementTrait; +use std::cell::RefCell; +use std::ffi::{c_char, c_int, c_void, CString}; +use std::sync::Arc; + +extern "C" { + fn WCDBRustHandle_getError(cpp_obj: *mut c_void) -> *mut c_void; + + fn WCDBRustHandle_getMainStatement(cpp_obj: *mut c_void) -> *mut c_void; + + fn WCDBRustHandle_tableExist(cpp_obj: *mut c_void, table_name: *const c_char) -> c_int; + + fn WCDBRustHandle_execute(cpp_obj: *mut c_void, statement: *mut c_void) -> bool; + + fn WCDBRustHandle_executeSQL(cpp_obj: *mut c_void, sql: *const c_char) -> bool; + + fn WCDBRustHandle_getChanges(cpp_obj: *mut c_void) -> c_int; + + fn WCDBRustHandle_getLastInsertRowid(cpp_obj: *mut c_void) -> i64; + + fn WCDBRustHandle_runTransaction( + cpp_obj: *mut c_void, + transaction_callback: extern "C" fn( + cb_raw: *mut c_void, + cpp_handle_raw: *mut c_void, + ) -> bool, + cb_raw: *mut c_void, + rust_handle_raw: *mut c_void, + ) -> bool; +} + +extern "C" fn transaction_callback(cb_raw: *mut c_void, rust_handle_raw: *mut c_void) -> bool { + let handle = unsafe { *(rust_handle_raw as *const &Handle) }; + let closure: Box bool>> = + unsafe { Box::from_raw(cb_raw as *mut Box bool>) }; + closure(handle) +} + +pub struct HandleInner { + handle_orm_operation: HandleORMOperation, + main_statement: Option>, + write_hint: bool, +} + +impl CppObjectConvertibleTrait for HandleInner { + fn as_cpp_object(&self) -> &CppObject { + self.handle_orm_operation.as_cpp_object() + } +} + +impl CppObjectTrait for HandleInner { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.handle_orm_operation.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.handle_orm_operation.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.handle_orm_operation.release_cpp_object() + } +} + +impl HandleInner { + pub fn get_cpp_handle(&mut self, database: &Database) -> WCDBResult<*mut c_void> { + let mut cpp_obj = self.get_cpp_obj(); + if cpp_obj.is_null() { + self.set_cpp_obj(Database::get_handle_raw( + CppObject::get(database), + self.write_hint, + )); + if self.get_cpp_obj().is_null() { + return Err(database.create_exception()); + } + } + Ok(self.get_cpp_obj()) + } + + pub fn invalidate(&mut self) { + self.main_statement.take(); + if !self.handle_orm_operation.get_cpp_obj().is_null() { + self.handle_orm_operation.release_cpp_object(); + self.write_hint = false; + } + } + + pub fn get_changes(&mut self, database: &Database) -> WCDBResult { + Ok(unsafe { WCDBRustHandle_getChanges(self.get_cpp_handle(database)?) }) + } + + pub fn prepared_with_main_statement( + &mut self, + database: &Database, + statement: &T, + ) -> WCDBResult> { + if self.main_statement.is_none() { + match self.get_cpp_handle(database) { + Ok(handle_cpp) => { + let cpp_obj = unsafe { WCDBRustHandle_getMainStatement(handle_cpp) }; + let mut prepared_statement = PreparedStatement::new(Some(cpp_obj)); + prepared_statement.auto_finalize = true; + self.main_statement = Some(Arc::new(prepared_statement)); + } + Err(error) => { + return Err(error.into()); + } + } + } + match self.main_statement.as_ref() { + None => Err(WCDBException::new_with_message( + ExceptionLevel::Error, + ExceptionCode::Error, + String::from( + "Method :prepared_with_main_statement error, cause :main_statement is none", + ), + )), + Some(main_statement) => { + main_statement.prepare(statement)?; + Ok(main_statement.clone()) + } + } + } + + pub fn prepared_with_main_statement_and_sql( + &mut self, + database: &Database, + sql: &str, + ) -> WCDBResult> { + if self.main_statement.is_none() { + let cpp_obj = + unsafe { WCDBRustHandle_getMainStatement(self.get_cpp_handle(database)?) }; + let mut prepared_statement = PreparedStatement::new(Some(cpp_obj)); + prepared_statement.auto_finalize = true; + self.main_statement = Some(Arc::new(prepared_statement)); + } + match self.main_statement.as_ref() { + None => { + Err(WCDBException::new_with_message( + ExceptionLevel::Error, + ExceptionCode::Error, + String::from("Method :prepared_with_main_statement_and_sql error, cause :main_statement is none"), + )) + } + Some(statement) => { + statement.prepare_with_sql(sql)?; + Ok(statement.clone()) + } + } + } +} + +pub struct Handle<'a> { + handle_inner: Arc>, + database: &'a Database, +} + +impl<'a> CppObjectConvertibleTrait for Handle<'a> { + fn as_cpp_object(&self) -> &CppObject { + // todo qixinbing 这里用的是占位,待完善 + static DUMMY: CppObject = CppObject { + cpp_obj: std::ptr::null_mut(), + }; + &DUMMY + } +} + +impl<'a> CppObjectTrait for Handle<'a> { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + let mut handle_inner = self.handle_inner.borrow_mut(); + handle_inner.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + let handle_inner_lock = self.handle_inner.borrow_mut(); + handle_inner_lock.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + let mut handle_inner = self.handle_inner.borrow_mut(); + handle_inner.release_cpp_object(); + } +} + +impl<'a> HandleOperationTrait for Handle<'a> { + fn get_handle(&self, _: bool) -> Handle { + Handle { + handle_inner: self.handle_inner.clone(), + database: self.database, + } + } + + fn auto_invalidate_handle(&self) -> bool { + false + } + + // todo qixibing 考虑该方法和 handle_operation 同名方法保留哪个? + fn run_transaction bool>(&self, closure: F) -> WCDBResult<()> { + let mut handle = self.get_handle(true); + let closure_box: Box bool>> = Box::new(Box::new(closure)); + let closure_raw = Box::into_raw(closure_box) as *mut c_void; + let rust_handle_raw = unsafe { &(&handle) as *const &Handle as *mut c_void }; + let mut exception_opt = None; + if !unsafe { + WCDBRustHandle_runTransaction( + handle.get_cpp_handle()?, + transaction_callback, + closure_raw, + rust_handle_raw, + ) + } { + exception_opt = Some(handle.create_exception()); + } + if self.auto_invalidate_handle() { + handle.invalidate(); + } + match exception_opt { + None => Ok(()), + Some(exception) => Err(exception), + } + } + + // todo qixibing 考虑该方法和 handle_operation 同名方法保留哪个? + fn execute(&self, statement: &T) -> WCDBResult<()> { + let mut handle = self.get_handle(true); + let mut exception_opt = None; + if !Handle::execute_inner(handle.get_cpp_handle()?, statement) { + exception_opt = Some(handle.create_exception()); + } + if self.auto_invalidate_handle() { + handle.invalidate(); + } + match exception_opt { + None => Ok(()), + Some(exception) => Err(exception), + } + } + + // todo qixibing 考虑该方法和 handle_operation 同名方法保留哪个? + fn execute_sql(&self, sql: &str) -> WCDBResult<()> { + let mut handle = self.get_handle(true); + let mut exception_opt = None; + if !Handle::execute_sql(handle.get_cpp_handle()?, sql) { + exception_opt = Some(handle.create_exception()); + } + if self.auto_invalidate_handle() { + handle.invalidate(); + } + match exception_opt { + None => Ok(()), + Some(exception) => Err(exception), + } + } +} + +impl<'a> HandleORMOperationTrait for Handle<'a> { + fn create_table>( + &self, + table_name: &str, + binding: &R, + ) -> WCDBResult { + let handle_inner = self.handle_inner.borrow(); + handle_inner + .handle_orm_operation + .create_table(self.get_handle(true), table_name, binding) + } + + fn create_virtual_table>( + &self, + table_name: &str, + binding: &R, + ) -> WCDBResult { + let handle_inner = self.handle_inner.borrow(); + handle_inner.handle_orm_operation.create_virtual_table( + self.get_handle(true), + table_name, + binding, + ) + } + + fn table_exist(&self, table_name: &str) -> WCDBResult { + let handle_inner = self.handle_inner.borrow(); + handle_inner.handle_orm_operation.table_exist( + self.get_handle(true), + self.auto_invalidate_handle(), + table_name, + ) + } + + fn drop_table(&self, table_name: &str) -> WCDBResult<()> { + let handle_inner = self.handle_inner.borrow(); + handle_inner.handle_orm_operation.drop_table( + self.get_handle(true), + self.auto_invalidate_handle(), + table_name, + ) + } + + fn prepare_insert(&self) -> Insert { + let handle_inner = self.handle_inner.borrow(); + handle_inner + .handle_orm_operation + .prepare_insert(self.get_handle(true), self.auto_invalidate_handle()) + } + + fn prepare_update(&self) -> Update { + let handle_inner = self.handle_inner.borrow(); + handle_inner + .handle_orm_operation + .prepare_update(self.get_handle(true), self.auto_invalidate_handle()) + } + + fn prepare_select(&self) -> Select { + let handle_inner = self.handle_inner.borrow(); + handle_inner + .handle_orm_operation + .prepare_select(self.get_handle(true), self.auto_invalidate_handle()) + } + + fn prepare_delete(&self) -> Delete { + let handle_inner = self.handle_inner.borrow(); + handle_inner + .handle_orm_operation + .prepare_delete(self.get_handle(true), self.auto_invalidate_handle()) + } + + fn insert_object( + &self, + object: T, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + let handle_inner = self.handle_inner.borrow(); + handle_inner.handle_orm_operation.insert_object( + self.get_handle(true), + self.auto_invalidate_handle(), + object, + fields, + table_name, + ) + } + + fn insert_or_replace_object( + &self, + object: T, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + let handle_inner = self.handle_inner.borrow(); + handle_inner.handle_orm_operation.insert_or_replace_object( + self.get_handle(true), + self.auto_invalidate_handle(), + object, + fields, + table_name, + ) + } + + fn insert_or_ignore_object( + &self, + object: T, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + let handle_inner = self.handle_inner.borrow(); + handle_inner.handle_orm_operation.insert_or_ignore_object( + self.get_handle(true), + self.auto_invalidate_handle(), + object, + fields, + table_name, + ) + } + + fn insert_objects( + &self, + objects: Vec, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + let handle_inner = self.handle_inner.borrow(); + handle_inner.handle_orm_operation.insert_objects( + self.get_handle(true), + self.auto_invalidate_handle(), + objects, + fields, + table_name, + ) + } + + fn insert_or_replace_objects( + &self, + objects: Vec, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + let handle_inner = self.handle_inner.borrow(); + handle_inner.handle_orm_operation.insert_or_replace_objects( + self.get_handle(true), + self.auto_invalidate_handle(), + objects, + fields, + table_name, + ) + } + + fn insert_or_ignore_objects( + &self, + objects: Vec, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + let handle_inner = self.handle_inner.borrow(); + handle_inner.handle_orm_operation.insert_or_ignore_objects( + self.get_handle(true), + self.auto_invalidate_handle(), + objects, + fields, + table_name, + ) + } + + fn delete_objects( + &self, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + let handle_inner = self.handle_inner.borrow(); + handle_inner.handle_orm_operation.delete_objects( + self.get_handle(true), + self.auto_invalidate_handle(), + table_name, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } + + fn update_object( + &self, + object: T, + fields: Vec<&Field>, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + let handle_inner = self.handle_inner.borrow(); + handle_inner.handle_orm_operation.update_object( + self.get_handle(true), + self.auto_invalidate_handle(), + object, + fields, + table_name, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } + + fn get_first_object( + &self, + fields: Vec<&Field>, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + offset_opt: Option, + ) -> WCDBResult> { + let handle_inner = self.handle_inner.borrow(); + handle_inner.handle_orm_operation.get_first_object( + self.get_handle(true), + self.auto_invalidate_handle(), + fields, + table_name, + condition_opt, + order_opt, + offset_opt, + ) + } + + fn get_all_objects( + &self, + fields: Vec<&Field>, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult> { + let handle_inner = self.handle_inner.borrow(); + handle_inner.handle_orm_operation.get_all_objects( + self.get_handle(true), + self.auto_invalidate_handle(), + fields, + table_name, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } +} + +impl<'a> Handle<'a> { + pub fn new(database: &'a Database, write_hint: bool) -> Self { + let handle_inner = Arc::new(RefCell::new(HandleInner { + handle_orm_operation: HandleORMOperation::new(None), + main_statement: None, + write_hint, + })); + Self { + handle_inner, + database, + } + } + + pub fn new_with_obj(cpp_obj: *mut c_void, database: &'a Database) -> Self { + let handle_inner = Arc::new(RefCell::new(HandleInner { + handle_orm_operation: HandleORMOperation::new(Some(cpp_obj)), + main_statement: None, + write_hint: false, + })); + Self { + handle_inner, + database, + } + } + + pub fn get_cpp_handle(&self) -> WCDBResult<*mut c_void> { + let mut handle_inner = self.handle_inner.borrow_mut(); + handle_inner.get_cpp_handle(self.database) + } + + pub fn create_exception(&self) -> WCDBException { + WCDBException::create_exception(unsafe { WCDBRustHandle_getError(self.get_cpp_obj()) }) + } + + pub fn invalidate(&self) { + let mut handle_inner = self.handle_inner.borrow_mut(); + handle_inner.invalidate(); + } + + pub fn close(&self) { + self.invalidate(); + } + + pub fn get_changes(&self) -> WCDBResult { + let mut handle_inner = self.handle_inner.borrow_mut(); + handle_inner.get_changes(self.database) + } + + pub fn get_last_inserted_row_id(&self) -> WCDBResult { + Ok(unsafe { WCDBRustHandle_getLastInsertRowid(self.get_cpp_handle()?) }) + } + + pub fn prepared_with_main_statement( + &self, + statement: &T, + ) -> WCDBResult> { + let mut handle_inner = self.handle_inner.borrow_mut(); + handle_inner.prepared_with_main_statement(self.database, statement) + } + + pub fn prepared_with_main_statement_and_sql( + &self, + sql: &str, + ) -> WCDBResult> { + let mut handle_inner = self.handle_inner.borrow_mut(); + handle_inner.prepared_with_main_statement_and_sql(self.database, sql) + } + + pub fn table_exist(cpp_obj: *mut c_void, table_name: &str) -> i32 { + let c_table_name = CString::new(table_name).unwrap_or_default(); + unsafe { WCDBRustHandle_tableExist(cpp_obj, c_table_name.as_ptr()) } + } + + pub fn execute_inner(cpp_obj: *mut c_void, statement: &T) -> bool { + unsafe { WCDBRustHandle_execute(cpp_obj, CppObject::get(statement)) } + } + pub fn execute_sql(cpp_obj: *mut c_void, sql: &str) -> bool { + let c_sql = CString::new(sql).unwrap_or_default(); + unsafe { WCDBRustHandle_executeSQL(cpp_obj, c_sql.as_ptr()) } + } +} diff --git a/src/rust/wcdb/src/core/handle_operation.rs b/src/rust/wcdb/src/core/handle_operation.rs new file mode 100644 index 000000000..dd42c72fb --- /dev/null +++ b/src/rust/wcdb/src/core/handle_operation.rs @@ -0,0 +1,140 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::wcdb_exception::WCDBResult; +use crate::core::handle::Handle; +use crate::winq::statement::StatementTrait; +use std::ffi::c_void; + +extern "C" { + fn WCDBRustHandle_runTransaction( + cpp_obj: *mut c_void, + transaction_callback: extern "C" fn( + cb_raw: *mut c_void, + cpp_handle_raw: *mut c_void, + ) -> bool, + cb_raw: *mut c_void, + rust_handle_raw: *mut c_void, + ) -> bool; +} + +extern "C" fn transaction_callback(cb_raw: *mut c_void, rust_handle_raw: *mut c_void) -> bool { + let handle = unsafe { *(rust_handle_raw as *const &Handle) }; + let closure: Box bool>> = + unsafe { Box::from_raw(cb_raw as *mut Box bool>) }; + closure(handle) +} + +#[derive(Debug)] +pub struct HandleOperation { + cpp_obj: CppObject, +} + +pub trait HandleOperationTrait: CppObjectTrait { + fn get_handle(&self, write_hint: bool) -> Handle; + + fn auto_invalidate_handle(&self) -> bool; + + fn run_transaction bool>(&self, callback: F) -> WCDBResult<()>; + + fn execute(&self, statement: &T) -> WCDBResult<()>; + + fn execute_sql(&self, sql: &str) -> WCDBResult<()>; +} + +impl CppObjectConvertibleTrait for HandleOperation { + fn as_cpp_object(&self) -> &CppObject { + self.cpp_obj.as_cpp_object() + } +} + +impl CppObjectTrait for HandleOperation { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + *self.cpp_obj = cpp_obj; + } + + fn get_cpp_obj(&self) -> *mut c_void { + *self.cpp_obj + } + + fn release_cpp_object(&mut self) { + self.cpp_obj.release_cpp_object(); + } +} + +impl HandleOperation { + pub fn new(cpp_obj_opt: Option<*mut c_void>) -> Self { + HandleOperation { + cpp_obj: CppObject::new(cpp_obj_opt), + } + } + + pub(crate) fn run_transaction bool>( + &self, + handle: Handle, + auto_invalidate_handle: bool, + callback: F, + ) -> WCDBResult<()> { + let mut handle = handle; + let closure_box: Box bool>> = Box::new(Box::new(callback)); + let closure_raw = Box::into_raw(closure_box) as *mut c_void; + let rust_handle_raw = unsafe { &(&handle) as *const &Handle as *mut c_void }; + let mut exception_opt = None; + if !unsafe { + WCDBRustHandle_runTransaction( + handle.get_cpp_handle()?, + transaction_callback, + closure_raw, + rust_handle_raw, + ) + } { + exception_opt = Some(handle.create_exception()); + } + if auto_invalidate_handle { + handle.invalidate(); + } + match exception_opt { + None => Ok(()), + Some(exception) => Err(exception), + } + } + + pub(crate) fn execute( + &self, + handle: Handle, + auto_invalidate_handle: bool, + statement: &T, + ) -> WCDBResult<()> { + let mut handle = handle; + let mut exception_opt = None; + if !Handle::execute_inner(handle.get_cpp_handle()?, statement) { + exception_opt = Some(handle.create_exception()); + } + if auto_invalidate_handle { + handle.invalidate(); + } + match exception_opt { + None => Ok(()), + Some(exception) => Err(exception), + } + } + + pub(crate) fn execute_sql( + &self, + handle: Handle, + auto_invalidate_handle: bool, + sql: &str, + ) -> WCDBResult<()> { + let mut handle = handle; + let mut exception_opt = None; + if !Handle::execute_sql(handle.get_cpp_handle()?, sql) { + exception_opt = Some(handle.create_exception()); + } + if auto_invalidate_handle { + handle.invalidate(); + } + match exception_opt { + None => Ok(()), + Some(exception) => Err(exception), + } + } +} diff --git a/src/rust/wcdb/src/core/handle_orm_operation.rs b/src/rust/wcdb/src/core/handle_orm_operation.rs new file mode 100644 index 000000000..6967b35cb --- /dev/null +++ b/src/rust/wcdb/src/core/handle_orm_operation.rs @@ -0,0 +1,493 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::wcdb_exception::WCDBResult; +use crate::chaincall::delete::Delete; +use crate::chaincall::insert::Insert; +use crate::chaincall::select::Select; +use crate::chaincall::update::Update; +use crate::core::handle::Handle; +use crate::core::handle_operation::{HandleOperation, HandleOperationTrait}; +use crate::orm::field::Field; +use crate::orm::table_binding::TableBinding; +use crate::winq::expression::Expression; +use crate::winq::ordering_term::OrderingTerm; +use crate::winq::statement::StatementTrait; +use crate::winq::statement_drop_table::StatementDropTable; +use std::ffi::c_void; + +#[derive(Debug)] +pub struct HandleORMOperation { + handle_operation: HandleOperation, +} + +pub trait HandleORMOperationTrait { + fn create_table>( + &self, + table_name: &str, + binding: &R, + ) -> WCDBResult; + + fn create_virtual_table>( + &self, + table_name: &str, + binding: &R, + ) -> WCDBResult; + + fn table_exist(&self, table_name: &str) -> WCDBResult; + + fn drop_table(&self, table_name: &str) -> WCDBResult<()>; + + fn prepare_insert(&self) -> Insert; + + fn prepare_update(&self) -> Update; + + fn prepare_select(&self) -> Select; + + fn prepare_delete(&self) -> Delete; + + fn insert_object( + &self, + object: T, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()>; + + fn insert_or_replace_object( + &self, + object: T, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()>; + + fn insert_or_ignore_object( + &self, + object: T, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()>; + + fn insert_objects( + &self, + objects: Vec, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()>; + + fn insert_or_replace_objects( + &self, + objects: Vec, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()>; + + fn insert_or_ignore_objects( + &self, + objects: Vec, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()>; + + fn delete_objects( + &self, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()>; + + fn update_object( + &self, + object: T, + fields: Vec<&Field>, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()>; + + fn get_first_object( + &self, + fields: Vec<&Field>, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + offset_opt: Option, + ) -> WCDBResult>; + + fn get_all_objects( + &self, + fields: Vec<&Field>, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult>; +} + +impl CppObjectTrait for HandleORMOperation { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.handle_operation.set_cpp_obj(cpp_obj) + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.handle_operation.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.handle_operation.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for HandleORMOperation { + fn as_cpp_object(&self) -> &CppObject { + self.handle_operation.as_cpp_object() + } +} + +impl HandleORMOperation { + pub fn new(cpp_obj_opt: Option<*mut c_void>) -> Self { + HandleORMOperation { + handle_operation: HandleOperation::new(cpp_obj_opt), + } + } + + pub(crate) fn run_transaction bool>( + &self, + handle: Handle, + auto_invalidate_handle: bool, + callback: F, + ) -> WCDBResult<()> { + self.handle_operation + .run_transaction(handle, auto_invalidate_handle, callback) + } + + pub(crate) fn execute( + &self, + handle: Handle, + auto_invalidate_handle: bool, + statement: &T, + ) -> WCDBResult<()> { + self.handle_operation + .execute(handle, auto_invalidate_handle, statement) + } + + pub(crate) fn execute_sql( + &self, + handle: Handle, + auto_invalidate_handle: bool, + sql: &str, + ) -> WCDBResult<()> { + self.handle_operation + .execute_sql(handle, auto_invalidate_handle, sql) + } + + pub(crate) fn create_table>( + &self, + handle: Handle, + table_name: &str, + binding: &R, + ) -> WCDBResult { + binding.base_binding().create_table(table_name, handle) + } + + pub(crate) fn create_virtual_table>( + &self, + handle: Handle, + table_name: &str, + binding: &R, + ) -> WCDBResult { + binding + .base_binding() + .create_virtual_table(table_name, handle) + } + + pub(crate) fn table_exist( + &self, + handle: Handle, + auto_invalidate_handle: bool, + table_name: &str, + ) -> WCDBResult { + let mut handle = handle; + let ret = Handle::table_exist(handle.get_cpp_handle()?, table_name); + let mut exception_opt = None; + if ret > 1 { + exception_opt = Some(handle.create_exception()); + } + if auto_invalidate_handle { + handle.invalidate(); + } + if exception_opt.is_some() { + match exception_opt { + None => {} + Some(ex) => { + return Err(ex); + } + } + } + Ok(ret == 1) + } + + pub(crate) fn drop_table( + &self, + handle: Handle, + auto_invalidate_handle: bool, + table_name: &str, + ) -> WCDBResult<()> { + let statement = StatementDropTable::new(); + self.handle_operation.execute( + handle, + auto_invalidate_handle, + statement.drop_table(table_name).if_exist(), + ) + } + + pub(crate) fn prepare_insert<'a, T>( + &self, + handle: Handle<'a>, + auto_invalidate_handle: bool, + ) -> Insert<'a, T> { + Insert::new(handle, false, auto_invalidate_handle) + } + + pub(crate) fn prepare_update<'a, T>( + &self, + handle: Handle<'a>, + auto_invalidate_handle: bool, + ) -> Update<'a, T> { + Update::new(handle, false, auto_invalidate_handle) + } + + pub(crate) fn prepare_select<'a, T>( + &self, + handle: Handle<'a>, + auto_invalidate_handle: bool, + ) -> Select<'a, T> { + Select::new(handle, false, auto_invalidate_handle) + } + + pub(crate) fn prepare_delete<'a>( + &self, + handle: Handle<'a>, + auto_invalidate_handle: bool, + ) -> Delete<'a> { + Delete::new(handle, false, auto_invalidate_handle) + } + + pub(crate) fn insert_object( + &self, + handle: Handle, + auto_invalidate_handle: bool, + object: T, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + self.prepare_insert::(handle, auto_invalidate_handle) + .into_table(table_name) + .value(object) + .on_fields(fields) + .execute()?; + Ok(()) + } + + pub(crate) fn insert_or_replace_object( + &self, + handle: Handle, + auto_invalidate_handle: bool, + object: T, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + self.prepare_insert::(handle, auto_invalidate_handle) + .or_replace() + .into_table(table_name) + .value(object) + .on_fields(fields) + .execute()?; + Ok(()) + } + + pub(crate) fn insert_or_ignore_object( + &self, + handle: Handle, + auto_invalidate_handle: bool, + object: T, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + self.prepare_insert::(handle, auto_invalidate_handle) + .or_ignore() + .into_table(table_name) + .value(object) + .on_fields(fields) + .execute()?; + Ok(()) + } + + pub(crate) fn insert_objects( + &self, + handle: Handle, + auto_invalidate_handle: bool, + objects: Vec, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + self.prepare_insert::(handle, auto_invalidate_handle) + .into_table(table_name) + .values(objects) + .on_fields(fields) + .execute()?; + Ok(()) + } + + pub(crate) fn insert_or_replace_objects( + &self, + handle: Handle, + auto_invalidate_handle: bool, + objects: Vec, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + self.prepare_insert::(handle, auto_invalidate_handle) + .or_replace() + .into_table(table_name) + .values(objects) + .on_fields(fields) + .execute()?; + Ok(()) + } + + pub(crate) fn insert_or_ignore_objects( + &self, + handle: Handle, + auto_invalidate_handle: bool, + objects: Vec, + fields: Vec<&Field>, + table_name: &str, + ) -> WCDBResult<()> { + self.prepare_insert::(handle, auto_invalidate_handle) + .or_ignore() + .into_table(table_name) + .values(objects) + .on_fields(fields) + .execute()?; + Ok(()) + } + + pub(crate) fn delete_objects( + &self, + handle: Handle, + auto_invalidate_handle: bool, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + let delete = self.prepare_delete(handle, auto_invalidate_handle); + delete.from_table(table_name); + if let Some(condition) = condition_opt { + delete.where_(&condition); + } + if let Some(order) = order_opt { + delete.order_by(vec![order]); + } + if let Some(limit) = limit_opt { + delete.limit(limit); + } + if let Some(offset) = offset_opt { + delete.offset(offset); + } + delete.execute()?; + Ok(()) + } + + pub(crate) fn update_object( + &self, + handle: Handle, + auto_invalidate_handle: bool, + object: T, + fields: Vec<&Field>, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + let update = self.prepare_update::(handle, auto_invalidate_handle); + update.table(table_name); + update.set(fields); + update.to_object(object); + if let Some(condition) = condition_opt { + update.where_(&condition); + } + if let Some(order) = order_opt { + update.order_by(vec![order]); + } + if let Some(limit) = limit_opt { + update.limit(limit); + } + if let Some(offset) = offset_opt { + update.offset(offset); + } + update.execute()?; + Ok(()) + } + + pub(crate) fn get_first_object( + &self, + handle: Handle, + auto_invalidate_handle: bool, + fields: Vec<&Field>, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + offset_opt: Option, + ) -> WCDBResult> { + let select = self.prepare_select::(handle, auto_invalidate_handle); + select.select(fields); + select.from(table_name); + if let Some(condition) = condition_opt { + select.where_(&condition); + } + if let Some(order) = order_opt { + select.order_by(vec![order]); + } + select.limit(1); + if let Some(offset) = offset_opt { + select.offset(offset); + } + select.first_object() + } + + pub(crate) fn get_all_objects( + &self, + handle: Handle, + auto_invalidate_handle: bool, + fields: Vec<&Field>, + table_name: &str, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult> { + let select = self.prepare_select::(handle, auto_invalidate_handle); + select.select(fields); + select.from(table_name); + if let Some(condition) = condition_opt { + select.where_(&condition); + } + if let Some(order) = order_opt { + select.order_by(vec![order]); + } + if let Some(limit) = limit_opt { + select.limit(limit); + } + if let Some(offset) = offset_opt { + select.offset(offset); + } + select.all_objects() + } +} diff --git a/src/rust/wcdb/src/core/mod.rs b/src/rust/wcdb/src/core/mod.rs new file mode 100644 index 000000000..7ff5fb4d2 --- /dev/null +++ b/src/rust/wcdb/src/core/mod.rs @@ -0,0 +1,8 @@ +pub mod database; +pub mod handle; +pub mod handle_operation; +pub mod handle_orm_operation; +pub mod prepared_statement; +pub mod table; +pub mod table_operation; +pub mod table_orm_operation; diff --git a/src/rust/wcdb/src/core/prepared_statement.rs b/src/rust/wcdb/src/core/prepared_statement.rs new file mode 100644 index 000000000..8427a47fc --- /dev/null +++ b/src/rust/wcdb/src/core/prepared_statement.rs @@ -0,0 +1,505 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::value::Value; +use crate::base::wcdb_exception::{WCDBException, WCDBResult}; +use crate::orm::field::Field; +use crate::utils::{ToCString, ToCow}; +use crate::winq::column_type::ColumnType; +use crate::winq::statement::StatementTrait; +use core::ffi::c_size_t; +use std::ffi::{c_char, c_double, c_int, c_void, CString}; +use std::slice; +use std::sync::atomic::{AtomicI32, Ordering}; +use std::sync::Arc; + +extern "C" { + fn WCDBRustHandleStatement_getError(cpp_obj: *mut c_void) -> *mut c_void; + fn WCDBRustHandleStatement_prepare(cpp_obj: *mut c_void, statement: *mut c_void) -> bool; + fn WCDBRustHandleStatement_prepareSQL(cpp_obj: *mut c_void, sql: *const c_char) -> bool; + fn WCDBRustHandleStatement_step(cpp_obj: *mut c_void) -> bool; + fn WCDBRustHandleStatement_reset(cpp_obj: *mut c_void); + fn WCDBRustHandleStatement_finalize(cpp_obj: *mut c_void); + fn WCDBRustHandleStatement_isDone(cpp_obj: *mut c_void) -> bool; + fn WCDBRustHandleStatement_bindInteger(cpp_obj: *mut c_void, value: i64, index: c_size_t); + fn WCDBRustHandleStatement_bindDouble(cpp_obj: *mut c_void, value: c_double, index: c_size_t); + fn WCDBRustHandleStatement_bindText( + cpp_obj: *mut c_void, + value: *const c_char, + index: c_size_t, + ); + fn WCDBRustHandleStatement_bindBLOB( + cpp_obj: *mut c_void, + value: *const u8, + value_len: c_size_t, + index: c_size_t, + ); + fn WCDBRustHandleStatement_bindNull(cpp_obj: *mut c_void, index: c_size_t); + fn WCDBRustHandleStatement_getInteger(cpp_obj: *mut c_void, index: c_size_t) -> i64; + fn WCDBRustHandleStatement_getDouble(cpp_obj: *mut c_void, index: c_size_t) -> c_double; + fn WCDBRustHandleStatement_getText(cpp_obj: *mut c_void, index: c_size_t) -> *const c_char; + fn WCDBRustHandleStatement_getColumnType(cpp_obj: *mut c_void, index: c_int) -> c_int; + fn WCDBRustHandleStatement_getColumnCount(cpp_obj: *mut c_void) -> c_int; + + fn WCDBRustHandleStatement_getBLOB( + cpp_obj: *mut c_void, + index: c_size_t, + data: *mut *const std::ffi::c_uchar, + data_size: *mut i64, + ); +} + +pub struct PreparedStatement { + cpp_obj: CppObject, + pub auto_finalize: bool, + column_count: AtomicI32, +} + +impl CppObjectTrait for PreparedStatement { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.cpp_obj.set_cpp_obj(cpp_obj) + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.cpp_obj.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.cpp_obj.release_cpp_object() + } +} + +impl CppObjectConvertibleTrait for PreparedStatement { + fn as_cpp_object(&self) -> &CppObject { + self.cpp_obj.as_cpp_object() + } +} + +impl PreparedStatement { + pub fn new(cpp_obj_opt: Option<*mut c_void>) -> PreparedStatement { + PreparedStatement { + cpp_obj: CppObject::new(cpp_obj_opt), + auto_finalize: false, + column_count: AtomicI32::new(-1), + } + } + + pub fn create_exception(&self) -> WCDBException { + WCDBException::create_exception(unsafe { + WCDBRustHandleStatement_getError(self.get_cpp_obj()) + }) + } + + pub fn bind_bool(&self, value: bool, index: usize) { + self.bind_i64(if value { 1 } else { 0 }, index); + } + + pub fn bind_bool_opt(&self, value: Option<&bool>, index: usize) { + if let Some(v) = value { + self.bind_bool(*v, index); + } else { + self.bind_null(index); + } + } + + pub fn bind_i8(&self, value: i8, index: usize) { + self.bind_i64(value as i64, index); + } + + pub fn bind_i8_opt(&self, value: Option<&i8>, index: usize) { + if let Some(v) = value { + self.bind_i8(*v, index); + } else { + self.bind_null(index); + } + } + + pub fn bind_i16(&self, value: i16, index: usize) { + self.bind_i64(value as i64, index); + } + + pub fn bind_i16_opt(&self, value: Option<&i16>, index: usize) { + if let Some(v) = value { + self.bind_i16(*v, index); + } else { + self.bind_null(index); + } + } + + pub fn bind_i32(&self, value: i32, index: usize) { + self.bind_i64(value as i64, index); + } + + pub fn bind_i32_opt(&self, value: Option<&i32>, index: usize) { + if let Some(v) = value { + self.bind_i32(*v, index); + } else { + self.bind_null(index); + } + } + + pub fn bind_i64(&self, value: i64, index: usize) { + unsafe { WCDBRustHandleStatement_bindInteger(*self.cpp_obj, value, index) } + } + + pub fn bind_i64_opt(&self, value: Option<&i64>, index: usize) { + if let Some(v) = value { + self.bind_i64(*v, index); + } else { + self.bind_null(index); + } + } + + pub fn bind_f32(&self, value: f32, index: usize) { + self.bind_f64(value as f64, index); + } + + pub fn bind_f32_opt(&self, value: Option<&f32>, index: usize) { + if let Some(v) = value { + self.bind_f32(*v, index); + } else { + self.bind_null(index); + } + } + + pub fn bind_f64(&self, value: f64, index: usize) { + unsafe { WCDBRustHandleStatement_bindDouble(*self.cpp_obj, value, index) } + } + + pub fn bind_f64_opt(&self, value: Option<&f64>, index: usize) { + if let Some(v) = value { + self.bind_f64(*v, index); + } else { + self.bind_null(index); + } + } + + pub fn bind_text(&self, value: &str, index: usize) { + let c_path = CString::new(value).unwrap_or_default(); + unsafe { WCDBRustHandleStatement_bindText(*self.cpp_obj, c_path.as_ptr(), index) } + } + + pub fn bind_text_opt(&self, value: Option<&String>, index: usize) { + if let Some(v) = value { + self.bind_text(v.as_str(), index); + } else { + self.bind_null(index); + } + } + + pub fn bind_blob(&self, value: &Vec, index: usize) { + let len = value.len(); + unsafe { + WCDBRustHandleStatement_bindBLOB(*self.cpp_obj, value.as_ptr(), len, index); + } + } + + pub fn bind_blob_opt(&self, value: Option<&Vec>, index: usize) { + if let Some(v) = value { + self.bind_blob(v, index); + } else { + self.bind_null(index); + } + } + + pub fn bind_null(&self, index: usize) { + unsafe { WCDBRustHandleStatement_bindNull(*self.cpp_obj, index) } + } + + pub fn bind_value(&self, value: &Value, index: usize) { + let value_type = value.get_type(); + if ColumnType::Integer == value_type { + self.bind_i64(value.get_i64(), index); + return; + } + if ColumnType::Float == value_type { + self.bind_f64(value.get_f64(), index); + return; + } + if ColumnType::Text == value_type { + self.bind_text(&value.get_text(), index); + return; + } + if ColumnType::BLOB == value_type { + self.bind_blob(&value.get_blob(), index); + return; + } + self.bind_null(index); + } + + pub fn bind_row(&self, row: &Vec) { + let mut index = 1; + for value in row { + self.bind_value(value, index); + index += 1; + } + } + + pub fn bind_object_by_fields( + arc_self: &Arc, + object: T, + fields: &Vec<&Field>, + ) { + if fields.is_empty() { + return; + } + let mut index = 1; + let binding = fields[0].get_table_binding(); + for field in fields { + binding.bind_field(&object, field, index, arc_self); + index += 1; + } + } + + pub fn get_column_type(&self, index: usize) -> ColumnType { + let ret = unsafe { WCDBRustHandleStatement_getColumnType(*self.cpp_obj, index as c_int) }; + ColumnType::value_of(ret) + } + + pub fn get_bool(&self, index: usize) -> bool { + self.get_i64(index) == 1 + } + + pub fn get_bool_opt(&self, index: usize) -> Option { + if self.get_column_type(index) == ColumnType::Null { + return None; + } + Some(self.get_bool(index)) + } + + pub fn get_i8(&self, index: usize) -> i8 { + self.get_i64(index) as i8 + } + + pub fn get_i8_opt(&self, index: usize) -> Option { + if self.get_column_type(index) == ColumnType::Null { + return None; + } + Some(self.get_i8(index)) + } + + pub fn get_i16(&self, index: usize) -> i16 { + self.get_i64(index) as i16 + } + + pub fn get_i16_opt(&self, index: usize) -> Option { + if self.get_column_type(index) == ColumnType::Null { + return None; + } + Some(self.get_i16(index)) + } + + pub fn get_i32(&self, index: usize) -> i32 { + self.get_i64(index) as i32 + } + + pub fn get_i32_opt(&self, index: usize) -> Option { + if self.get_column_type(index) == ColumnType::Null { + return None; + } + Some(self.get_i32(index)) + } + + pub fn get_i64(&self, index: usize) -> i64 { + unsafe { WCDBRustHandleStatement_getInteger(*self.cpp_obj, index) } + } + + pub fn get_i64_opt(&self, index: usize) -> Option { + if self.get_column_type(index) == ColumnType::Null { + return None; + } + Some(self.get_i64(index)) + } + + pub fn get_f32(&self, index: usize) -> f32 { + self.get_f64(index) as f32 + } + + pub fn get_f32_opt(&self, index: usize) -> Option { + if self.get_column_type(index) == ColumnType::Null { + return None; + } + Some(self.get_f32(index)) + } + + pub fn get_f64(&self, index: usize) -> f64 { + unsafe { WCDBRustHandleStatement_getDouble(*self.cpp_obj, index) } + } + + pub fn get_f64_opt(&self, index: usize) -> Option { + if self.get_column_type(index) == ColumnType::Null { + return None; + } + Some(self.get_f64(index)) + } + + pub fn get_text(&self, index: usize) -> String { + let text = unsafe { WCDBRustHandleStatement_getText(*self.cpp_obj, index) }; + text.to_cow().to_string() + } + + pub fn get_text_opt(&self, index: usize) -> Option { + if self.get_column_type(index) == ColumnType::Null { + return None; + } + Some(self.get_text(index)) + } + + pub fn get_blob(&self, index: usize) -> Vec { + let mut blob_ptr: *const u8 = std::ptr::null(); + let mut blob_len: i64 = 0; + + unsafe { + WCDBRustHandleStatement_getBLOB(*self.cpp_obj, index, &mut blob_ptr, &mut blob_len) + }; + if blob_len <= 0 || blob_ptr.is_null() { + return Vec::new(); + } + let blob: &[u8] = unsafe { slice::from_raw_parts(blob_ptr, blob_len as usize) }; + blob.to_vec() + } + + pub fn get_blob_opt(&self, index: usize) -> Option> { + if self.get_column_type(index) == ColumnType::Null { + return None; + } + Some(self.get_blob(index)) + } + + pub fn prepare(&self, statement: &T) -> WCDBResult<()> { + if unsafe { WCDBRustHandleStatement_prepare(*self.cpp_obj, CppObject::get(statement)) } { + Ok(()) + } else { + Err(self.create_exception()) + } + } + + pub fn prepare_with_sql(&self, sql: &str) -> WCDBResult<()> { + let cstr = sql.to_cstring(); + if unsafe { WCDBRustHandleStatement_prepareSQL(*self.cpp_obj, cstr.as_ptr()) } { + Ok(()) + } else { + Err(self.create_exception()) + } + } + + pub fn step(&self) -> WCDBResult<()> { + if !unsafe { WCDBRustHandleStatement_step(*self.cpp_obj) } { + if self.auto_finalize { + self.finalize_statement(); + } + Err(self.create_exception()) + } else { + Ok(()) + } + } + + pub fn reset(&self) { + unsafe { WCDBRustHandleStatement_reset(*self.cpp_obj) } + } + + pub fn finalize_statement(&self) { + unsafe { WCDBRustHandleStatement_finalize(*self.cpp_obj) } + } + + pub fn get_one_object(&self, fields: &Vec<&Field>) -> WCDBResult> { + assert!(fields.len() > 0); + let binding = fields[0].get_table_binding(); + let ret = binding.extract_object(fields, self); + Ok(Some(ret)) + } + + pub fn get_all_objects(&self, fields: &Vec<&Field>) -> WCDBResult> { + assert!(fields.len() > 0); + let field_opt = fields.first(); + if field_opt.is_none() { + return Err(WCDBException::create_exception(self.get_cpp_obj())); + } + let field = match field_opt { + Some(f) => f, + None => return Err(WCDBException::create_exception(self.get_cpp_obj())), + }; + let tb = field.get_table_binding(); + + let mut obj_vec: Vec = Vec::new(); + self.step()?; + + while !self.is_done() { + let obj = tb.extract_object(fields, self); + obj_vec.push(obj); + self.step()?; + } + + Ok(obj_vec) + } + + pub fn get_all_values(&self) -> WCDBResult>> { + let count = unsafe { WCDBRustHandleStatement_getColumnCount(*self.cpp_obj) as i32 }; + if count == 0 { + return Ok(Vec::new()); + } + + let mut rows: Vec> = Vec::new(); + self.step()?; + while !self.is_done() { + let mut row: Vec = Vec::new(); + for i in 0..count { + row.push(self.get_value(i)); + } + rows.push(row); + self.step()?; + } + Ok(rows) + } + + pub fn is_done(&self) -> bool { + unsafe { WCDBRustHandleStatement_isDone(*self.cpp_obj) } + } + + pub fn get_value(&self, index: i32) -> Value { + let ret = unsafe { WCDBRustHandleStatement_getColumnType(*self.cpp_obj, index as c_int) }; + if ret == 1 { + Value::new(self.get_i64(index as usize)) + } else if ret == 2 { + Value::new(self.get_f64(index as usize)) + } else if ret == 3 { + Value::new(&*self.get_text(index as usize)) + } else if ret == 4 { + Value::new(self.get_blob(index as usize)) + } else { + Value::default() + } + } + + pub fn get_one_row(&self) -> Vec { + let count = self.get_column_count(); + let mut row: Vec = Vec::new(); + if count == 0 { + return row; + } + for i in 0..count { + row.push(self.get_value(i)); + } + row + } + + pub fn get_multi_rows(&self) -> WCDBResult>> { + let mut rows: Vec> = Vec::new(); + self.step()?; + while !self.is_done() { + rows.push(self.get_one_row()); + self.step()?; + } + Ok(rows) + } + + pub fn get_column_count(&self) -> i32 { + let count = self.column_count.load(Ordering::SeqCst); + if count == -1 { + let real_count = unsafe { WCDBRustHandleStatement_getColumnCount(self.get_cpp_obj()) }; + self.column_count.store(real_count, Ordering::SeqCst); + real_count + } else { + count + } + } +} diff --git a/src/rust/wcdb/src/core/table.rs b/src/rust/wcdb/src/core/table.rs new file mode 100644 index 000000000..c9517975d --- /dev/null +++ b/src/rust/wcdb/src/core/table.rs @@ -0,0 +1,251 @@ +use crate::base::value::{Value, ValueObject}; +use crate::base::wcdb_exception::WCDBResult; +use crate::chaincall::delete::Delete; +use crate::chaincall::insert::Insert; +use crate::chaincall::select::Select; +use crate::chaincall::update::Update; +use crate::core::database::Database; +use crate::core::table_operation::TableOperationTrait; +use crate::core::table_orm_operation::{TableORMOperation, TableORMOperationTrait}; +use crate::orm::field::Field; +use crate::orm::table_binding::TableBinding; +use crate::winq::column::Column; +use crate::winq::expression::Expression; +use crate::winq::ordering_term::OrderingTerm; +use std::marker::PhantomData; + +pub struct Table<'a, T, R: TableBinding> { + table_orm_operation: TableORMOperation<'a, T, R>, + _phantom: PhantomData, +} + +impl<'a, T, R: TableBinding> TableOperationTrait for Table<'a, T, R> { + fn get_table_name(&self) -> &str { + self.table_orm_operation.get_table_name() + } + + fn get_database(&self) -> &Database { + self.table_orm_operation.get_database() + } + + fn insert_rows(&self, rows: Vec>, columns: Vec) -> WCDBResult<()> { + self.table_orm_operation.insert_rows(rows, columns) + } + + fn insert_or_replace_rows( + &self, + rows: Vec>, + columns: Vec, + ) -> WCDBResult<()> { + self.table_orm_operation + .insert_or_replace_rows(rows, columns) + } + + fn insert_or_ignore_rows(&self, rows: Vec>, columns: Vec) -> WCDBResult<()> { + self.table_orm_operation + .insert_or_ignore_rows(rows, columns) + } + + fn update_value>( + &self, + value: V, + column: Column, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + self.table_orm_operation.update_value( + value, + column, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } + + fn update_row( + &self, + row: &Vec, + columns: &Vec, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + self.table_orm_operation.update_row( + row, + columns, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } + + fn delete_value( + &self, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + self.table_orm_operation + .delete_value(condition_opt, order_opt, limit_opt, offset_opt) + } + + fn get_values( + &self, + columns: Vec<&Column>, + condition_opt: Option<&Expression>, + order_opt: Option>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult>> { + self.table_orm_operation.get_values( + columns, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } +} + +impl<'a, T, R: TableBinding> TableORMOperationTrait<'a, T, R> for Table<'a, T, R> { + fn get_binding(&self) -> &'a R { + self.table_orm_operation.get_binding() + } + + fn prepare_insert(&self) -> Insert { + self.table_orm_operation.prepare_insert() + } + + fn prepare_update(&self) -> Update { + self.table_orm_operation.prepare_update() + } + + fn prepare_select(&self) -> Select { + self.table_orm_operation.prepare_select() + } + + fn prepare_delete(&self) -> Delete { + self.table_orm_operation.prepare_delete() + } + + fn insert_object(&self, object: T, fields_opt: Option>>) -> WCDBResult<()> { + self.table_orm_operation.insert_object(object, fields_opt) + } + + fn insert_or_replace_object( + &self, + object: T, + fields_opt: Option>>, + ) -> WCDBResult<()> { + self.table_orm_operation + .insert_or_replace_object(object, fields_opt) + } + + fn insert_or_ignore_object( + &self, + object: T, + fields_opt: Option>>, + ) -> WCDBResult<()> { + self.table_orm_operation + .insert_or_ignore_object(object, fields_opt) + } + + fn insert_objects( + &self, + objects: Vec, + fields_opt: Option>>, + ) -> WCDBResult<()> { + self.table_orm_operation.insert_objects(objects, fields_opt) + } + + fn insert_or_replace_objects( + &self, + objects: Vec, + fields_opt: Option>>, + ) -> WCDBResult<()> { + self.table_orm_operation + .insert_or_replace_objects(objects, fields_opt) + } + + fn insert_or_ignore_objects( + &self, + objects: Vec, + fields_opt: Option>>, + ) -> WCDBResult<()> { + self.table_orm_operation + .insert_or_ignore_objects(objects, fields_opt) + } + + fn delete_objects( + &self, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + self.table_orm_operation + .delete_objects(condition_opt, order_opt, limit_opt, offset_opt) + } + + fn update_object( + &self, + object: T, + fields_opt: Option>>, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + self.table_orm_operation.update_object( + object, + fields_opt, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } + + fn get_first_object( + &self, + fields_opt: Option>>, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + offset_opt: Option, + ) -> WCDBResult> { + self.table_orm_operation + .get_first_object(fields_opt, condition_opt, order_opt, offset_opt) + } + + fn get_all_objects( + &self, + fields_opt: Option>>, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult> { + self.table_orm_operation.get_all_objects( + fields_opt, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } +} + +impl<'a, T, R: TableBinding> Table<'a, T, R> { + pub fn new(table_name: &str, binding: &'a R, database: &'a Database) -> Table<'a, T, R> { + Table { + table_orm_operation: TableORMOperation::new(table_name, binding, database), + _phantom: PhantomData, + } + } +} diff --git a/src/rust/wcdb/src/core/table_operation.rs b/src/rust/wcdb/src/core/table_operation.rs new file mode 100644 index 000000000..a6eb06078 --- /dev/null +++ b/src/rust/wcdb/src/core/table_operation.rs @@ -0,0 +1,303 @@ +use crate::base::value::{Value, ValueObject}; +use crate::base::wcdb_exception::WCDBResult; +use crate::core::database::Database; +use crate::core::handle::Handle; +use crate::core::handle_operation::HandleOperationTrait; +use crate::winq::column::Column; +use crate::winq::conflict_action::ConflictAction; +use crate::winq::expression::Expression; +use crate::winq::identifier::IdentifierTrait; +use crate::winq::ordering_term::OrderingTerm; +use crate::winq::statement_delete::StatementDelete; +use crate::winq::statement_insert::StatementInsert; +use crate::winq::statement_select::StatementSelect; +use crate::winq::statement_update::StatementUpdate; + +pub struct TableOperation<'a> { + table_name: String, + database: &'a Database, +} + +pub trait TableOperationTrait { + fn get_table_name(&self) -> &str; + + fn get_database(&self) -> &Database; + + fn insert_rows(&self, rows: Vec>, columns: Vec) -> WCDBResult<()>; + + fn insert_or_replace_rows(&self, rows: Vec>, columns: Vec) + -> WCDBResult<()>; + + fn insert_or_ignore_rows(&self, rows: Vec>, columns: Vec) -> WCDBResult<()>; + + fn update_value>( + &self, + value: V, + column: Column, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()>; + + fn update_row( + &self, + row: &Vec, + columns: &Vec, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()>; + + fn delete_value( + &self, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()>; + + fn get_values( + &self, + columns: Vec<&Column>, + condition_opt: Option<&Expression>, + order_opt: Option>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult>>; +} + +impl<'a> TableOperationTrait for TableOperation<'a> { + fn get_table_name(&self) -> &str { + &self.table_name + } + + fn get_database(&self) -> &Database { + self.database + } + + fn insert_rows(&self, rows: Vec>, columns: Vec) -> WCDBResult<()> { + self.insert_rows_with_conflict_action(rows, columns, ConflictAction::None) + } + + fn insert_or_replace_rows( + &self, + rows: Vec>, + columns: Vec, + ) -> WCDBResult<()> { + self.insert_rows_with_conflict_action(rows, columns, ConflictAction::Replace) + } + + fn insert_or_ignore_rows(&self, rows: Vec>, columns: Vec) -> WCDBResult<()> { + self.insert_rows_with_conflict_action(rows, columns, ConflictAction::Ignore) + } + + fn update_value>( + &self, + value: V, + column: Column, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + let row_item = Value::new(value); + self.update_row( + &vec![row_item], + &vec![column], + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } + + fn update_row( + &self, + row: &Vec, + columns: &Vec, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + let binding = StatementUpdate::new(); + binding + .update(self.table_name.as_str()) + .set_columns_to_bind_parameters(columns); + self.execute_update( + row, + &binding, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } + + fn delete_value( + &self, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + let binding = StatementDelete::new(); + binding.delete_from(self.table_name.as_ref()); + if let Some(expression) = condition_opt { + binding.where_(&expression); + } + if let Some(order) = order_opt { + binding.order_by(vec![order]); + } + if let Some(limit) = limit_opt { + binding.limit(limit); + } + if let Some(offset) = offset_opt { + binding.offset(offset); + } + self.database.get_handle(true).execute(&binding) + } + + fn get_values( + &self, + columns: Vec<&Column>, + condition_opt: Option<&Expression>, + order_opt: Option>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult>> { + let handle = self.database.get_handle(false); + let binding = StatementSelect::new(); + binding.from(vec![self.table_name.as_str()]); + binding.select(columns); + if let Some(expression) = condition_opt { + binding.where_(&expression); + } + if let Some(order_vec) = order_opt { + binding.order_by(order_vec); + } + if let Some(limit) = limit_opt { + binding.limit(limit); + } + if let Some(offset) = offset_opt { + binding.offset(offset); + } + match handle.prepared_with_main_statement(&binding) { + Ok(statement) => match statement.get_all_values() { + Ok(ret) => Ok(ret), + Err(err) => Err(err), + }, + Err(err) => Err(err), + } + } +} + +impl<'a> TableOperation<'a> { + pub fn new(table_name: &str, database: &'a Database) -> TableOperation<'a> { + TableOperation { + table_name: table_name.to_string(), + database, + } + } +} + +impl<'a> TableOperation<'a> { + fn insert_rows_with_conflict_action( + &self, + rows: Vec>, + columns: Vec, + action: ConflictAction, + ) -> WCDBResult<()> { + if rows.len() == 0 { + return Ok(()); + } + let binding = StatementInsert::new(); + let insert = binding + .insert_into(self.table_name.as_ref()) + .columns(&columns) + .values_with_bind_parameters(columns.len()); + match action { + ConflictAction::Replace => { + insert.or_replace(); + } + ConflictAction::Ignore => { + insert.or_ignore(); + } + _ => {} + } + let handle = self.database.get_handle(true); + if rows.len() == 1 { + return self.insert_rows_with_handle(&rows, &insert, &handle); + } + handle.run_transaction(|handle: &Handle| { + self.insert_rows_with_handle(&rows, &insert, handle).is_ok() + }) + } + + fn insert_rows_with_handle( + &self, + rows: &Vec>, + insert: &StatementInsert, + handle: &Handle, + ) -> WCDBResult<()> { + println!("statement: {:?}", insert.get_description()); + match handle.prepared_with_main_statement(insert) { + Ok(prepared_stmt) => { + for row in rows { + prepared_stmt.reset(); + prepared_stmt.bind_row(row); + prepared_stmt.step()?; + } + prepared_stmt.finalize_statement(); + Ok(()) + } + Err(err) => Err(err), + } + } +} + +/// Update +impl<'a> TableOperation<'a> { + fn execute_update( + &self, + row: &Vec, + update: &StatementUpdate, + expression: Option<&Expression>, + order: Option<&OrderingTerm>, + limit: Option, + offset: Option, + ) -> WCDBResult<()> { + if let Some(order) = order { + update.order_by(vec![order]); + } + if let Some(limit) = limit { + update.limit(limit); + } + if let Some(offset) = offset { + update.offset(offset); + } + if let Some(expression) = expression { + update.where_(&expression); + } + let mut handler = self.database.get_handle(true); + let ret = match handler.prepared_with_main_statement(update) { + Ok(statement) => { + statement.bind_row(row); + let step_ret = statement.step(); + statement.finalize_statement(); + step_ret + } + Err(err) => Err(err), + }; + handler.invalidate(); + ret + } +} + +impl<'a> TableOperation<'a> { + pub fn get_handle(&self, write_hint: bool) -> Handle { + self.database.get_handle(write_hint) + } +} diff --git a/src/rust/wcdb/src/core/table_orm_operation.rs b/src/rust/wcdb/src/core/table_orm_operation.rs new file mode 100644 index 000000000..e4e2efe23 --- /dev/null +++ b/src/rust/wcdb/src/core/table_orm_operation.rs @@ -0,0 +1,430 @@ +use crate::base::value::{Value, ValueObject}; +use crate::base::wcdb_exception::WCDBResult; +use crate::chaincall::delete::Delete; +use crate::chaincall::insert::Insert; +use crate::chaincall::select::Select; +use crate::chaincall::update::Update; +use crate::core::database::Database; +use crate::core::table_operation::{TableOperation, TableOperationTrait}; +use crate::orm::field::Field; +use crate::orm::table_binding::TableBinding; +use crate::winq::column::Column; +use crate::winq::expression::Expression; +use crate::winq::ordering_term::OrderingTerm; +use std::marker::PhantomData; + +pub struct TableORMOperation<'a, T, R: TableBinding> { + table_operation: TableOperation<'a>, + binding: &'a R, + _phantom: PhantomData, +} + +pub trait TableORMOperationTrait<'a, T, R: TableBinding>: TableOperationTrait { + fn get_binding(&self) -> &'a R; + + fn prepare_insert(&self) -> Insert; + + fn prepare_update(&self) -> Update; + + fn prepare_select(&self) -> Select; + + fn prepare_delete(&self) -> Delete; + + fn insert_object(&self, object: T, fields_opt: Option>>) -> WCDBResult<()>; + + fn insert_or_replace_object( + &self, + object: T, + fields_opt: Option>>, + ) -> WCDBResult<()>; + + fn insert_or_ignore_object( + &self, + object: T, + fields_opt: Option>>, + ) -> WCDBResult<()>; + + fn insert_objects(&self, objects: Vec, fields_opt: Option>>) + -> WCDBResult<()>; + + fn insert_or_replace_objects( + &self, + objects: Vec, + fields_opt: Option>>, + ) -> WCDBResult<()>; + + fn insert_or_ignore_objects( + &self, + objects: Vec, + fields_opt: Option>>, + ) -> WCDBResult<()>; + + fn delete_objects( + &self, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()>; + + fn update_object( + &self, + object: T, + fields_opt: Option>>, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()>; + + fn get_first_object( + &self, + fields_opt: Option>>, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + offset_opt: Option, + ) -> WCDBResult>; + + fn get_all_objects( + &self, + fields_opt: Option>>, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult>; +} + +impl<'a, T, R: TableBinding> TableOperationTrait for TableORMOperation<'a, T, R> { + fn get_table_name(&self) -> &str { + self.table_operation.get_table_name() + } + + fn get_database(&self) -> &Database { + self.table_operation.get_database() + } + + fn insert_rows(&self, rows: Vec>, columns: Vec) -> WCDBResult<()> { + self.table_operation.insert_rows(rows, columns) + } + + fn insert_or_replace_rows( + &self, + rows: Vec>, + columns: Vec, + ) -> WCDBResult<()> { + self.table_operation.insert_or_replace_rows(rows, columns) + } + + fn insert_or_ignore_rows(&self, rows: Vec>, columns: Vec) -> WCDBResult<()> { + self.table_operation.insert_or_ignore_rows(rows, columns) + } + + fn update_value>( + &self, + value: V, + column: Column, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + self.table_operation.update_value( + value, + column, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } + + fn update_row( + &self, + row: &Vec, + columns: &Vec, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + self.table_operation.update_row( + row, + columns, + condition_opt, + order_opt, + limit_opt, + offset_opt, + ) + } + + fn delete_value( + &self, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + self.table_operation + .delete_value(condition_opt, order_opt, limit_opt, offset_opt) + } + + fn get_values( + &self, + columns: Vec<&Column>, + condition_opt: Option<&Expression>, + order_opt: Option>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult>> { + self.table_operation + .get_values(columns, condition_opt, order_opt, limit_opt, offset_opt) + } +} + +impl<'a, T, R: TableBinding> TableORMOperationTrait<'a, T, R> for TableORMOperation<'a, T, R> { + fn get_binding(&self) -> &'a R { + self.binding + } + + fn prepare_insert(&self) -> Insert { + let insert = Insert::new(self.table_operation.get_handle(true), false, true); + insert.into_table(self.table_operation.get_table_name()); + insert + } + + fn prepare_update(&self) -> Update { + let update = Update::new(self.table_operation.get_handle(true), false, true); + update.table(self.table_operation.get_table_name()); + update + } + + fn prepare_select(&self) -> Select { + let select = Select::new(self.table_operation.get_handle(false), false, true); + select.from(self.table_operation.get_table_name()); + select + } + + fn prepare_delete(&self) -> Delete { + let delete = Delete::new(self.table_operation.get_handle(true), false, true); + delete.from_table(self.table_operation.get_table_name()); + delete + } + + fn insert_object(&self, object: T, fields_opt: Option>>) -> WCDBResult<()> { + let insert = self.prepare_insert(); + insert.value(object); + if let Some(fields) = fields_opt { + insert.on_fields(fields); + } else { + insert.on_fields(self.binding.all_binding_fields()); + } + insert.execute()?; + Ok(()) + } + + fn insert_or_replace_object( + &self, + object: T, + fields_opt: Option>>, + ) -> WCDBResult<()> { + let insert = self.prepare_insert(); + insert.value(object).or_replace(); + if let Some(fields) = fields_opt { + insert.on_fields(fields); + } else { + insert.on_fields(self.binding.all_binding_fields()); + } + insert.execute()?; + Ok(()) + } + + fn insert_or_ignore_object( + &self, + object: T, + fields_opt: Option>>, + ) -> WCDBResult<()> { + let insert = self.prepare_insert(); + insert.value(object).or_ignore(); + if let Some(fields) = fields_opt { + insert.on_fields(fields); + } else { + insert.on_fields(self.binding.all_binding_fields()); + } + insert.execute()?; + Ok(()) + } + + fn insert_objects( + &self, + objects: Vec, + fields_opt: Option>>, + ) -> WCDBResult<()> { + let insert = self.prepare_insert(); + insert.values(objects); + if let Some(fields) = fields_opt { + insert.on_fields(fields); + } else { + insert.on_fields(self.binding.all_binding_fields()); + } + insert.execute()?; + Ok(()) + } + + fn insert_or_replace_objects( + &self, + objects: Vec, + fields_opt: Option>>, + ) -> WCDBResult<()> { + let insert = self.prepare_insert(); + insert.values(objects).or_replace(); + if let Some(fields) = fields_opt { + insert.on_fields(fields); + } else { + insert.on_fields(self.binding.all_binding_fields()); + } + insert.execute()?; + Ok(()) + } + + fn insert_or_ignore_objects( + &self, + objects: Vec, + fields_opt: Option>>, + ) -> WCDBResult<()> { + let insert = self.prepare_insert(); + insert.values(objects).or_ignore(); + if let Some(fields) = fields_opt { + insert.on_fields(fields); + } else { + insert.on_fields(self.binding.all_binding_fields()); + } + insert.execute()?; + Ok(()) + } + + fn delete_objects( + &self, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + let delete = self.prepare_delete(); + if let Some(condition) = condition_opt { + delete.where_(&condition); + } + if let Some(order) = order_opt { + delete.order_by(vec![order]); + } + if let Some(limit) = limit_opt { + delete.limit(limit); + } + if let Some(offset) = offset_opt { + delete.offset(offset); + } + delete.execute()?; + Ok(()) + } + + fn update_object( + &self, + object: T, + fields_opt: Option>>, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult<()> { + let update = self.prepare_update(); + if let Some(fields) = fields_opt { + update.set(fields); + } else { + update.set(self.binding.all_binding_fields()); + } + if let Some(condition) = condition_opt { + update.where_(&condition); + } + if let Some(order) = order_opt { + update.order_by(vec![order]); + } + if let Some(limit) = limit_opt { + update.limit(limit); + } + if let Some(offset) = offset_opt { + update.offset(offset); + } + update.to_object(object); + update.execute()?; + Ok(()) + } + + fn get_first_object( + &self, + fields_opt: Option>>, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + offset_opt: Option, + ) -> WCDBResult> { + let select = self.prepare_select(); + if let Some(fields) = fields_opt { + select.select(fields); + } else { + select.select(self.binding.all_binding_fields()); + } + if let Some(condition) = condition_opt { + select.where_(&condition); + } + if let Some(order) = order_opt { + select.order_by(vec![order]); + } + select.limit(1); + if let Some(offset) = offset_opt { + select.offset(offset); + } + select.first_object() + } + + fn get_all_objects( + &self, + fields_opt: Option>>, + condition_opt: Option<&Expression>, + order_opt: Option<&OrderingTerm>, + limit_opt: Option, + offset_opt: Option, + ) -> WCDBResult> { + let select = self.prepare_select(); + if let Some(fields) = fields_opt { + select.select(fields); + } else { + select.select(self.binding.all_binding_fields()); + } + if let Some(condition) = condition_opt { + select.where_(&condition); + } + if let Some(order) = order_opt { + select.order_by(vec![order]); + } + if let Some(limit) = limit_opt { + select.limit(limit); + } + if let Some(offset) = offset_opt { + select.offset(offset); + } + select.all_objects() + } +} + +impl<'a, K, R: TableBinding> TableORMOperation<'a, K, R> { + pub fn new( + table_name: &str, + binding: &'a R, + database: &'a Database, + ) -> TableORMOperation<'a, K, R> { + TableORMOperation { + table_operation: TableOperation::new(table_name, database), + binding, + _phantom: PhantomData, + } + } +} diff --git a/src/rust/wcdb/src/fts/builtin_tokenizer.rs b/src/rust/wcdb/src/fts/builtin_tokenizer.rs new file mode 100644 index 000000000..6cb390a88 --- /dev/null +++ b/src/rust/wcdb/src/fts/builtin_tokenizer.rs @@ -0,0 +1,70 @@ +pub struct BuiltinTokenizer; + +impl BuiltinTokenizer { + /** + * The following four are sqlite built-in fts tokenizers. + * `Simple` tokenizer can be used in fts3/4 and the others can be used in fts3/4/5. + * You can use `com.tencent.wcdb.FTSModule` annotation to config fts tokenizer for a Java ORM class. + */ + pub const SIMPLE: &'static str = "simple"; + pub const PORTER: &'static str = "porter"; + pub const ICU: &'static str = "icu"; + pub const UNICODE61: &'static str = "unicode61"; + + /** + * `OneOrBinary` is a WCDB implemented tokenizer for fts3/4. + * Consecutive English letters will be recognized as an English token, and this token will be stemmed using the porter stemming algorithm. + * Continuous Arabic numerals will be recognized as a single numeric token. + * For other Unicode characters, each character will be recognized as one token. + * For example, the sentence "The phone number of 张三 is 12345" will be split into these tokens: "the", "phone", "number", "of", "张", "三", "is", "12345". + * You can use `com.tencent.wcdb.FTSModule` annotation to config fts tokenizer for a Java ORM class. + */ + pub const ONE_OR_BINARY: &'static str = "wcdb_one_or_binary"; + + /** + * Deprecated tokenizer for fts3/4. + */ + pub const MMICU: &'static str = "mmicu"; + + /** + * `Verbatim` is a WCDB implemented tokenizer for fts5. + * It has the same tokenize rules as `OneOrBinary`. + * You can use `com.tencent.wcdb.FTSModule annotation` to config fts tokenizer for a Java ORM class. + * + * @see #OneOrBinary + */ + pub const VERBATIM: &'static str = "wcdb_verbatim"; + + /** + * `Pinyin` is a WCDB implemented tokenizer for fts5. + * It is designed for pinyin search. + * You can use the simplified or full pinyin of Chinese characters to search for Chinese characters. + * Before using this tokenizer, you need to use `Database.configPinyinDict()` to configure the mapping relationship between Chinese characters and their pinyin. + * You can use `com.tencent.wcdb.FTSModule annotation` to config fts tokenizer for a Java ORM class. + * + * @see com.tencent.wcdb.core.Database#configPinyinDict(Map) + */ + pub const PINYIN: &'static str = "wcdb_pinyin"; +} + +/** + * The following two are optional parameters for WCDB implemented tokenizers. + * You can configure these parameters on the `tokenizerParameters` attribute of `com.tencent.wcdb.FTSModule` annotation. + */ +pub struct BuiltinTokenizerParameter; + +impl BuiltinTokenizerParameter { + /** + * `SimplifyChinese` enables the tokenizer to convert each traditional Chinese character into a simplified Chinese character, + * so that you can use Simplified Chinese characters to search Traditional Chinese characters. + * Note that you need to use `Database.configTraditionalChineseDict()` to config the mapping relationship between traditional Chinese characters and simplified Chinese characters. + * + * @see com.tencent.wcdb.core.Database#configTraditionalChineseDict(Map) + */ + pub const SIMPLIFY_CHINESE: &'static str = "chinese_traditional_to_simplified"; + + /** + * `SkipStemming` will disable the stemming during tokenization. + */ + pub const SKIP_STEMMING: &'static str = "skip_stemming"; +} diff --git a/src/rust/wcdb/src/fts/mod.rs b/src/rust/wcdb/src/fts/mod.rs new file mode 100644 index 000000000..2119abbdf --- /dev/null +++ b/src/rust/wcdb/src/fts/mod.rs @@ -0,0 +1 @@ +pub mod builtin_tokenizer; diff --git a/src/rust/wcdb/src/lib.rs b/src/rust/wcdb/src/lib.rs new file mode 100644 index 000000000..3fc8bdd3e --- /dev/null +++ b/src/rust/wcdb/src/lib.rs @@ -0,0 +1,13 @@ +#![feature(box_into_inner)] +#![feature(c_size_t)] +#![feature(get_mut_unchecked)] +extern crate core as other_core; + +pub mod base; +pub mod chaincall; +pub mod core; +pub mod orm; +pub mod winq; + +pub mod fts; +mod utils; diff --git a/src/rust/wcdb/src/orm/binding.rs b/src/rust/wcdb/src/orm/binding.rs new file mode 100644 index 000000000..60cc749f4 --- /dev/null +++ b/src/rust/wcdb/src/orm/binding.rs @@ -0,0 +1,163 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::wcdb_exception::WCDBResult; +use crate::core::handle::Handle; +use crate::utils::ToCString; +use crate::winq::column_def::ColumnDef; +use crate::winq::statement_create_index::StatementCreateIndex; +use crate::winq::table_constraint::TableConstraint; +use std::ffi::{c_char, c_void, CString}; +use std::sync::RwLock; + +extern "C" { + fn WCDBRustBinding_create() -> *mut c_void; + + fn WCDBRustBinding_addColumnDef(cpp_obj: *mut c_void, column_def: *mut c_void); + + fn WCDBRustBinding_enableAutoIncrementForExistingTable(cpp_obj: *mut c_void); + + fn WCDBRustBinding_addIndex( + cpp_obj: *mut c_void, + index_name_or_suffix: *const c_char, + is_full_name: bool, + create_index: *mut c_void, + ); + + fn WCDBRustBinding_configWithoutRowId(cpp_obj: *mut c_void); + + fn WCDBRustBinding_addTableConstraint(cpp_obj: *mut c_void, table_constraint: *mut c_void); + + fn WCDBRustBinding_configVirtualModule(cpp_obj: *mut c_void, module: *const c_char); + + fn WCDBRustBinding_configVirtualModuleArgument(cpp_obj: *mut c_void, argument: *const c_char); + + fn WCDBRustBinding_createTable( + cpp_obj: *mut c_void, + path: *const c_char, + handle: *mut c_void, + ) -> bool; + + fn WCDBRustBinding_createVirtualTable( + cpp_obj: *mut c_void, + path: *const c_char, + handle: *mut c_void, + ) -> bool; + + fn WCDBRustBinding_getBaseBinding(cpp_obj: *mut c_void) -> *mut c_void; +} + +pub struct Binding { + cpp_obj: CppObject, + base_binding: RwLock<*mut c_void>, +} + +unsafe impl Send for Binding {} +unsafe impl Sync for Binding {} + +impl Binding { + pub fn new() -> Binding { + Binding { + cpp_obj: CppObject::new(Some(unsafe { WCDBRustBinding_create() })), + base_binding: RwLock::new(std::ptr::null_mut()), + } + } + + pub fn add_column_def(&self, column_def: ColumnDef) { + unsafe { + WCDBRustBinding_addColumnDef(self.cpp_obj.get_cpp_obj(), column_def.get_cpp_obj()) + }; + } + + pub fn enable_auto_increment_for_existing_table(&self) { + unsafe { WCDBRustBinding_enableAutoIncrementForExistingTable(self.cpp_obj.get_cpp_obj()) }; + } + + pub fn add_index( + &self, + index_name_or_suffix: &str, + is_full_name: bool, + create_index: StatementCreateIndex, + ) { + let c_index_name = CString::new(index_name_or_suffix).unwrap_or_default(); + unsafe { + WCDBRustBinding_addIndex( + self.cpp_obj.get_cpp_obj(), + c_index_name.as_ptr(), + is_full_name, + create_index.get_cpp_obj(), + ); + } + } + + pub fn add_table_constraint(&self, constraint: TableConstraint) { + unsafe { + WCDBRustBinding_addTableConstraint(self.cpp_obj.get_cpp_obj(), constraint.get_cpp_obj()) + }; + } + + pub fn config_virtual_module(&self, module: &str) -> &Binding { + let cstr = module.to_cstring(); + unsafe { WCDBRustBinding_configVirtualModule(*self.cpp_obj, cstr.as_ptr()) } + self + } + + pub fn config_virtual_module_argument(&self, argument: &str) { + let cstr = argument.to_cstring(); + unsafe { + WCDBRustBinding_configVirtualModuleArgument(*self.cpp_obj, cstr.as_ptr()); + } + } + + pub fn config_without_row_id(&self) { + unsafe { WCDBRustBinding_configWithoutRowId(*self.cpp_obj) } + } + + pub fn create_table(&self, table_name: &str, handle: Handle) -> WCDBResult { + let c_table_name = table_name.to_cstring(); + let cpp_handle = handle.get_cpp_handle()?; + Ok(unsafe { + WCDBRustBinding_createTable( + self.cpp_obj.get_cpp_obj(), + c_table_name.as_ptr(), + cpp_handle, + ) + }) + } + + pub fn create_virtual_table(&self, table_name: &str, handle: Handle) -> WCDBResult { + let c_table_name = table_name.to_cstring(); + let cpp_handle = handle.get_cpp_handle()?; + Ok(unsafe { + WCDBRustBinding_createVirtualTable( + self.cpp_obj.get_cpp_obj(), + c_table_name.as_ptr(), + cpp_handle, + ) + }) + } + + pub fn get_base_binding(&self) -> *mut c_void { + // 先检查是否为空,但不保持读锁 + let is_null = match self.base_binding.read() { + Ok(guard) => guard.is_null(), + Err(_) => false, // 如果读取失败,假设不为空 + }; + + // 如果为空,则获取写锁并设置值 + if is_null { + if let Ok(mut write_guard) = self.base_binding.write() { + // 再次检查是否为空,因为可能在我们释放读锁和获取写锁之间被其他线程修改 + if write_guard.is_null() { + let base_binding = + unsafe { WCDBRustBinding_getBaseBinding(self.cpp_obj.get_cpp_obj()) }; + *write_guard = base_binding; + } + } + } + + // 最后再读取一次返回值 + match self.base_binding.read() { + Ok(guard) => *guard, + Err(_) => std::ptr::null_mut(), // 如果读取失败,返回空指针 + } + } +} diff --git a/src/rust/wcdb/src/orm/field.rs b/src/rust/wcdb/src/orm/field.rs new file mode 100644 index 000000000..47f110ab9 --- /dev/null +++ b/src/rust/wcdb/src/orm/field.rs @@ -0,0 +1,478 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_basic_expression::BasicExpression; +use crate::base::param::enum_expression_ref::ExpressionRef; +use crate::base::param::enum_string_schema::StringSchema; +use crate::orm::table_binding::TableBinding; +use crate::winq::column::{Column, ColumnStaticTrait, ColumnTrait}; +use crate::winq::column_def::ColumnDef; +use crate::winq::column_type::ColumnType; +use crate::winq::expression::Expression; +use crate::winq::expression_convertible::ExpressionConvertibleTrait; +use crate::winq::expression_operable::ExpressionOperableTrait; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::indexed_column_convertible::IndexedColumnConvertibleTrait; +use crate::winq::ordering_term::{Order, OrderingTerm}; +use crate::winq::result_column::ResultColumn; +use crate::winq::result_column_convertible_trait::ResultColumnConvertibleTrait; +use crate::winq::statement_select::StatementSelect; +use std::ffi::c_void; + +pub struct Field { + column: Column, + name: String, + binding: *const dyn TableBinding, + field_id: usize, + is_auto_increment: bool, + is_primary_key: bool, +} + +impl CppObjectTrait for Field { + fn set_cpp_obj(&mut self, _cpp_obj: *mut c_void) { + self.column.set_cpp_obj(_cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.column.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.column.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for Field { + fn as_cpp_object(&self) -> &CppObject { + self.column.as_cpp_object() + } +} + +impl IdentifierConvertibleTrait for Field { + fn as_identifier(&self) -> &Identifier { + self.column.as_identifier() + } +} + +impl IdentifierTrait for Field { + fn get_type(&self) -> CPPType { + self.column.get_type() + } + + fn get_description(&self) -> String { + self.column.get_description() + } +} + +impl ExpressionOperableTrait for Field { + fn is_null(&self) -> Expression { + self.column.is_null() + } + + fn not_null(&self) -> Expression { + self.column.not_null() + } + + fn or<'a, E>(&self, operand: E) -> Expression + where + E: Into>, + { + self.column.or(operand) + } + + fn and<'a, E>(&self, operand: E) -> Expression + where + E: Into>, + { + self.column.and(operand) + } + + fn multiply<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.multiply(operand) + } + + fn divide<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.divide(operand) + } + + fn mod_<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.mod_(operand) + } + + fn add<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.add(operand) + } + + fn minus<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.minus(operand) + } + + fn left_shift<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.left_shift(operand) + } + + fn right_shift<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.right_shift(operand) + } + + fn bit_and<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.bit_and(operand) + } + + fn bit_or<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.bit_or(operand) + } + + fn lt<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.lt(operand) + } + + fn le<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.le(operand) + } + + fn gt<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.gt(operand) + } + + fn ge<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.ge(operand) + } + + fn eq<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.eq(operand) + } + + fn not_eq<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.not_eq(operand) + } + + fn concat<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.concat(operand) + } + + fn between<'a, T, V>(&self, begin: T, end: V) -> Expression + where + T: Into>, + V: Into>, + { + self.column.between(begin, end) + } + + fn not_between<'a, T, V>(&self, begin: T, end: V) -> Expression + where + T: Into>, + V: Into>, + { + self.column.not_between(begin, end) + } + + fn in_<'a, S>(&self, operands: Vec) -> Expression + where + S: Into>, + { + self.column + .in_(Identifier::get_cpp_type(self), operands, false) + } + + fn not_in<'a, S>(&self, operands: Vec) -> Expression + where + S: Into>, + { + self.column + .not_in(Identifier::get_cpp_type(self), operands, true) + } + + fn in_statement_select(&self, stat: &StatementSelect) -> Expression { + self.column.in_statement_select(stat) + } + + fn not_in_statement_select(&self, stat: &StatementSelect) -> Expression { + self.column.in_statement_select(stat) + } + + fn in_table(&self, table: &str) -> Expression { + self.column.in_table(table) + } + + fn not_in_table(&self, table: &str) -> Expression { + self.column.not_in_table(table) + } + + fn collate(&self, collation: &str) -> Expression { + self.column.collate(collation) + } + + fn substr(&self, start: i64, length: i64) -> Expression { + self.column.substr(start, length) + } + + fn like(&self, content: &str) -> Expression { + self.column.like(content) + } + + fn not_like(&self, content: &str) -> Expression { + self.column.not_like(content) + } + + fn glob(&self, content: &str) -> Expression { + self.column.glob(content) + } + + fn not_glob(&self, content: &str) -> Expression { + self.column.not_glob(content) + } + + fn match_(&self, content: &str) -> Expression { + self.column.match_(content) + } + + fn not_match(&self, content: &str) -> Expression { + self.column.not_match(content) + } + + fn regexp(&self, content: &str) -> Expression { + self.column.regexp(content) + } + + fn not_regexp(&self, content: &str) -> Expression { + self.column.not_regexp(content) + } + + fn is<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.is(operand) + } + + fn is_not<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.column.is_not(operand) + } + + fn avg(&self) -> Expression { + self.column.avg() + } + + fn count(&self) -> Expression { + self.column.count() + } + + fn group_concat(&self) -> Expression { + self.column.group_concat() + } + + fn group_concat_string(&self, separator: &str) -> Expression { + self.column.group_concat_string(separator) + } + + fn max(&self) -> Expression { + self.column.max() + } + + fn min(&self) -> Expression { + self.column.min() + } + + fn sum(&self) -> Expression { + self.column.sum() + } + + fn total(&self) -> Expression { + self.column.total() + } + + fn abs(&self) -> Expression { + self.column.abs() + } + + fn hex(&self) -> Expression { + self.column.hex() + } + + fn length(&self) -> Expression { + self.column.length() + } + + fn lower(&self) -> Expression { + self.column.lower() + } + + fn upper(&self) -> Expression { + self.column.upper() + } + + fn round(&self) -> Expression { + self.column.round() + } + + fn match_info(&self) -> Expression { + self.column.match_info() + } + + fn offsets(&self) -> Expression { + self.column.offsets() + } + + fn snippet(&self) -> Expression { + self.column.snippet() + } + + fn bm25(&self) -> Expression { + self.column.bm25() + } + + fn highlight(&self) -> Expression { + self.column.highlight() + } + + fn substring_match_info(&self) -> Expression { + self.column.substring_match_info() + } +} + +impl IndexedColumnConvertibleTrait for Field {} + +impl ResultColumnConvertibleTrait for Field {} + +impl ExpressionConvertibleTrait for Field {} + +impl ColumnTrait for Field { + fn as_(&self, alias: &str) -> ResultColumn { + self.column.as_(alias) + } + + fn order(&self, order: Order) -> OrderingTerm { + self.column.order(order) + } + + fn as_def(&self, column_type: ColumnType) -> ColumnDef { + self.column.as_def(column_type) + } +} + +impl ColumnStaticTrait for Field { + fn table(&self, table: &str) -> &Column { + self.column.table(table) + } + + fn of<'a, T: Into>>(&self, schema: T) -> &Column { + self.column.of(schema) + } + + fn all() -> Column { + Column::all() + } + + fn row_id() -> Column { + Column::row_id() + } +} + +impl Field { + pub fn new( + name: &str, + binding: *const dyn TableBinding, + field_id: usize, + is_auto_increment: bool, + is_primary_key: bool, + ) -> Field { + let bind = unsafe { &*binding }; + Field { + column: Column::new(name, Some(bind.base_binding().get_base_binding())), + name: name.to_string(), + binding, + field_id, + is_auto_increment, + is_primary_key, + } + } + + pub fn get_column(&self) -> &Column { + &self.column + } + + pub fn get_name(&self) -> &str { + &self.name + } + + pub fn get_field_id(&self) -> usize { + self.field_id + } + + pub fn get_table_binding(&self) -> &dyn TableBinding { + assert!(!self.binding.is_null()); + unsafe { &*self.binding } + } + + pub fn is_auto_increment(&self) -> bool { + self.is_auto_increment + } + + pub(crate) fn get_binding_from_field(field: &Field) -> &dyn TableBinding { + field.get_table_binding() + } + + pub(crate) fn get_binding_from_fields<'a>( + fields: &Vec<&'a Field>, + ) -> &'a dyn TableBinding { + assert!(!fields.is_empty()); + let field = fields[0]; + Self::get_binding_from_field(field) + } + + pub fn is_primary_key(&self) -> bool { + self.is_primary_key + } +} diff --git a/src/rust/wcdb/src/orm/mod.rs b/src/rust/wcdb/src/orm/mod.rs new file mode 100644 index 000000000..b3600d7da --- /dev/null +++ b/src/rust/wcdb/src/orm/mod.rs @@ -0,0 +1,3 @@ +pub mod binding; +pub mod field; +pub mod table_binding; diff --git a/src/rust/wcdb/src/orm/table_binding.rs b/src/rust/wcdb/src/orm/table_binding.rs new file mode 100644 index 000000000..9dfe87b53 --- /dev/null +++ b/src/rust/wcdb/src/orm/table_binding.rs @@ -0,0 +1,27 @@ +use crate::core::prepared_statement::PreparedStatement; +use crate::orm::binding::Binding; +use crate::orm::field::Field; +use std::any::TypeId; +use std::sync::Arc; + +pub trait TableBinding { + fn binding_type(&self) -> TypeId; + + fn all_binding_fields(&self) -> Vec<&Field>; + + fn base_binding(&self) -> &Binding; + + fn extract_object(&self, fields: &Vec<&Field>, prepared_statement: &PreparedStatement) -> T; + + fn bind_field( + &self, + object: &T, + field: &Field, + index: usize, + prepared_statement: &Arc, + ); + + fn is_auto_increment(&self, object: &T) -> bool; + + fn set_last_insert_row_id(&self, object: &mut T, last_insert_row_id: i64); +} diff --git a/src/rust/wcdb/src/utils.rs b/src/rust/wcdb/src/utils.rs new file mode 100644 index 000000000..e5ced5dfc --- /dev/null +++ b/src/rust/wcdb/src/utils.rs @@ -0,0 +1,44 @@ +use std::borrow::Cow; +use std::ffi::{c_char, CStr, CString}; + +pub(crate) trait ToCow { + fn to_cow(&self) -> Cow; +} + +impl ToCow for *const c_char { + /// 将 C 类型字符串指针,转换成 Cow 类型,分三种情况 + /// 1. 如 C 类型字符串指针为 NULL,则转成的 Cow 为 Cow::Borrow 类型,实际指向一个空字符串地址 + /// 2. 如 C 类型字符串指针不为 NULL,且不包含非法 UTF-8 字符,则转成的 Cow 为 Cow::Borrow 类型,实际为 C 字符串的内存切片 + /// 3. 如 C 类型字符串指针不为 NULL,但包含非法 UTF-8 字符,则将非法字符转成 �,并拷贝构建一个 Cow 为 Cow::Owned 的类型 + /// 注:原 *const c_char 指针仍需要手动释放 + fn to_cow(&self) -> Cow { + if *self == std::ptr::null() { + return Cow::Borrowed(""); + } + unsafe { CStr::from_ptr(*self).to_string_lossy() } + } +} + +pub(crate) trait ToCString { + fn to_cstring(&self) -> CString; +} + +impl ToCString for &str { + /// 根据 &str 创建新的 CString 对象,返回 *const c_char 指针和所有权 + fn to_cstring(&self) -> CString { + CString::new(*self).unwrap_or_default() + } +} + +impl ToCString for String { + /// 根据 String 创建新的 CString 对象,返回 *const c_char 指针和所有权 + fn to_cstring(&self) -> CString { + self.as_str().to_cstring() + } +} + +impl<'a> ToCString for Cow<'a, str> { + fn to_cstring(&self) -> CString { + self.as_ref().to_cstring() + } +} diff --git a/src/rust/wcdb/src/winq/bind_parameter.rs b/src/rust/wcdb/src/winq/bind_parameter.rs new file mode 100644 index 000000000..0aa77674f --- /dev/null +++ b/src/rust/wcdb/src/winq/bind_parameter.rs @@ -0,0 +1,136 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use std::ffi::{c_char, c_int, c_void}; +use std::sync::LazyLock; + +extern "C" { + fn WCDBRustBindParameter_createQuestionSignType(num: c_int) -> *mut c_void; + + fn WCDBRustBindParameter_createColonSignType(name: *const c_char) -> *mut c_void; + + fn WCDBRustBindParameter_createAtSignType(name: *const c_char) -> *mut c_void; + + fn WCDBRustBindParameter_createDollarSignType(name: *const c_char) -> *mut c_void; + + fn WCDBRustBindParameter_bindParameters(size: c_int) -> *mut *mut c_void; +} + +pub struct BindParameter { + identifier: Identifier, +} + +impl CppObjectTrait for BindParameter { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for BindParameter { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for BindParameter { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for BindParameter { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +pub trait BindParameterParam { + fn create_cpp_obj(&self) -> *mut c_void; +} + +impl BindParameterParam for i32 { + fn create_cpp_obj(&self) -> *mut c_void { + unsafe { WCDBRustBindParameter_createQuestionSignType(*self) } + } +} + +impl BindParameterParam for &str { + fn create_cpp_obj(&self) -> *mut c_void { + let cstr = self.to_cstring(); + unsafe { WCDBRustBindParameter_createColonSignType(cstr.as_ptr()) } + } +} + +impl BindParameter { + pub fn new(param: T) -> Self { + let cpp_obj = param.create_cpp_obj(); + BindParameter { + identifier: Identifier::new(CPPType::BindParameter, Some(cpp_obj)), + } + } + + pub fn at(name: &str) -> Self { + let cpp_obj = + { unsafe { WCDBRustBindParameter_createAtSignType(name.to_cstring().as_ptr()) } }; + BindParameter { + identifier: Identifier::new(CPPType::BindParameter, Some(cpp_obj)), + } + } + + pub fn colon(name: &str) -> Self { + BindParameter::new(name) + } + + pub fn dollar(name: &str) -> Self { + let cpp_obj = + { unsafe { WCDBRustBindParameter_createDollarSignType(name.to_cstring().as_ptr()) } }; + BindParameter { + identifier: Identifier::new(CPPType::BindParameter, Some(cpp_obj)), + } + } + + pub fn bind_parameters(num: i32) -> Vec { + if num <= 0 { + return vec![]; + } + let size: usize = num as usize; + + let cpp_obj_c_vec = unsafe { WCDBRustBindParameter_bindParameters(num as c_int) }; + let cpp_obj_vec_slice: &[*mut c_void] = + unsafe { std::slice::from_raw_parts(cpp_obj_c_vec, size) }; + let cpp_obj_vec = cpp_obj_vec_slice.to_vec(); + let mut bind_parameter_vec: Vec = Vec::with_capacity(size); + for index in 0..size { + let identifier = Identifier::new(CPPType::BindParameter, Some(cpp_obj_vec[index])); + bind_parameter_vec.push(BindParameter { identifier }); + } + unsafe { libc::free(cpp_obj_c_vec as *mut c_void) }; + bind_parameter_vec + } +} + +pub static DEF: LazyLock = LazyLock::new(|| BindParameter::new(0)); //? +pub static _1: LazyLock = LazyLock::new(|| BindParameter::new(1)); //?1 +pub static _2: LazyLock = LazyLock::new(|| BindParameter::new(2)); //?2 +pub static _3: LazyLock = LazyLock::new(|| BindParameter::new(3)); //?3 +pub static _4: LazyLock = LazyLock::new(|| BindParameter::new(4)); //?4 +pub static _5: LazyLock = LazyLock::new(|| BindParameter::new(5)); //?5 +pub static _6: LazyLock = LazyLock::new(|| BindParameter::new(6)); //?6 +pub static _7: LazyLock = LazyLock::new(|| BindParameter::new(7)); //?7 +pub static _8: LazyLock = LazyLock::new(|| BindParameter::new(8)); //?8 +pub static _9: LazyLock = LazyLock::new(|| BindParameter::new(9)); //?9 +pub static _10: LazyLock = LazyLock::new(|| BindParameter::new(10)); //?10 diff --git a/src/rust/wcdb/src/winq/column.rs b/src/rust/wcdb/src/winq/column.rs new file mode 100644 index 000000000..571a962ba --- /dev/null +++ b/src/rust/wcdb/src/winq/column.rs @@ -0,0 +1,512 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_basic_expression::BasicExpression; +use crate::base::param::enum_expression_ref::ExpressionRef; +use crate::base::param::enum_string_column_def::StringColumnDef; +use crate::base::param::enum_string_schema::StringSchema; +use crate::utils::ToCString; +use crate::winq::column_def::ColumnDef; +use crate::winq::column_type::ColumnType; +use crate::winq::expression::Expression; +use crate::winq::expression_convertible::ExpressionConvertibleTrait; +use crate::winq::expression_operable::{ExpressionOperable, ExpressionOperableTrait}; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::indexed_column_convertible::IndexedColumnConvertibleTrait; +use crate::winq::ordering_term::{Order, OrderingTerm}; +use crate::winq::result_column::ResultColumn; +use crate::winq::result_column_convertible_trait::ResultColumnConvertibleTrait; +use crate::winq::statement_select::StatementSelect; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustColumn_createWithName(name: *const c_char, binding: *mut c_void) -> *mut c_void; + + // copy() + + fn WCDBRustColumn_inTable(column: *mut c_void, table: *const c_char); + + fn WCDBRustColumn_ofSchema( + column: *mut c_void, + r#type: c_int, + schema: *mut c_void, + schema_name: *const c_char, + ); + + fn WCDBRustColumn_createAll() -> *mut c_void; + + fn WCDBRustColumn_createRowId() -> *mut c_void; + + fn WCDBRustColumn_configAlias(cpp_obj: *mut c_void, alias: *const c_char) -> *mut c_void; +} + +pub struct Column { + expression_operable: ExpressionOperable, +} + +pub trait ColumnTrait: + ExpressionConvertibleTrait + IndexedColumnConvertibleTrait + ResultColumnConvertibleTrait +{ + fn as_(&self, alias: &str) -> ResultColumn; + + fn order(&self, order: Order) -> OrderingTerm; + + fn as_def(&self, column_type: ColumnType) -> ColumnDef; +} + +pub trait ColumnStaticTrait { + fn table(&self, table: &str) -> &Column; + fn of<'a, T: Into>>(&self, schema: T) -> &Column; + fn all() -> Column; + + fn row_id() -> Column; +} + +impl CppObjectTrait for Column { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.expression_operable.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.expression_operable.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.expression_operable.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for Column { + fn as_cpp_object(&self) -> &CppObject { + self.expression_operable.as_cpp_object() + } +} + +impl IdentifierTrait for Column { + fn get_type(&self) -> CPPType { + self.expression_operable.get_type() + } + + fn get_description(&self) -> String { + self.expression_operable.get_description() + } +} + +impl IdentifierConvertibleTrait for Column { + fn as_identifier(&self) -> &Identifier { + self.expression_operable.as_identifier() + } +} + +impl ExpressionConvertibleTrait for Column {} + +impl ExpressionOperableTrait for Column { + fn is_null(&self) -> Expression { + self.expression_operable.is_null() + } + + fn not_null(&self) -> Expression { + self.expression_operable.not_null() + } + + fn or<'a, E>(&self, operand: E) -> Expression + where + E: Into>, + { + self.expression_operable.or(operand) + } + + fn and<'a, E>(&self, operand: E) -> Expression + where + E: Into>, + { + self.expression_operable.and(operand) + } + + fn multiply<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.multiply(operand) + } + + fn divide<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.divide(operand) + } + + fn mod_<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.mod_(operand) + } + + fn add<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.add(operand) + } + + fn minus<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.minus(operand) + } + + fn left_shift<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.left_shift(operand) + } + + fn right_shift<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.right_shift(operand) + } + + fn bit_and<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.bit_and(operand) + } + + fn bit_or<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.bit_or(operand) + } + + fn lt<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.lt(operand) + } + + fn le<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.le(operand) + } + + fn gt<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.gt(operand) + } + + fn ge<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.ge(operand) + } + + fn eq<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.eq(operand) + } + + fn not_eq<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.not_eq(operand) + } + + fn concat<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.concat(operand) + } + + fn between<'a, T, V>(&self, begin: T, end: V) -> Expression + where + T: Into>, + V: Into>, + { + self.expression_operable.between(begin, end) + } + + fn not_between<'a, T, V>(&self, begin: T, end: V) -> Expression + where + T: Into>, + V: Into>, + { + self.expression_operable.not_between(begin, end) + } + + fn in_<'a, S>(&self, operands: Vec) -> Expression + where + S: Into>, + { + self.expression_operable + .in_(Identifier::get_cpp_type(self), operands, false) + } + + fn not_in<'a, S>(&self, operands: Vec) -> Expression + where + S: Into>, + { + self.expression_operable + .not_in(Identifier::get_cpp_type(self), operands, true) + } + + fn in_statement_select(&self, stat: &StatementSelect) -> Expression { + self.expression_operable.in_statement_select(stat) + } + + fn not_in_statement_select(&self, stat: &StatementSelect) -> Expression { + self.expression_operable.not_in_statement_select(stat) + } + + fn in_table(&self, table: &str) -> Expression { + self.expression_operable.in_table(table) + } + + fn not_in_table(&self, table: &str) -> Expression { + self.expression_operable.not_in_table(table) + } + + fn collate(&self, collation: &str) -> Expression { + self.expression_operable.collate(collation) + } + + fn substr(&self, start: i64, length: i64) -> Expression { + self.expression_operable.substr(start, length) + } + + fn like(&self, content: &str) -> Expression { + self.expression_operable.like(content) + } + + fn not_like(&self, content: &str) -> Expression { + self.expression_operable.not_like(content) + } + + fn glob(&self, content: &str) -> Expression { + self.expression_operable.glob(content) + } + + fn not_glob(&self, content: &str) -> Expression { + self.expression_operable.not_glob(content) + } + + fn match_(&self, content: &str) -> Expression { + self.expression_operable.match_(content) + } + + fn not_match(&self, content: &str) -> Expression { + self.expression_operable.not_match(content) + } + + fn regexp(&self, content: &str) -> Expression { + self.expression_operable.regexp(content) + } + + fn not_regexp(&self, content: &str) -> Expression { + self.expression_operable.not_regexp(content) + } + + fn is<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.is(operand) + } + + fn is_not<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.is_not(operand) + } + + fn avg(&self) -> Expression { + self.expression_operable.avg() + } + + fn count(&self) -> Expression { + self.expression_operable.count() + } + + fn group_concat(&self) -> Expression { + self.expression_operable.group_concat() + } + + fn group_concat_string(&self, separator: &str) -> Expression { + self.expression_operable.group_concat_string(separator) + } + + fn max(&self) -> Expression { + self.expression_operable.max() + } + + fn min(&self) -> Expression { + self.expression_operable.min() + } + + fn sum(&self) -> Expression { + self.expression_operable.sum() + } + + fn total(&self) -> Expression { + self.expression_operable.total() + } + + fn abs(&self) -> Expression { + self.expression_operable.abs() + } + + fn hex(&self) -> Expression { + self.expression_operable.hex() + } + + fn length(&self) -> Expression { + self.expression_operable.length() + } + + fn lower(&self) -> Expression { + self.expression_operable.lower() + } + + fn upper(&self) -> Expression { + self.expression_operable.upper() + } + + fn round(&self) -> Expression { + self.expression_operable.round() + } + + fn match_info(&self) -> Expression { + self.expression_operable.match_info() + } + + fn offsets(&self) -> Expression { + self.expression_operable.offsets() + } + + fn snippet(&self) -> Expression { + self.expression_operable.snippet() + } + + fn bm25(&self) -> Expression { + self.expression_operable.bm25() + } + + fn highlight(&self) -> Expression { + self.expression_operable.highlight() + } + + fn substring_match_info(&self) -> Expression { + self.expression_operable.substring_match_info() + } +} + +impl IndexedColumnConvertibleTrait for Column {} + +impl ResultColumnConvertibleTrait for Column {} + +impl ColumnTrait for Column { + fn as_(&self, alias: &str) -> ResultColumn { + let c_alias = alias.to_cstring(); + let cpp_obj = unsafe { WCDBRustColumn_configAlias(self.get_cpp_obj(), c_alias.as_ptr()) }; + ResultColumn::new_with_cpp_obj(cpp_obj) + } + + fn order(&self, order: Order) -> OrderingTerm { + OrderingTerm::new(self).order(order) + } + + fn as_def(&self, column_type: ColumnType) -> ColumnDef { + ColumnDef::new(StringColumnDef::Column(self, Some(column_type))) + } +} + +impl ColumnStaticTrait for Column { + fn table(&self, table: &str) -> &Column { + let c_table = table.to_cstring(); + unsafe { WCDBRustColumn_inTable(self.get_cpp_obj(), c_table.as_ptr()) }; + self + } + + fn of<'a, T: Into>>(&self, schema: T) -> &Column { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { WCDBRustColumn_ofSchema(self.get_cpp_obj(), cpp_type as c_int, cpp_obj, name_ptr) } + self + } + + fn all() -> Column { + let cpp_obj = unsafe { WCDBRustColumn_createAll() }; + Column { + expression_operable: ExpressionOperable::new(CPPType::Column, Some(cpp_obj)), + } + } + + fn row_id() -> Column { + let cpp_obj = unsafe { WCDBRustColumn_createRowId() }; + Column { + expression_operable: ExpressionOperable::new(CPPType::Column, Some(cpp_obj)), + } + } +} + +impl Column { + pub fn new(name: &str, table_binding_opt: Option<*mut c_void>) -> Self { + let c_name = name.to_cstring(); + let cpp_obj = match table_binding_opt { + Some(table_binding) => unsafe { + WCDBRustColumn_createWithName(c_name.as_ptr(), table_binding) + }, + None => unsafe { WCDBRustColumn_createWithName(c_name.as_ptr(), std::ptr::null_mut()) }, + }; + Self { + expression_operable: ExpressionOperable::new(CPPType::Column, Some(cpp_obj)), + } + } + + pub(crate) fn in_<'a, S>( + &self, + left_cpp_type: CPPType, + operands: Vec, + is_not: bool, + ) -> Expression + where + S: Into>, + { + self.expression_operable + .in_(left_cpp_type, operands, is_not) + } + + pub(crate) fn not_in<'a, S>( + &self, + left_cpp_type: CPPType, + operands: Vec, + is_not: bool, + ) -> Expression + where + S: Into>, + { + self.expression_operable + .in_(left_cpp_type, operands, is_not) + } +} diff --git a/src/rust/wcdb/src/winq/column_constraint.rs b/src/rust/wcdb/src/winq/column_constraint.rs new file mode 100644 index 000000000..449b37948 --- /dev/null +++ b/src/rust/wcdb/src/winq/column_constraint.rs @@ -0,0 +1,188 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_basic_expression::BasicExpression; +use crate::utils::ToCString; +use crate::winq::conflict_action::ConflictAction; +use crate::winq::foreign_key::ForeignKey; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use std::ffi::{c_char, c_double, c_int, c_longlong, c_void}; + +extern "C" { + fn WCDBRustColumnConstraint_create(name: *const c_char) -> *mut c_void; + + fn WCDBRustColumnConstraint_configPrimaryKey(cpp_obj: *mut c_void); + + fn WCDBRustColumnConstraint_configAutoIncrement(cpp_obj: *mut c_void); + + fn WCDBRustColumnConstraint_configNotNull(cpp_obj: *mut c_void); + + fn WCDBRustColumnConstraint_configConflictAction(cpp_obj: *mut c_void, action: c_int); + + fn WCDBRustColumnConstraint_configUnique(cpp_obj: *mut c_void); + fn WCDBRustColumnConstraint_configDefaultValue( + cpp_obj: *mut c_void, + cpp_type: c_int, + int_value: c_longlong, + double_value: c_double, + string_value: *const c_char, + ); + + fn WCDBRustColumnConstraint_configCollation(cpp_obj: *mut c_void, collation: *const c_char); + + fn WCDBRustColumnConstraint_configForeignKey(cpp_obj: *mut c_void, foreign_key: *mut c_void); + + fn WCDBRustColumnConstraint_configUnIndex(cpp_obj: *mut c_void); +} + +pub struct ColumnConstraint { + identifier: Identifier, +} + +impl CppObjectTrait for ColumnConstraint { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for ColumnConstraint { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for ColumnConstraint { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for ColumnConstraint { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl ColumnConstraint { + pub fn new(column_name_opt: Option<&str>) -> Self { + let cpp_obj = match column_name_opt { + Some(column_name) => { + let c_name = column_name.to_cstring(); + unsafe { WCDBRustColumnConstraint_create(c_name.as_ptr()) } + } + None => unsafe { WCDBRustColumnConstraint_create(std::ptr::null_mut()) }, + }; + Self { + identifier: Identifier::new(CPPType::ColumnConstraint, Some(cpp_obj)), + } + } + + pub fn primary_key(&self) -> &Self { + unsafe { + WCDBRustColumnConstraint_configPrimaryKey(self.get_cpp_obj()); + } + self + } + + pub fn auto_increment(&self) -> &Self { + unsafe { + WCDBRustColumnConstraint_configAutoIncrement(self.get_cpp_obj()); + } + self + } + + pub fn not_null(&self) -> &Self { + unsafe { + WCDBRustColumnConstraint_configNotNull(self.get_cpp_obj()); + } + self + } + + pub fn conflict(&self, action: ConflictAction) -> &Self { + unsafe { + WCDBRustColumnConstraint_configConflictAction(self.get_cpp_obj(), action as c_int) + } + self + } + + pub fn unique(&self) -> &Self { + unsafe { + WCDBRustColumnConstraint_configUnique(self.get_cpp_obj()); + } + self + } + + pub fn default_to<'a, V>(&self, value: V) -> &Self + where + V: Into>, + { + let (cpp_type, int_value, double_value, string_value_opt) = value.into().get_params(); + let string_ptr = match string_value_opt.as_ref() { + Some(s) => s.as_ptr(), + None => std::ptr::null(), + }; + unsafe { + WCDBRustColumnConstraint_configDefaultValue( + self.get_cpp_obj(), + cpp_type as c_int, + int_value, + double_value, + string_ptr, + ); + } + self + } + + fn inner_default_to( + &self, + cpp_type: CPPType, + int_value: i64, + double_value: f64, + string_value: *const c_char, + ) { + unsafe { + WCDBRustColumnConstraint_configDefaultValue( + self.get_cpp_obj(), + cpp_type as i32, + int_value as c_longlong, + double_value as c_double, + string_value, + ); + } + } + + pub fn collate(&self, collation: &str) -> &Self { + let cstr = collation.to_cstring(); + unsafe { WCDBRustColumnConstraint_configCollation(self.get_cpp_obj(), cstr.as_ptr()) } + self + } + + pub fn foreign_key(&self, foreign_key: &ForeignKey) -> &Self { + unsafe { + WCDBRustColumnConstraint_configForeignKey( + self.get_cpp_obj(), + CppObject::get(foreign_key), + ); + } + self + } + + pub fn un_index(&self) -> &Self { + unsafe { + WCDBRustColumnConstraint_configUnIndex(self.get_cpp_obj()); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/column_def.rs b/src/rust/wcdb/src/winq/column_def.rs new file mode 100644 index 000000000..c8b0367b5 --- /dev/null +++ b/src/rust/wcdb/src/winq/column_def.rs @@ -0,0 +1,142 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_basic_expression::BasicExpression; +use crate::base::param::enum_string_column_def::StringColumnDef; +use crate::utils::ToCString; +use crate::winq::column_constraint::ColumnConstraint; +use crate::winq::foreign_key::ForeignKey; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustColumnDef_create( + cpp_type: c_int, + column_cpp_obj: *mut c_void, + name: *const c_char, + column_type: c_int, + ) -> *mut c_void; + + fn WCDBRustColumnDef_constraint(cpp_obj: *mut c_void, constraint_cpp_obj: *mut c_void); +} + +pub struct ColumnDef { + identifier: Identifier, +} + +impl CppObjectTrait for ColumnDef { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for ColumnDef { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for ColumnDef { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for ColumnDef { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl ColumnDef { + pub fn new<'a, T>(param: T) -> ColumnDef + where + T: Into>, + { + let cpp_obj = match param.into() { + StringColumnDef::String(str, column_type_opt) => { + let column_type = match column_type_opt { + Some(column_type) => column_type as c_int, + None => 0, + }; + let c_name = str.to_cstring(); + unsafe { + WCDBRustColumnDef_create( + CPPType::String as c_int, + std::ptr::null_mut(), + c_name.as_ptr(), + column_type, + ) + } + } + StringColumnDef::Column(column, column_type_opt) => { + let column_type = match column_type_opt { + Some(column_type) => column_type as c_int, + None => 0, + }; + unsafe { + WCDBRustColumnDef_create( + Identifier::get_cpp_type(column) as c_int, + CppObject::get(column), + std::ptr::null_mut(), + column_type, + ) + } + } + }; + Self { + identifier: Identifier::new(CPPType::ColumnDef, Some(cpp_obj)), + } + } + + pub fn constraint(&self, constraint: &ColumnConstraint) -> &Self { + unsafe { + WCDBRustColumnDef_constraint(self.get_cpp_obj(), CppObject::get(constraint)); + } + self + } + + pub fn make_primary(&self, is_auto_increment: Option) -> &Self { + let column_constraint = ColumnConstraint::new(None); + column_constraint.primary_key(); + if let Some(true) = is_auto_increment { + column_constraint.auto_increment(); + } + self.constraint(&column_constraint) + } + + pub fn make_default_to<'a, T>(&self, value: T) -> &Self + where + T: Into>, + { + self.constraint(ColumnConstraint::new(None).default_to(value)) + } + + pub fn make_unique(&self) -> &Self { + self.constraint(ColumnConstraint::new(None).unique()) + } + + pub fn make_not_null(&self) -> &Self { + self.constraint(ColumnConstraint::new(None).not_null()) + } + + pub fn make_foreign_key(&self, foreign_key: &ForeignKey) -> &Self { + self.constraint(ColumnConstraint::new(None).foreign_key(foreign_key)) + } + + pub fn make_not_indexed(&self) -> &Self { + self.constraint(ColumnConstraint::new(None).un_index()) + } +} diff --git a/src/rust/wcdb/src/winq/column_type.rs b/src/rust/wcdb/src/winq/column_type.rs new file mode 100644 index 000000000..90ef6c91b --- /dev/null +++ b/src/rust/wcdb/src/winq/column_type.rs @@ -0,0 +1,32 @@ +use crate::winq::identifier::CPPType; + +#[derive(PartialEq)] +pub enum ColumnType { + Null = 0, + Integer = 1, + Float = 2, + Text = 3, + BLOB = 4, +} + +impl ColumnType { + pub fn cpp_type(&self) -> CPPType { + match self { + ColumnType::Null => CPPType::Null, + ColumnType::Integer => CPPType::Int, + ColumnType::Float => CPPType::Double, + ColumnType::Text => CPPType::String, + ColumnType::BLOB => CPPType::BindParameter, + } + } + + pub fn value_of(value: i32) -> Self { + match value { + 1 => ColumnType::Integer, + 2 => ColumnType::Float, + 3 => ColumnType::Text, + 4 => ColumnType::BLOB, + _ => ColumnType::Null, + } + } +} diff --git a/src/rust/wcdb/src/winq/common_table_expression.rs b/src/rust/wcdb/src/winq/common_table_expression.rs new file mode 100644 index 000000000..61597b0c1 --- /dev/null +++ b/src/rust/wcdb/src/winq/common_table_expression.rs @@ -0,0 +1,90 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::utils::ToCString; +use crate::winq::column::Column; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement_select::StatementSelect; +use std::ffi::{c_char, c_void}; + +extern "C" { + fn WCDBRustCommonTableExpression_createWithTable(table_name: *const c_char) -> *mut c_void; + + fn WCDBRustCommonTableExpression_configColumn(self_obj: *mut c_void, column: *mut c_void); + + fn WCDBRustCommonTableExpression_configSelectStatement( + self_obj: *mut c_void, + select: *mut c_void, + ); +} + +pub struct CommonTableExpression { + identifier: Identifier, +} + +impl CppObjectTrait for CommonTableExpression { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for CommonTableExpression { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for CommonTableExpression { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for CommonTableExpression { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl CommonTableExpression { + pub fn new(table_name: &str) -> Self { + let c_table_name = table_name.to_cstring(); + let cpp_obj = + unsafe { WCDBRustCommonTableExpression_createWithTable(c_table_name.as_ptr()) }; + Self { + identifier: Identifier::new(CPPType::CommonTableExpression, Some(cpp_obj)), + } + } + + pub fn column(&self, column: &Column) -> &Self { + unsafe { + WCDBRustCommonTableExpression_configColumn( + self.identifier.get_cpp_obj(), + column.get_cpp_obj(), + ); + } + self + } + + pub fn as_(&self, select: &StatementSelect) -> &Self { + unsafe { + WCDBRustCommonTableExpression_configSelectStatement( + self.get_cpp_obj(), + CppObject::get(select), + ) + } + self + } +} diff --git a/src/rust/wcdb/src/winq/conflict_action.rs b/src/rust/wcdb/src/winq/conflict_action.rs new file mode 100644 index 000000000..d192f07a4 --- /dev/null +++ b/src/rust/wcdb/src/winq/conflict_action.rs @@ -0,0 +1,9 @@ +#[repr(i32)] +pub enum ConflictAction { + None, + Replace, + Rollback, + Abort, + Fail, + Ignore, +} diff --git a/src/rust/wcdb/src/winq/expression.rs b/src/rust/wcdb/src/winq/expression.rs new file mode 100644 index 000000000..717ea6264 --- /dev/null +++ b/src/rust/wcdb/src/winq/expression.rs @@ -0,0 +1,779 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_basic_expression::BasicExpression; +use crate::base::param::enum_expression_ref::ExpressionRef; +use crate::base::param::enum_string_expression::StringExpression; +use crate::base::param::enum_string_schema::StringSchema; +use crate::base::param::enum_string_window_def::StringWindowDef; +use crate::orm::field::Field; +use crate::utils::ToCString; +use crate::winq::bind_parameter::BindParameter; +use crate::winq::column::Column; +use crate::winq::column_type::ColumnType; +use crate::winq::expression_convertible::ExpressionConvertibleTrait; +use crate::winq::expression_operable::{ExpressionOperable, ExpressionOperableTrait}; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::indexed_column_convertible::IndexedColumnConvertibleTrait; +use crate::winq::literal_value::LiteralValue; +use crate::winq::result_column::ResultColumn; +use crate::winq::result_column_convertible_trait::ResultColumnConvertibleTrait; +use crate::winq::statement_select::StatementSelect; +use std::ffi::{c_char, c_double, c_int, c_longlong, c_void}; + +extern "C" { + fn WCDBRustExpression_create(value_type: c_int, cpp_obj: *mut c_void) -> *mut c_void; + + fn WCDBRustExpression_createWithFunction(func: *const c_char) -> *mut c_void; + + fn WCDBRustExpression_createWithExistStatement(cpp_obj: *mut c_void) -> *mut c_void; + + fn WCDBRustExpression_createWithNotExistStatement(cpp_obj: *mut c_void) -> *mut c_void; + + fn WCDBRustExpression_setWithSchema( + expression: *mut c_void, + schema_type: c_int, + schema_object: *mut c_void, + schema_string: *const c_char, + ); + + fn WCDBRustExpression_argument( + cpp_obj: *mut c_void, + cpp_type: c_int, + int_value: c_longlong, + double_value: c_double, + string_value: *const c_char, + ); + + fn WCDBRustExpression_invoke(cpp_obj: *mut c_void); + + fn WCDBRustExpression_invokeAll(cpp_obj: *mut c_void); + + fn WCDBRustExpression_distinct(cpp_obj: *mut c_void); + + fn WCDBRustExpression_cast( + cpp_type: c_int, + object: *mut c_void, + column_name: *const c_char, + ) -> *mut c_void; + + fn WCDBRustExpression_as(cpp_obj: *mut c_void, column_type: c_int); + + fn WCDBRustExpression_configAlias(cpp_obj: *mut c_void, alias: *const c_char) -> *mut c_void; + + fn WCDBRustExpression_caseWithExp( + cpp_type: c_int, + object: *mut c_void, + column_name: *const c_char, + ) -> *mut c_void; + + fn WCDBRustExpression_setWithWhenExp( + cpp_obj: *mut c_void, + cpp_type: c_int, + int_value: c_longlong, + double_value: c_double, + string_value: *const c_char, + ); + + fn WCDBRustExpression_setWithThenExp( + cpp_obj: *mut c_void, + cpp_type: c_int, + int_value: c_longlong, + double_value: c_double, + string_value: *const c_char, + ); + + fn WCDBRustExpression_setWithElseExp( + cpp_obj: *mut c_void, + cpp_type: c_int, + int_value: c_longlong, + double_value: c_double, + string_value: *const c_char, + ); + + fn WCDBRustExpression_escapeWith(cpp_obj: *mut c_void, string_value: *const c_char); + + fn WCDBRustExpression_createWithWindowFunction(func_name: *const c_char) -> *mut c_void; + + fn WCDBRustExpression_filter(cpp_obj: *mut c_void, condition: *mut c_void); + + fn WCDBRustExpression_overWindowDef(cpp_obj: *mut c_void, window_def: *mut c_void); + + fn WCDBRustExpression_overWindow(cpp_obj: *mut c_void, window: *const c_char); +} + +#[derive(Debug)] +pub struct Expression { + expression_operable: ExpressionOperable, +} + +impl CppObjectTrait for Expression { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.expression_operable.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.expression_operable.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.expression_operable.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for Expression { + fn as_cpp_object(&self) -> &CppObject { + self.expression_operable.as_cpp_object() + } +} + +impl IdentifierTrait for Expression { + fn get_type(&self) -> CPPType { + self.expression_operable.get_type() + } + + fn get_description(&self) -> String { + self.expression_operable.get_description() + } +} + +impl IdentifierConvertibleTrait for Expression { + fn as_identifier(&self) -> &Identifier { + self.expression_operable.as_identifier() + } +} + +impl ExpressionConvertibleTrait for Expression {} + +impl ExpressionOperableTrait for Expression { + fn is_null(&self) -> Expression { + self.expression_operable.is_null() + } + + fn not_null(&self) -> Expression { + self.expression_operable.not_null() + } + + fn or<'a, E>(&self, operand: E) -> Expression + where + E: Into>, + { + self.expression_operable.or(operand) + } + + fn and<'a, E>(&self, operand: E) -> Expression + where + E: Into>, + { + self.expression_operable.and(operand) + } + + fn multiply<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.multiply(operand) + } + + fn divide<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.divide(operand) + } + + fn mod_<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.mod_(operand) + } + + fn add<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.add(operand) + } + + fn minus<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.minus(operand) + } + + fn left_shift<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.left_shift(operand) + } + + fn right_shift<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.right_shift(operand) + } + + fn bit_and<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.bit_and(operand) + } + + fn bit_or<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.bit_or(operand) + } + + fn lt<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.lt(operand) + } + + fn le<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.le(operand) + } + + fn gt<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.gt(operand) + } + + fn ge<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.ge(operand) + } + + fn eq<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.eq(operand) + } + + fn not_eq<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.not_eq(operand) + } + + fn concat<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.concat(operand) + } + + fn between<'a, T, V>(&self, begin: T, end: V) -> Expression + where + T: Into>, + V: Into>, + { + self.expression_operable.between(begin, end) + } + + fn not_between<'a, T, V>(&self, begin: T, end: V) -> Expression + where + T: Into>, + V: Into>, + { + self.expression_operable.not_between(begin, end) + } + + fn in_<'a, S>(&self, operands: Vec) -> Expression + where + S: Into>, + { + self.expression_operable + .in_(Identifier::get_cpp_type(self), operands, false) + } + + fn not_in<'a, S>(&self, operands: Vec) -> Expression + where + S: Into>, + { + self.expression_operable + .not_in(Identifier::get_cpp_type(self), operands, true) + } + + fn in_statement_select(&self, stat: &StatementSelect) -> Expression { + self.expression_operable.in_statement_select(stat) + } + + fn not_in_statement_select(&self, stat: &StatementSelect) -> Expression { + self.expression_operable.not_in_statement_select(stat) + } + + fn in_table(&self, table: &str) -> Expression { + self.expression_operable.in_table(table) + } + + fn not_in_table(&self, table: &str) -> Expression { + self.expression_operable.not_in_table(table) + } + + fn collate(&self, collation: &str) -> Expression { + self.expression_operable.collate(collation) + } + + fn substr(&self, start: i64, length: i64) -> Expression { + self.expression_operable.substr(start, length) + } + + fn like(&self, content: &str) -> Expression { + self.expression_operable.like(content) + } + + fn not_like(&self, content: &str) -> Expression { + self.expression_operable.not_like(content) + } + + fn glob(&self, content: &str) -> Expression { + self.expression_operable.glob(content) + } + + fn not_glob(&self, content: &str) -> Expression { + self.expression_operable.not_glob(content) + } + + fn match_(&self, content: &str) -> Expression { + self.expression_operable.match_(content) + } + + fn not_match(&self, content: &str) -> Expression { + self.expression_operable.not_match(content) + } + + fn regexp(&self, content: &str) -> Expression { + self.expression_operable.regexp(content) + } + + fn not_regexp(&self, content: &str) -> Expression { + self.expression_operable.not_regexp(content) + } + + fn is<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.is(operand) + } + + fn is_not<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.expression_operable.is_not(operand) + } + + fn avg(&self) -> Expression { + self.expression_operable.avg() + } + + fn count(&self) -> Expression { + self.expression_operable.count() + } + + fn group_concat(&self) -> Expression { + self.expression_operable.group_concat() + } + + fn group_concat_string(&self, separator: &str) -> Expression { + self.expression_operable.group_concat_string(separator) + } + + fn max(&self) -> Expression { + self.expression_operable.max() + } + + fn min(&self) -> Expression { + self.expression_operable.min() + } + + fn sum(&self) -> Expression { + self.expression_operable.sum() + } + + fn total(&self) -> Expression { + self.expression_operable.total() + } + + fn abs(&self) -> Expression { + self.expression_operable.abs() + } + + fn hex(&self) -> Expression { + self.expression_operable.hex() + } + + fn length(&self) -> Expression { + self.expression_operable.length() + } + + fn lower(&self) -> Expression { + self.expression_operable.lower() + } + + fn upper(&self) -> Expression { + self.expression_operable.upper() + } + + fn round(&self) -> Expression { + self.expression_operable.round() + } + + fn match_info(&self) -> Expression { + self.expression_operable.match_info() + } + + fn offsets(&self) -> Expression { + self.expression_operable.offsets() + } + + fn snippet(&self) -> Expression { + self.expression_operable.snippet() + } + + fn bm25(&self) -> Expression { + self.expression_operable.bm25() + } + + fn highlight(&self) -> Expression { + self.expression_operable.highlight() + } + + fn substring_match_info(&self) -> Expression { + self.expression_operable.substring_match_info() + } +} + +impl IndexedColumnConvertibleTrait for Expression {} + +impl ResultColumnConvertibleTrait for Expression {} + +impl Expression { + pub(crate) fn new_empty() -> Self { + Expression { + expression_operable: ExpressionOperable::new(CPPType::Expression, None), + } + } + + pub fn new<'a, T>(value: T) -> Self + where + T: Into>, + { + let (value_cpp_type, value_cpp_obj) = match value.into() { + ExpressionNewParam::BindParameter(value) => { + (Identifier::get_cpp_type(value), CppObject::get(value)) + } + ExpressionNewParam::LiteralValue(value) => { + (Identifier::get_cpp_type(value), CppObject::get(value)) + } + ExpressionNewParam::Column(value) => { + (Identifier::get_cpp_type(value), CppObject::get(value)) + } + ExpressionNewParam::StatementSelect(value) => { + (Identifier::get_cpp_type(value), CppObject::get(value)) + } + }; + let cpp_obj = unsafe { WCDBRustExpression_create(value_cpp_type as c_int, value_cpp_obj) }; + Expression { + expression_operable: ExpressionOperable::new(CPPType::Expression, Some(cpp_obj)), + } + } + + pub fn function(func_name: &str) -> Self { + let cpp_obj = + unsafe { WCDBRustExpression_createWithFunction(func_name.to_cstring().as_ptr()) }; + ExpressionOperable::create_expression(cpp_obj) + } + + pub fn schema<'a, T>(&self, param: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = param.into().get_params(); + let name_ptr = match name_opt.as_ref() { + Some(s) => s.as_ptr(), + None => std::ptr::null(), + }; + unsafe { + WCDBRustExpression_setWithSchema( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ) + } + self + } + + pub fn distinct(&self) -> &Self { + unsafe { + WCDBRustExpression_distinct(self.get_cpp_obj()); + } + self + } + + pub fn invoke(&self) -> &Self { + unsafe { + WCDBRustExpression_invoke(self.get_cpp_obj()); + } + self + } + + pub fn invoke_all(&self) -> &Self { + unsafe { + WCDBRustExpression_invokeAll(self.get_cpp_obj()); + } + self + } + + pub fn argument<'a, T>(&self, param: T) -> &Self + where + T: Into>, + { + let (arg_type, arg_long, arg_double, arg_cstr_opt) = param.into().get_params(); + let arg_string_ptr = match arg_cstr_opt.as_ref() { + Some(s) => s.as_ptr(), + None => std::ptr::null(), + }; + unsafe { + WCDBRustExpression_argument( + self.get_cpp_obj(), + arg_type as c_int, + arg_long, + arg_double, + arg_string_ptr, + ); + } + self + } + + pub fn escape(&self, content: &str) -> &Self { + let cstr = content.to_cstring(); + unsafe { + WCDBRustExpression_escapeWith(self.get_cpp_obj(), cstr.as_ptr()); + } + self + } + + pub fn exists(select: &StatementSelect) -> Self { + let cpp_obj = + unsafe { WCDBRustExpression_createWithExistStatement(CppObject::get(select)) }; + Self { + expression_operable: ExpressionOperable::new(CPPType::Expression, Some(cpp_obj)), + } + } + + pub fn not_exists(select: &StatementSelect) -> Self { + let cpp_obj = + unsafe { WCDBRustExpression_createWithNotExistStatement(CppObject::get(select)) }; + Self { + expression_operable: ExpressionOperable::new(CPPType::Expression, Some(cpp_obj)), + } + } + + pub fn cast<'a, T>(param: T) -> Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = param.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + let cpp_obj = unsafe { WCDBRustExpression_cast(cpp_type as c_int, cpp_obj, name_ptr) }; + + Self { + expression_operable: ExpressionOperable::new(CPPType::Expression, Some(cpp_obj)), + } + } + + pub fn as_(&self, column_type: ColumnType) -> &Self { + unsafe { WCDBRustExpression_as(self.get_cpp_obj(), column_type as c_int) }; + self + } + + pub fn as_result_column(&self, alias: &str) -> ResultColumn { + let c_string = alias.to_cstring(); + let cpp_obj = + unsafe { WCDBRustExpression_configAlias(self.get_cpp_obj(), c_string.as_ptr()) }; + ResultColumn::new_with_cpp_obj(cpp_obj) + } + + pub fn case_() -> Self { + let mut ret = Expression::new_empty(); + let cpp_obj = + unsafe { WCDBRustExpression_caseWithExp(0, 0 as *mut c_void, std::ptr::null()) }; + ret.set_cpp_obj(cpp_obj); + ret + } + + pub fn case<'a, T>(param: T) -> Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = param.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + let cpp_obj = + unsafe { WCDBRustExpression_caseWithExp(cpp_type as c_int, cpp_obj, name_ptr) }; + Self { + expression_operable: ExpressionOperable::new(CPPType::Expression, Some(cpp_obj)), + } + } + + pub fn when<'a, T>(&self, param: T) -> &Self + where + T: Into>, + { + let (arg_type, arg_long, arg_double, arg_string_opt) = param.into().get_params(); + let arg_string_ptr = match arg_string_opt.as_ref() { + Some(s) => s.as_ptr(), + None => std::ptr::null(), + }; + unsafe { + WCDBRustExpression_setWithWhenExp( + self.get_cpp_obj(), + arg_type as c_int, + arg_long as c_longlong, + arg_double, + arg_string_ptr, + ); + } + self + } + + pub fn then<'a, T>(&self, param: T) -> &Self + where + T: Into>, + { + let (arg_type, arg_long, arg_double, arg_string_opt) = param.into().get_params(); + let arg_string_ptr = match arg_string_opt.as_ref() { + Some(s) => s.as_ptr(), + None => std::ptr::null(), + }; + unsafe { + WCDBRustExpression_setWithThenExp( + self.get_cpp_obj(), + arg_type as c_int, + arg_long as c_longlong, + arg_double, + arg_string_ptr, + ); + } + self + } + + pub fn else_<'a, T>(&self, param: T) -> &Self + where + T: Into>, + { + let (arg_type, arg_long, arg_double, arg_string_opt) = param.into().get_params(); + let arg_string_ptr = match arg_string_opt.as_ref() { + Some(s) => s.as_ptr(), + None => std::ptr::null(), + }; + unsafe { + WCDBRustExpression_setWithElseExp( + self.get_cpp_obj(), + arg_type as c_int, + arg_long as c_longlong, + arg_double, + arg_string_ptr, + ); + } + self + } + + pub fn window_function(func_name: &str) -> Expression { + let cpp_obj = + unsafe { WCDBRustExpression_createWithWindowFunction(func_name.to_cstring().as_ptr()) }; + Self { + expression_operable: ExpressionOperable::new(CPPType::Expression, Some(cpp_obj)), + } + } + + pub fn filter(&self, condition: &Expression) -> &Expression { + unsafe { WCDBRustExpression_filter(self.get_cpp_obj(), condition.get_cpp_obj()) }; + self + } + + pub fn over<'a, T>(&self, window: T) -> &Self + where + T: Into>, + { + match window.into() { + StringWindowDef::String(str) => { + unsafe { + WCDBRustExpression_overWindow(self.get_cpp_obj(), str.to_cstring().as_ptr()); + }; + } + StringWindowDef::WindowDef(window_def) => { + unsafe { + WCDBRustExpression_overWindowDef( + self.get_cpp_obj(), + CppObject::get(window_def), + ); + }; + } + } + self + } +} + +pub enum ExpressionNewParam<'a> { + BindParameter(&'a BindParameter), + LiteralValue(&'a LiteralValue), + Column(&'a Column), + StatementSelect(&'a StatementSelect), +} + +impl<'a> From<&'a BindParameter> for ExpressionNewParam<'a> { + fn from(value: &'a BindParameter) -> Self { + ExpressionNewParam::BindParameter(value) + } +} + +impl<'a> From<&'a LiteralValue> for ExpressionNewParam<'a> { + fn from(value: &'a LiteralValue) -> Self { + ExpressionNewParam::LiteralValue(value) + } +} + +impl<'a> From<&'a Column> for ExpressionNewParam<'a> { + fn from(value: &'a Column) -> Self { + ExpressionNewParam::Column(value) + } +} + +impl<'a, T> From<&'a Field> for ExpressionNewParam<'a> { + fn from(value: &'a Field) -> Self { + ExpressionNewParam::Column(value.get_column()) + } +} + +impl<'a> From<&'a StatementSelect> for ExpressionNewParam<'a> { + fn from(value: &'a StatementSelect) -> Self { + ExpressionNewParam::StatementSelect(value) + } +} diff --git a/src/rust/wcdb/src/winq/expression_convertible.rs b/src/rust/wcdb/src/winq/expression_convertible.rs new file mode 100644 index 000000000..86885ac89 --- /dev/null +++ b/src/rust/wcdb/src/winq/expression_convertible.rs @@ -0,0 +1,3 @@ +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; + +pub trait ExpressionConvertibleTrait: IdentifierConvertibleTrait {} diff --git a/src/rust/wcdb/src/winq/expression_operable.rs b/src/rust/wcdb/src/winq/expression_operable.rs new file mode 100644 index 000000000..c0062c92c --- /dev/null +++ b/src/rust/wcdb/src/winq/expression_operable.rs @@ -0,0 +1,948 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_basic_expression::BasicExpression; +use crate::base::param::enum_expression_ref::ExpressionRef; +use crate::utils::ToCString; +use crate::winq::expression::Expression; +use crate::winq::expression_convertible::ExpressionConvertibleTrait; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement_select::StatementSelect; +use std::ffi::{c_char, c_double, c_int, c_void}; + +extern "C" { + fn WCDBRustExpressionOperable_nullOperate( + operand_type: c_int, + operand: *mut c_void, + is_not: bool, + ) -> *mut c_void; + + fn WCDBRustExpressionOperable_binaryOperate( + left_type: c_int, + left: *mut c_void, + right_type: c_int, + right_long: i64, + right_double: c_double, + right_string: *const c_char, + operator_type: c_int, + is_not: bool, + ) -> *mut c_void; + + fn WCDBRustExpressionOperable_betweenOperate( + operand_type: c_int, + operand: *mut c_void, + left_type: c_int, + left_long: *mut c_void, + left_double: c_double, + left_string: *const c_char, + right_type: c_int, + right_long: *mut c_void, + right_double: c_double, + right_string: *const c_char, + is_not: bool, + ) -> *mut c_void; + + fn WCDBRustExpressionOperable_inOperate( + operand_type: c_int, + operand: *mut c_void, + cpp_type: c_int, + long_array: *const i64, + double_array: *const c_double, + string_array: *const *const c_char, + array_length: c_int, + is_not: bool, + ) -> *mut c_void; + + fn WCDBRustExpressionOperable_inSelectionOperate( + operand_type: c_int, + operand: *mut c_void, + select: *mut c_void, + is_not: bool, + ) -> *mut c_void; + + fn WCDBRustExpressionOperable_inTableOperate( + cpp_type: c_int, + operand: *mut c_void, + table: *const c_char, + is_not: bool, + ) -> *mut c_void; + + fn WCDBRustExpressionOperable_collateOperate( + cpp_type: c_int, + operand: *mut c_void, + collation: *const c_char, + ) -> *mut c_void; +} + +#[derive(Debug)] +pub(crate) struct ExpressionOperable { + identifier: Identifier, +} + +impl CppObjectTrait for ExpressionOperable { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for ExpressionOperable { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierConvertibleTrait for ExpressionOperable { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl IdentifierTrait for ExpressionOperable { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl ExpressionConvertibleTrait for ExpressionOperable {} + +pub trait ExpressionOperableTrait { + fn is_null(&self) -> Expression; + + fn not_null(&self) -> Expression; + + fn or<'a, E>(&self, operand: E) -> Expression + where + E: Into>; + + fn and<'a, E>(&self, operand: E) -> Expression + where + E: Into>; + + fn multiply<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn divide<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn mod_<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn add<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn minus<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn left_shift<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn right_shift<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn bit_and<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn bit_or<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn lt<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn le<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn gt<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn ge<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn eq<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn not_eq<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn concat<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn between<'a, T, V>(&self, begin: T, end: V) -> Expression + where + T: Into>, + V: Into>; + + fn not_between<'a, T, V>(&self, begin: T, end: V) -> Expression + where + T: Into>, + V: Into>; + + fn in_<'a, S>(&self, operands: Vec) -> Expression + where + S: Into>; + + fn not_in<'a, S>(&self, operands: Vec) -> Expression + where + S: Into>; + + fn in_statement_select(&self, stat: &StatementSelect) -> Expression; + + fn not_in_statement_select(&self, stat: &StatementSelect) -> Expression; + + fn in_table(&self, table: &str) -> Expression; + + fn not_in_table(&self, table: &str) -> Expression; + + fn collate(&self, collation: &str) -> Expression; + + fn substr(&self, start: i64, length: i64) -> Expression; + + fn like(&self, content: &str) -> Expression; + + fn not_like(&self, content: &str) -> Expression; + + fn glob(&self, content: &str) -> Expression; + + fn not_glob(&self, content: &str) -> Expression; + + fn match_(&self, content: &str) -> Expression; + + fn not_match(&self, content: &str) -> Expression; + + fn regexp(&self, content: &str) -> Expression; + + fn not_regexp(&self, content: &str) -> Expression; + + fn is<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn is_not<'a, T>(&self, operand: T) -> Expression + where + T: Into>; + + fn avg(&self) -> Expression; + + fn count(&self) -> Expression; + + fn group_concat(&self) -> Expression; + + fn group_concat_string(&self, separator: &str) -> Expression; + + fn max(&self) -> Expression; + + fn min(&self) -> Expression; + + fn sum(&self) -> Expression; + + fn total(&self) -> Expression; + + fn abs(&self) -> Expression; + + fn hex(&self) -> Expression; + + fn length(&self) -> Expression; + + fn lower(&self) -> Expression; + + fn upper(&self) -> Expression; + + fn round(&self) -> Expression; + + fn match_info(&self) -> Expression; + + fn offsets(&self) -> Expression; + + fn snippet(&self) -> Expression; + + fn bm25(&self) -> Expression; + + fn highlight(&self) -> Expression; + + fn substring_match_info(&self) -> Expression; +} + +impl ExpressionOperableTrait for ExpressionOperable { + fn is_null(&self) -> Expression { + self.null_operate(false) + } + + fn not_null(&self) -> Expression { + self.null_operate(true) + } + + fn or<'a, E>(&self, operand: E) -> Expression + where + E: Into>, + { + let exp = match operand.into() { + ExpressionRef::ExpressionConvertible(obj) => obj, + }; + self.binary_operate(exp, BinaryOperatorType::Or, false) + } + + fn and<'a, E>(&self, operand: E) -> Expression + where + E: Into>, + { + let exp = match operand.into() { + ExpressionRef::ExpressionConvertible(obj) => obj, + }; + self.binary_operate(exp, BinaryOperatorType::And, false) + } + + fn multiply<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::Multiply, false) + } + + fn divide<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::Divide, false) + } + + fn mod_<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::Modulo, false) + } + + fn add<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::Plus, false) + } + + fn minus<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::Minus, false) + } + + fn left_shift<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::LeftShift, false) + } + + fn right_shift<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::RightShift, false) + } + + fn bit_and<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::BitwiseAnd, false) + } + + fn bit_or<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::BitwiseOr, false) + } + + fn lt<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::Less, false) + } + + fn le<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::LessOrEqual, false) + } + + fn gt<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::Greater, false) + } + + fn ge<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::GreaterOrEqual, false) + } + + fn eq<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::Equal, false) + } + + fn not_eq<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::NotEqual, false) + } + + fn concat<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::Concatenate, false) + } + + fn between<'a, T, V>(&self, begin: T, end: V) -> Expression + where + T: Into>, + V: Into>, + { + self.between_operate(begin, end, false) + } + + fn not_between<'a, T, V>(&self, begin: T, end: V) -> Expression + where + T: Into>, + V: Into>, + { + self.between_operate(begin, end, true) + } + + fn in_<'a, S>(&self, operands: Vec) -> Expression + where + S: Into>, + { + self.in_(CPPType::Expression, operands, false) + } + + fn not_in<'a, S>(&self, operands: Vec) -> Expression + where + S: Into>, + { + self.not_in(CPPType::Expression, operands, true) + } + + fn in_statement_select(&self, stat: &StatementSelect) -> Expression { + let cpp_obj = unsafe { + WCDBRustExpressionOperable_inSelectionOperate( + self.get_type() as i32, + CppObject::get(self), + CppObject::get(stat), + false, + ) + }; + Self::create_expression(cpp_obj) + } + + fn not_in_statement_select(&self, stat: &StatementSelect) -> Expression { + let cpp_obj = unsafe { + WCDBRustExpressionOperable_inSelectionOperate( + self.get_type() as i32, + CppObject::get(self), + CppObject::get(stat), + true, + ) + }; + Self::create_expression(cpp_obj) + } + + fn in_table(&self, table: &str) -> Expression { + let cpp_obj = unsafe { + WCDBRustExpressionOperable_inTableOperate( + self.get_type() as i32, + self.get_cpp_obj(), + table.to_cstring().as_ptr(), + true, + ) + }; + Self::create_expression(cpp_obj) + } + + fn not_in_table(&self, table: &str) -> Expression { + let cpp_obj = unsafe { + WCDBRustExpressionOperable_inTableOperate( + self.get_type() as i32, + self.get_cpp_obj(), + table.to_cstring().as_ptr(), + false, + ) + }; + Self::create_expression(cpp_obj) + } + + fn collate(&self, collation: &str) -> Expression { + let cpp_obj = unsafe { + WCDBRustExpressionOperable_collateOperate( + self.get_type() as i32, + self.get_cpp_obj(), + collation.to_cstring().as_ptr(), + ) + }; + Self::create_expression(cpp_obj) + } + + fn substr(&self, start: i64, length: i64) -> Expression { + let exp = Expression::function("SUBSTR"); + exp.argument(Some(self)); + exp.argument(start); + exp.argument(length); + exp + } + + fn like(&self, content: &str) -> Expression { + self.binary_operate(content, BinaryOperatorType::Like, false) + } + + fn not_like(&self, content: &str) -> Expression { + self.binary_operate(content, BinaryOperatorType::Like, true) + } + + fn glob(&self, content: &str) -> Expression { + self.binary_operate(content, BinaryOperatorType::GLOB, false) + } + + fn not_glob(&self, content: &str) -> Expression { + self.binary_operate(content, BinaryOperatorType::GLOB, true) + } + + fn match_(&self, content: &str) -> Expression { + self.binary_operate(content, BinaryOperatorType::Match, false) + } + + fn not_match(&self, content: &str) -> Expression { + self.binary_operate(content, BinaryOperatorType::Match, true) + } + + fn regexp(&self, content: &str) -> Expression { + self.binary_operate(content, BinaryOperatorType::RegExp, false) + } + + fn not_regexp(&self, content: &str) -> Expression { + self.binary_operate(content, BinaryOperatorType::RegExp, true) + } + + fn is<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::Is, false) + } + + fn is_not<'a, T>(&self, operand: T) -> Expression + where + T: Into>, + { + self.binary_operate(operand, BinaryOperatorType::Is, true) + } + + fn avg(&self) -> Expression { + let exp = Expression::function("AVG"); + exp.argument(Some(self)); + exp + } + + fn count(&self) -> Expression { + let exp = Expression::function("COUNT"); + exp.argument(Some(self)); + exp + } + + fn group_concat(&self) -> Expression { + let exp = Expression::function("GROUP_CONCAT"); + exp.argument(Some(self)); + exp + } + + fn group_concat_string(&self, separator: &str) -> Expression { + let exp = Expression::function("GROUP_CONCAT"); + exp.argument(Some(self)).argument(separator); + exp + } + + fn max(&self) -> Expression { + let exp = Expression::function("MAX"); + exp.argument(Some(self)); + exp + } + + fn min(&self) -> Expression { + let exp = Expression::function("MIN"); + exp.argument(Some(self)); + exp + } + + fn sum(&self) -> Expression { + let exp = Expression::function("SUM"); + exp.argument(Some(self)); + exp + } + + fn total(&self) -> Expression { + let exp = Expression::function("TOTAL"); + exp.argument(Some(self)); + exp + } + + fn abs(&self) -> Expression { + let exp = Expression::function("ABS"); + exp.argument(Some(self)); + exp + } + + fn hex(&self) -> Expression { + let exp = Expression::function("HEX"); + exp.argument(Some(self)); + exp + } + + fn length(&self) -> Expression { + let exp = Expression::function("LENGTH"); + exp.argument(Some(self)); + exp + } + + fn lower(&self) -> Expression { + let exp = Expression::function("LOWER"); + exp.argument(Some(self)); + exp + } + + fn upper(&self) -> Expression { + let exp = Expression::function("UPPER"); + exp.argument(Some(self)); + exp + } + + fn round(&self) -> Expression { + let exp = Expression::function("ROUND"); + exp.argument(Some(self)); + exp + } + + fn match_info(&self) -> Expression { + let exp = Expression::function("matchInfo"); + exp.argument(Some(self)); + exp + } + + fn offsets(&self) -> Expression { + let exp = Expression::function("offsets"); + exp.argument(Some(self)); + exp + } + + fn snippet(&self) -> Expression { + let exp = Expression::function("snippet"); + exp.argument(Some(self)); + exp + } + + fn bm25(&self) -> Expression { + let exp = Expression::function("bm25"); + exp.argument(Some(self)); + exp + } + + fn highlight(&self) -> Expression { + let exp = Expression::function("highlight"); + exp.argument(Some(self)); + exp + } + + fn substring_match_info(&self) -> Expression { + let exp = Expression::function("substring_match_info"); + exp.argument(Some(self)); + exp + } +} + +impl ExpressionOperable { + pub(crate) fn new(cpp_type: CPPType, cpp_obj_opt: Option<*mut c_void>) -> Self { + ExpressionOperable { + identifier: Identifier::new(cpp_type, cpp_obj_opt), + } + } + + fn null_operate(&self, is_not: bool) -> Expression { + let cpp_obj = unsafe { + WCDBRustExpressionOperable_nullOperate( + self.get_type() as i32, + self.get_cpp_obj(), + is_not, + ) + }; + Self::create_expression(cpp_obj) + } + + pub(crate) fn create_expression(cpp_obj: *mut c_void) -> Expression { + let mut expression = Expression::new_empty(); + expression.set_cpp_obj(cpp_obj); + expression + } + + pub(crate) fn in_<'a, S>( + &self, + left_cpp_type: CPPType, + operands: Vec, + is_not: bool, + ) -> Expression + where + S: Into>, + { + let value_vec: Vec> = + operands.into_iter().map(|operand| operand.into()).collect(); + + let len = value_vec.len(); + + let mut i64_vec = Vec::new(); + let mut f64_vec = Vec::new(); + let mut c_strings: Vec = vec![]; + let mut string_vec = Vec::new(); + let mut expr_vec = Vec::new(); + let mut cpp_type = CPPType::Null; + + for param in value_vec { + match param { + BasicExpression::Bool(bool) => { + cpp_type = CPPType::Int; + let value = if bool { 1 } else { 0 }; + i64_vec.push(value); + } + BasicExpression::Int(i64) => { + cpp_type = CPPType::Int; + i64_vec.push(i64); + } + BasicExpression::Float(f64) => { + cpp_type = CPPType::Double; + f64_vec.push(f64); + } + BasicExpression::String(string) => { + cpp_type = CPPType::String; + let c = string.as_str().to_cstring(); + string_vec.push(c.as_ptr()); + c_strings.push(c); + } + BasicExpression::ExpressionConvertible(expr_opt) => { + cpp_type = CPPType::Expression; + match expr_opt { + None => { + expr_vec.push(std::ptr::null()); + } + Some(expr) => { + expr_vec.push(CppObject::get(expr)); + } + } + } + } + } + let cpp_obj = unsafe { + WCDBRustExpressionOperable_inOperate( + left_cpp_type as c_int, + CppObject::get(self), + cpp_type as c_int, + i64_vec.as_ptr(), + f64_vec.as_ptr(), + string_vec.as_ptr(), + len as c_int, + is_not, + ) + }; + Self::create_expression(cpp_obj) + } + + pub(crate) fn not_in<'a, S>( + &self, + left_cpp_type: CPPType, + operands: Vec, + is_not: bool, + ) -> Expression + where + S: Into>, + { + let value_vec: Vec> = + operands.into_iter().map(|operand| operand.into()).collect(); + + let len = value_vec.len(); + + let mut i64_vec = Vec::new(); + let mut f64_vec = Vec::new(); + let mut c_strings: Vec = vec![]; + let mut string_vec = Vec::new(); + let mut expr_vec = Vec::new(); + let mut cpp_type = CPPType::Null; + + for param in value_vec { + match param { + BasicExpression::Bool(bool) => { + cpp_type = CPPType::Int; + let value = if bool { 1 } else { 0 }; + i64_vec.push(value); + } + BasicExpression::Int(i64) => { + cpp_type = CPPType::Int; + i64_vec.push(i64); + } + BasicExpression::Float(f64) => { + cpp_type = CPPType::Double; + f64_vec.push(f64); + } + BasicExpression::String(string) => { + cpp_type = CPPType::String; + let c = string.as_str().to_cstring(); + string_vec.push(c.as_ptr()); + c_strings.push(c); + } + BasicExpression::ExpressionConvertible(expr_opt) => { + cpp_type = CPPType::Expression; + match expr_opt { + None => { + expr_vec.push(std::ptr::null()); + } + Some(expr) => { + expr_vec.push(CppObject::get(expr)); + } + } + } + } + } + let cpp_obj = unsafe { + WCDBRustExpressionOperable_inOperate( + left_cpp_type as c_int, + CppObject::get(self), + cpp_type as c_int, + i64_vec.as_ptr(), + f64_vec.as_ptr(), + string_vec.as_ptr(), + len as c_int, + is_not, + ) + }; + Self::create_expression(cpp_obj) + } + + fn binary_operate<'a, T>( + &self, + operand: T, + operand_type: BinaryOperatorType, + is_not: bool, + ) -> Expression + where + T: Into>, + { + let (right_type, right_long, right_double, right_cstr_opt) = operand.into().get_params(); + let right_string_ptr = match right_cstr_opt.as_ref() { + Some(s) => s.as_ptr(), + None => std::ptr::null(), + }; + let cpp_obj = unsafe { + WCDBRustExpressionOperable_binaryOperate( + self.get_type() as i32, + self.get_cpp_obj(), + right_type as i32, + right_long, + right_double, + right_string_ptr, + operand_type as i32, + is_not, + ) + }; + Self::create_expression(cpp_obj) + } + + fn between_operate<'a, T, V>(&self, begin: T, end: V, is_not: bool) -> Expression + where + T: Into>, + V: Into>, + { + let (begin_type, begin_long, begin_double, begin_cstr_opt) = begin.into().get_params(); + let (end_type, end_long, end_double, end_cstr_opt) = end.into().get_params(); + let begin_string_ptr = match begin_cstr_opt.as_ref() { + Some(s) => s.as_ptr(), + None => std::ptr::null(), + }; + let end_string_ptr = match end_cstr_opt.as_ref() { + Some(s) => s.as_ptr(), + None => std::ptr::null(), + }; + let cpp_obj = unsafe { + WCDBRustExpressionOperable_betweenOperate( + self.get_type() as i32, + self.get_cpp_obj(), + begin_type as i32, + begin_long as usize as *mut c_void, + begin_double, + begin_string_ptr, + end_type as i32, + end_long as usize as *mut c_void, + end_double, + end_string_ptr, + is_not, + ) + }; + Self::create_expression(cpp_obj) + } +} + +pub enum BinaryOperatorType { + Concatenate = 1, + Multiply = 2, + Divide = 3, + Modulo = 4, + Plus = 5, + Minus = 6, + LeftShift = 7, + RightShift = 8, + BitwiseAnd = 9, + BitwiseOr = 10, + Less = 11, + LessOrEqual = 12, + Greater = 13, + GreaterOrEqual = 14, + Equal = 15, + NotEqual = 16, + Is = 17, + And = 18, + Or = 19, + Like = 20, + GLOB = 21, + RegExp = 22, + Match = 23, +} diff --git a/src/rust/wcdb/src/winq/foreign_key.rs b/src/rust/wcdb/src/winq/foreign_key.rs new file mode 100644 index 000000000..5eaf8753d --- /dev/null +++ b/src/rust/wcdb/src/winq/foreign_key.rs @@ -0,0 +1,219 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_column::StringColumn; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use std::ffi::{c_char, c_int, c_void}; + +#[repr(i32)] +pub enum Action { + SetNull = 0, + SetDefault = 1, + Cascade = 2, + Restrict = 3, + NoAction = 4, +} +#[repr(i32)] +pub enum Match { + Simple = 0, + Full = 1, + Partial = 2, +} +#[repr(i32)] +pub enum Initially { + Default = 0, + Deferred = 1, + Immediate = 2, +} + +extern "C" { + fn WCDBRustForeignKey_createCppObject() -> *mut c_void; + + fn WCDBRustForeignKey_configReferencesTable(cpp_obj: *mut c_void, table: *const c_char); + + fn WCDBRustForeignKey_configColumns( + cpp_obj: *mut c_void, + cpp_type: c_int, + objects: *const *mut c_void, + column_names: *const *const c_char, + count: usize, + ); + + fn WCDBRustForeignKey_configOnDeleteAction(cpp_obj: *mut c_void, action: c_int); + + fn WCDBRustForeignKey_configOnUpdateAction(cpp_obj: *mut c_void, action: c_int); + + fn WCDBRustForeignKey_configMatch(cpp_obj: *mut c_void, match_: c_int); + + fn WCDBRustForeignKey_configDeferrable(cpp_obj: *mut c_void, r#type: c_int); + + fn WCDBRustForeignKey_configNotDeferrable(cpp_obj: *mut c_void, r#type: c_int); +} + +pub struct ForeignKey { + identifier: Identifier, +} + +impl CppObjectTrait for ForeignKey { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj) + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object() + } +} + +impl CppObjectConvertibleTrait for ForeignKey { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for ForeignKey { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for ForeignKey { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl ForeignKey { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustForeignKey_createCppObject() }; + ForeignKey { + identifier: Identifier::new(CPPType::ForeignKeyClause, Some(cpp_obj)), + } + } + + pub fn references(&self, table: &str) -> &Self { + let c_str = table.to_string().to_cstring(); + unsafe { + WCDBRustForeignKey_configReferencesTable(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } + + pub fn column<'a, T>(&self, column: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = column.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + let mut cpp_obj_vec = Vec::new(); + cpp_obj_vec.push(cpp_obj); + let mut c_str_vec = Vec::new(); + c_str_vec.push(name_ptr); + unsafe { + WCDBRustForeignKey_configColumns( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj_vec.as_ptr(), + c_str_vec.as_ptr(), + 1, + ); + } + self + } + + pub fn columns<'a, I, S>(&self, columns: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = columns.into_iter().map(Into::into).collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type = CPPType::String; + let mut cpp_str_vec = vec![]; + let mut cpp_obj_vec = vec![]; + let mut c_strings = vec![]; + for item in data_vec { + match item { + StringColumn::String(str) => { + let c_string = str.to_cstring(); + cpp_str_vec.push(c_string.as_ptr()); + c_strings.push(c_string); + } + StringColumn::Column(obj) => { + cpp_type = Identifier::get_cpp_type(obj.as_identifier()); + cpp_obj_vec.push(CppObject::get(obj)); + } + } + } + if !cpp_str_vec.is_empty() { + unsafe { + WCDBRustForeignKey_configColumns( + self.get_cpp_obj(), + CPPType::String as c_int, + std::ptr::null(), + cpp_str_vec.as_ptr(), + cpp_str_vec.len(), + ); + } + } else { + unsafe { + WCDBRustForeignKey_configColumns( + self.get_cpp_obj(), + CPPType::Column as c_int, + cpp_obj_vec.as_ptr(), + std::ptr::null(), + cpp_obj_vec.len(), + ); + } + } + self + } + + pub fn on_delete(&self, action: Action) -> &Self { + unsafe { + WCDBRustForeignKey_configOnDeleteAction(self.get_cpp_obj(), action as c_int); + } + self + } + + pub fn on_update(&self, action: Action) -> &Self { + unsafe { + WCDBRustForeignKey_configOnUpdateAction(self.get_cpp_obj(), action as c_int); + } + self + } + + pub fn match_(&self, match_: Match) -> &Self { + unsafe { + WCDBRustForeignKey_configMatch(self.get_cpp_obj(), (match_ as c_int) + 1); + } + self + } + + pub fn deferrable(&self, initially: Initially) -> &Self { + unsafe { + WCDBRustForeignKey_configDeferrable(self.get_cpp_obj(), initially as c_int); + } + self + } + + pub fn not_deferrable(&self, initially: Initially) -> &Self { + unsafe { + WCDBRustForeignKey_configNotDeferrable(self.get_cpp_obj(), initially as c_int); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/frame_spec.rs b/src/rust/wcdb/src/winq/frame_spec.rs new file mode 100644 index 000000000..f04bb92a5 --- /dev/null +++ b/src/rust/wcdb/src/winq/frame_spec.rs @@ -0,0 +1,218 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_int_expression::IntExpression; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use std::ffi::{c_int, c_longlong, c_void}; + +extern "C" { + fn WCDBRustFrameSpec_createCppObj() -> *mut c_void; + fn WCDBRustFrameSpec_configRange(cpp_obj: *mut c_void); + + fn WCDBRustFrameSpec_configRows(cpp_obj: *mut c_void); + + fn WCDBRustFrameSpec_configUnboundedPreceding(cpp_obj: *mut c_void); + + fn WCDBRustFrameSpec_configPreceding( + cpp_obj: *mut c_void, + c_type: c_int, + offset_obj: c_longlong, + ); + + fn WCDBRustFrameSpec_configCurrentRow(cpp_obj: *mut c_void); + + fn WCDBRustFrameSpec_configBetweenUnboundedPreceding(cpp_obj: *mut c_void); + + fn WCDBRustFrameSpec_configBetweenPreceding( + cpp_obj: *mut c_void, + c_type: c_int, + offset_obj: c_longlong, + ); + + fn WCDBRustFrameSpec_configBetweenCurrentRow(cpp_obj: *mut c_void); + + fn WCDBRustFrameSpec_configBetweenFollowing( + cpp_obj: *mut c_void, + c_type: c_int, + offset_obj: c_longlong, + ); + + fn WCDBRustFrameSpec_configAndCurrentRow(cpp_obj: *mut c_void); + + fn WCDBRustFrameSpec_configAndPreceding( + cpp_obj: *mut c_void, + c_type: c_int, + offset_obj: c_longlong, + ); + + fn WCDBRustFrameSpec_configAndUnboundedFollowing(cpp_obj: *mut c_void); + + fn WCDBRustFrameSpec_configAndFollowing( + cpp_obj: *mut c_void, + c_type: c_int, + offset_obj: c_longlong, + ); +} + +#[derive(Debug)] +pub struct FrameSpec { + identifier: Identifier, +} + +impl CppObjectTrait for FrameSpec { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for FrameSpec { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for FrameSpec { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for FrameSpec { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl FrameSpec { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustFrameSpec_createCppObj() }; + FrameSpec { + identifier: Identifier::new(CPPType::FrameSpec, Some(cpp_obj)), + } + } + + pub fn range(&self) -> &Self { + unsafe { WCDBRustFrameSpec_configRange(self.get_cpp_obj()) } + self + } + + pub fn rows(&self) -> &Self { + unsafe { WCDBRustFrameSpec_configRows(self.get_cpp_obj()) } + self + } + + pub fn unbounded_preceding(&self) -> &Self { + unsafe { WCDBRustFrameSpec_configUnboundedPreceding(self.get_cpp_obj()) } + self + } + + pub fn preceding<'a, T>(&self, offset: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj) = offset.into().get_params(); + unsafe { + WCDBRustFrameSpec_configPreceding(self.get_cpp_obj(), cpp_type as c_int, cpp_obj); + } + self + } + + pub fn current_row(&self) -> &Self { + unsafe { + WCDBRustFrameSpec_configCurrentRow(self.get_cpp_obj()); + } + self + } + + pub fn between_unbounded_preceding(&self) -> &Self { + unsafe { + WCDBRustFrameSpec_configBetweenUnboundedPreceding(self.get_cpp_obj()); + } + self + } + + pub fn between_preceding<'a, T>(&self, offset: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj) = offset.into().get_params(); + unsafe { + WCDBRustFrameSpec_configBetweenPreceding( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + ); + } + self + } + + pub fn between_current_row(&self) -> &Self { + unsafe { + WCDBRustFrameSpec_configBetweenCurrentRow(self.get_cpp_obj()); + } + self + } + + pub fn between_following<'a, T>(&self, offset: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj) = offset.into().get_params(); + unsafe { + WCDBRustFrameSpec_configBetweenFollowing( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + ); + } + self + } + + pub fn and_current_row(&self) -> &Self { + unsafe { + WCDBRustFrameSpec_configAndCurrentRow(self.get_cpp_obj()); + } + self + } + + pub fn and_preceding<'a, T>(&self, offset: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj) = offset.into().get_params(); + unsafe { + WCDBRustFrameSpec_configAndPreceding(self.get_cpp_obj(), cpp_type as c_int, cpp_obj); + } + self + } + + pub fn and_unbounded_following(&self) -> &Self { + unsafe { + WCDBRustFrameSpec_configAndUnboundedFollowing(self.get_cpp_obj()); + } + self + } + + pub fn and_following<'a, T>(&self, offset: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj) = offset.into().get_params(); + unsafe { + WCDBRustFrameSpec_configAndFollowing(self.get_cpp_obj(), cpp_type as c_int, cpp_obj); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/identifier.rs b/src/rust/wcdb/src/winq/identifier.rs new file mode 100644 index 000000000..6cd8a62df --- /dev/null +++ b/src/rust/wcdb/src/winq/identifier.rs @@ -0,0 +1,137 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::utils::ToCow; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use num_derive::FromPrimitive; +use std::ffi::{c_char, c_void}; +use std::fmt::Debug; + +extern "C" { + pub fn WCDBRustWinq_isWriteStatement(statement: *mut c_void) -> bool; + fn WCDBRustWinq_getDescription(statement: *mut c_void) -> *const c_char; +} + +#[derive(Clone, Debug, Eq, FromPrimitive, PartialEq)] +#[repr(i32)] +pub enum CPPType { + Invalid = 0, + Null = 1, + Bool = 2, + Int = 3, + UInt = 4, + Double = 5, + String = 6, + + Column = 7, + Schema = 8, + ColumnDef = 9, + ColumnConstraint = 10, + Expression = 11, + LiteralValue = 12, + ForeignKeyClause = 13, + BindParameter = 14, + RaiseFunction = 15, + WindowDef = 16, + Filter = 17, + IndexedColumn = 18, + TableConstraint = 19, + CommonTableExpression = 20, + QualifiedTableName = 21, + OrderingTerm = 22, + UpsertClause = 23, + Pragma = 24, + JoinClause = 25, + TableOrSubquery = 26, + JoinConstraint = 27, + SelectCore = 28, + ResultColumn = 29, + FrameSpec = 30, + + AlterTableSTMT = 31, + AnalyzeSTMT = 32, + AttachSTMT = 33, + BeginSTMT = 34, + CommitSTMT = 35, + RollbackSTMT = 36, + SavepointSTMT = 37, + ReleaseSTMT = 38, + CreateIndexSTMT = 39, + CreateTableSTMT = 40, + CreateTriggerSTMT = 41, + SelectSTMT = 42, + InsertSTMT = 43, + DeleteSTMT = 44, + UpdateSTMT = 45, + CreateViewSTMT = 46, + CreateVirtualTableSTMT = 47, + DetachSTMT = 48, + DropIndexSTMT = 49, + DropTableSTMT = 50, + DropTriggerSTMT = 51, + DropViewSTMT = 52, + PragmaSTMT = 53, + ReindexSTMT = 54, + VacuumSTMT = 55, + ExplainSTMT = 56, +} + +#[derive(Debug)] +pub struct Identifier { + cpp_type: CPPType, + cpp_obj: CppObject, +} + +impl CppObjectTrait for Identifier { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.cpp_obj.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.cpp_obj.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.cpp_obj.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for Identifier { + fn as_cpp_object(&self) -> &CppObject { + &self.cpp_obj + } +} + +pub trait IdentifierTrait: IdentifierConvertibleTrait { + fn get_type(&self) -> CPPType; + fn get_description(&self) -> String; +} + +impl IdentifierTrait for Identifier { + fn get_type(&self) -> CPPType { + self.cpp_type.clone() + } + + fn get_description(&self) -> String { + let c_description = unsafe { WCDBRustWinq_getDescription(self.get_cpp_obj()) }; + c_description.to_cow().to_string() + } +} + +impl IdentifierConvertibleTrait for Identifier { + fn as_identifier(&self) -> &Self { + self + } +} + +impl Identifier { + pub(crate) fn new(cpp_type: CPPType, cpp_obj_opt: Option<*mut c_void>) -> Self { + Identifier { + cpp_type, + cpp_obj: CppObject::new(cpp_obj_opt), + } + } + + pub(crate) fn get_cpp_type(identifier: &T) -> CPPType { + identifier.as_identifier().get_type() + } +} diff --git a/src/rust/wcdb/src/winq/identifier_convertible.rs b/src/rust/wcdb/src/winq/identifier_convertible.rs new file mode 100644 index 000000000..3f8372464 --- /dev/null +++ b/src/rust/wcdb/src/winq/identifier_convertible.rs @@ -0,0 +1,6 @@ +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::winq::identifier::Identifier; + +pub trait IdentifierConvertibleTrait: CppObjectConvertibleTrait { + fn as_identifier(&self) -> &Identifier; +} diff --git a/src/rust/wcdb/src/winq/indexed_column.rs b/src/rust/wcdb/src/winq/indexed_column.rs new file mode 100644 index 000000000..418c0473f --- /dev/null +++ b/src/rust/wcdb/src/winq/indexed_column.rs @@ -0,0 +1,94 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_indexed_column::StringIndexedColumn; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::indexed_column_convertible::IndexedColumnConvertibleTrait; +use crate::winq::ordering_term::Order; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustIndexedColumn_create( + cpp_type: c_int, + object: *mut c_void, + column_name: *const c_char, + ) -> *mut c_void; + + fn WCDBRustIndexedColumn_configCollation(cpp_obj: *mut c_void, collation: *const c_char); + fn WCDBRustIndexedColumn_configOrder(cpp_obj: *mut c_void, order: c_int); +} + +pub struct IndexedColumn { + identifier: Identifier, +} + +impl CppObjectTrait for IndexedColumn { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj) + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object() + } +} + +impl CppObjectConvertibleTrait for IndexedColumn { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for IndexedColumn { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for IndexedColumn { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl IndexedColumnConvertibleTrait for IndexedColumn {} + +impl IndexedColumn { + pub fn new<'a, T>(param: T) -> Self + where + T: Into>, + { + let (cpp_type, c_obj, c_str_opt) = param.into().get_params(); + let c_str_ptr = match c_str_opt.as_ref() { + None => std::ptr::null(), + Some(s) => s.as_ptr(), + }; + let cpp_obj = unsafe { WCDBRustIndexedColumn_create(cpp_type as c_int, c_obj, c_str_ptr) }; + IndexedColumn { + identifier: Identifier::new(CPPType::IndexedColumn, Some(cpp_obj)), + } + } + + pub fn collate(&self, collation: &str) -> &Self { + unsafe { + WCDBRustIndexedColumn_configCollation( + self.get_cpp_obj(), + collation.to_cstring().as_ptr(), + ) + } + self + } + + pub fn order(&self, order: Order) -> &Self { + unsafe { WCDBRustIndexedColumn_configOrder(self.get_cpp_obj(), (order as c_int) + 1) } + self + } +} diff --git a/src/rust/wcdb/src/winq/indexed_column_convertible.rs b/src/rust/wcdb/src/winq/indexed_column_convertible.rs new file mode 100644 index 000000000..1a78e6b9d --- /dev/null +++ b/src/rust/wcdb/src/winq/indexed_column_convertible.rs @@ -0,0 +1,3 @@ +use crate::winq::identifier::IdentifierTrait; + +pub trait IndexedColumnConvertibleTrait: IdentifierTrait {} diff --git a/src/rust/wcdb/src/winq/join.rs b/src/rust/wcdb/src/winq/join.rs new file mode 100644 index 000000000..30b62d199 --- /dev/null +++ b/src/rust/wcdb/src/winq/join.rs @@ -0,0 +1,537 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_column::StringColumn; +use crate::base::param::enum_string_table_or_subquery::StringTableOrSubquery; +use crate::utils::ToCString; +use crate::winq::expression::Expression; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::table_or_subquery_convertible_trait::TableOrSubqueryConvertibleTrait; +use core::ffi::c_size_t; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustJoin_createCppObj( + cpp_type: c_int, + object: *mut c_void, + table_name: *const c_char, + ) -> *mut c_void; + + fn WCDBRustJoin_configWith( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustJoin_configWithJoin( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustJoin_configWithLeftOuterJoin( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustJoin_configWithLeftJoin( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustJoin_configWithInnerJoin( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustJoin_configWithCrossJoin( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustJoin_configWithNaturalJoin( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustJoin_configWithNaturalLeftOuterJoin( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustJoin_configWithNaturalLeftJoin( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustJoin_configWithNaturalInnerJoin( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustJoin_configWithNaturalCrossJoin( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustJoin_configOn(cpp_obj: *mut c_void, expression: *mut c_void); + + fn WCDBRustJoin_configUsingColumn( + cpp_obj: *mut c_void, + cpp_type: c_int, + columns: *const *mut c_void, + column_names: *const *const c_char, + vec_len: c_size_t, + ); +} + +pub struct Join { + identifier: Identifier, +} + +impl CppObjectTrait for Join { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj) + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object() + } +} + +impl CppObjectConvertibleTrait for Join { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for Join { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for Join { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl TableOrSubqueryConvertibleTrait for Join {} + +impl Join { + pub fn new<'a, S>(value: S) -> Self + where + S: Into>, + { + let cpp_obj = match value.into() { + StringTableOrSubquery::String(table_name) => unsafe { + let cstr = table_name.to_cstring(); + WCDBRustJoin_createCppObj(CPPType::String as c_int, 0 as *mut c_void, cstr.as_ptr()) + }, + StringTableOrSubquery::TableOrSubquery(table_or_subquery) => unsafe { + WCDBRustJoin_createCppObj( + Identifier::get_cpp_type(table_or_subquery) as c_int, + CppObject::get(table_or_subquery), + std::ptr::null(), + ) + }, + }; + Join { + identifier: Identifier::new(CPPType::JoinClause, Some(cpp_obj)), + } + } + + pub fn with<'a, S>(&self, value: S) -> &Join + where + S: Into>, + { + match value.into() { + StringTableOrSubquery::String(table_name) => { + let cstr = table_name.to_cstring(); + unsafe { + WCDBRustJoin_configWith( + self.get_cpp_obj(), + CPPType::String as c_int, + 0 as *mut c_void, + cstr.as_ptr(), + ); + } + } + StringTableOrSubquery::TableOrSubquery(table_or_subquery) => unsafe { + WCDBRustJoin_configWith( + self.get_cpp_obj(), + Identifier::get_cpp_type(table_or_subquery) as c_int, + CppObject::get(table_or_subquery), + std::ptr::null(), + ); + }, + } + self + } + + pub fn join<'a, S>(&self, value: S) -> &Join + where + S: Into>, + { + match value.into() { + StringTableOrSubquery::String(table_name) => { + let cstr = table_name.to_cstring(); + unsafe { + WCDBRustJoin_configWithJoin( + self.get_cpp_obj(), + CPPType::String as c_int, + 0 as *mut c_void, + cstr.as_ptr(), + ); + } + } + StringTableOrSubquery::TableOrSubquery(table_or_subquery) => unsafe { + WCDBRustJoin_configWithJoin( + self.get_cpp_obj(), + Identifier::get_cpp_type(table_or_subquery) as c_int, + CppObject::get(table_or_subquery), + std::ptr::null(), + ); + }, + } + self + } + + pub fn left_outer_join<'a, S>(&self, value: S) -> &Join + where + S: Into>, + { + match value.into() { + StringTableOrSubquery::String(table_name) => { + let cstr = table_name.to_cstring(); + unsafe { + WCDBRustJoin_configWithLeftOuterJoin( + self.get_cpp_obj(), + CPPType::String as c_int, + 0 as *mut c_void, + cstr.as_ptr(), + ); + } + } + StringTableOrSubquery::TableOrSubquery(table_or_subquery) => unsafe { + WCDBRustJoin_configWithLeftOuterJoin( + self.get_cpp_obj(), + Identifier::get_cpp_type(table_or_subquery) as c_int, + CppObject::get(table_or_subquery), + std::ptr::null(), + ); + }, + } + self + } + + pub fn left_join<'a, S>(&self, value: S) -> &Join + where + S: Into>, + { + match value.into() { + StringTableOrSubquery::String(table_name) => { + let cstr = table_name.to_cstring(); + unsafe { + WCDBRustJoin_configWithLeftJoin( + self.get_cpp_obj(), + CPPType::String as c_int, + 0 as *mut c_void, + cstr.as_ptr(), + ); + } + } + StringTableOrSubquery::TableOrSubquery(table_or_subquery) => unsafe { + WCDBRustJoin_configWithLeftJoin( + self.get_cpp_obj(), + Identifier::get_cpp_type(table_or_subquery) as c_int, + CppObject::get(table_or_subquery), + std::ptr::null(), + ); + }, + } + self + } + + pub fn inner_join<'a, S>(&self, value: S) -> &Join + where + S: Into>, + { + match value.into() { + StringTableOrSubquery::String(table_name) => { + let cstr = table_name.to_cstring(); + unsafe { + WCDBRustJoin_configWithInnerJoin( + self.get_cpp_obj(), + CPPType::String as c_int, + 0 as *mut c_void, + cstr.as_ptr(), + ); + } + } + StringTableOrSubquery::TableOrSubquery(table_or_subquery) => unsafe { + WCDBRustJoin_configWithInnerJoin( + self.get_cpp_obj(), + Identifier::get_cpp_type(table_or_subquery) as c_int, + CppObject::get(table_or_subquery), + std::ptr::null(), + ); + }, + } + self + } + + pub fn cross_join<'a, S>(&self, value: S) -> &Join + where + S: Into>, + { + match value.into() { + StringTableOrSubquery::String(table_name) => { + let cstr = table_name.to_cstring(); + unsafe { + WCDBRustJoin_configWithCrossJoin( + self.get_cpp_obj(), + CPPType::String as c_int, + 0 as *mut c_void, + cstr.as_ptr(), + ); + } + } + StringTableOrSubquery::TableOrSubquery(table_or_subquery) => unsafe { + WCDBRustJoin_configWithCrossJoin( + self.get_cpp_obj(), + Identifier::get_cpp_type(table_or_subquery) as c_int, + CppObject::get(table_or_subquery), + std::ptr::null(), + ); + }, + } + self + } + + pub fn natural_join<'a, S>(&self, value: S) -> &Join + where + S: Into>, + { + match value.into() { + StringTableOrSubquery::String(table_name) => { + let cstr = table_name.to_cstring(); + unsafe { + WCDBRustJoin_configWithNaturalJoin( + self.get_cpp_obj(), + CPPType::String as c_int, + 0 as *mut c_void, + cstr.as_ptr(), + ); + } + } + StringTableOrSubquery::TableOrSubquery(table_or_subquery) => unsafe { + WCDBRustJoin_configWithNaturalJoin( + self.get_cpp_obj(), + Identifier::get_cpp_type(table_or_subquery) as c_int, + CppObject::get(table_or_subquery), + std::ptr::null(), + ); + }, + } + self + } + + pub fn natural_left_outer_join<'a, S>(&self, value: S) -> &Join + where + S: Into>, + { + match value.into() { + StringTableOrSubquery::String(table_name) => { + let cstr = table_name.to_cstring(); + unsafe { + WCDBRustJoin_configWithNaturalLeftOuterJoin( + self.get_cpp_obj(), + CPPType::String as c_int, + 0 as *mut c_void, + cstr.as_ptr(), + ); + } + } + StringTableOrSubquery::TableOrSubquery(table_or_subquery) => unsafe { + WCDBRustJoin_configWithNaturalLeftOuterJoin( + self.get_cpp_obj(), + Identifier::get_cpp_type(table_or_subquery) as c_int, + CppObject::get(table_or_subquery), + std::ptr::null(), + ); + }, + } + self + } + + pub fn natural_left_join<'a, S>(&self, value: S) -> &Join + where + S: Into>, + { + match value.into() { + StringTableOrSubquery::String(table_name) => { + let cstr = table_name.to_cstring(); + unsafe { + WCDBRustJoin_configWithNaturalLeftJoin( + self.get_cpp_obj(), + CPPType::String as c_int, + 0 as *mut c_void, + cstr.as_ptr(), + ); + } + } + StringTableOrSubquery::TableOrSubquery(table_or_subquery) => unsafe { + WCDBRustJoin_configWithNaturalLeftJoin( + self.get_cpp_obj(), + Identifier::get_cpp_type(table_or_subquery) as c_int, + CppObject::get(table_or_subquery), + std::ptr::null(), + ); + }, + } + self + } + + pub fn natural_inner_join<'a, S>(&self, value: S) -> &Join + where + S: Into>, + { + match value.into() { + StringTableOrSubquery::String(table_name) => { + let cstr = table_name.to_cstring(); + unsafe { + WCDBRustJoin_configWithNaturalInnerJoin( + self.get_cpp_obj(), + CPPType::String as c_int, + 0 as *mut c_void, + cstr.as_ptr(), + ); + } + } + StringTableOrSubquery::TableOrSubquery(table_or_subquery) => unsafe { + WCDBRustJoin_configWithNaturalInnerJoin( + self.get_cpp_obj(), + Identifier::get_cpp_type(table_or_subquery) as c_int, + CppObject::get(table_or_subquery), + std::ptr::null(), + ); + }, + } + self + } + + pub fn natural_cross_join<'a, S>(&self, value: S) -> &Join + where + S: Into>, + { + match value.into() { + StringTableOrSubquery::String(table_name) => { + let cstr = table_name.to_cstring(); + unsafe { + WCDBRustJoin_configWithNaturalCrossJoin( + self.get_cpp_obj(), + CPPType::String as c_int, + 0 as *mut c_void, + cstr.as_ptr(), + ); + } + } + StringTableOrSubquery::TableOrSubquery(table_or_subquery) => unsafe { + WCDBRustJoin_configWithNaturalCrossJoin( + self.get_cpp_obj(), + Identifier::get_cpp_type(table_or_subquery) as c_int, + CppObject::get(table_or_subquery), + std::ptr::null(), + ); + }, + } + self + } + + pub fn on(&self, expression: &Expression) -> &Join { + unsafe { + WCDBRustJoin_configOn(self.get_cpp_obj(), CppObject::get(expression)); + } + self + } + + pub fn using<'a, I, S>(&self, column_vec: I) -> &Join + where + I: IntoIterator, + S: Into>, + { + let data_vec = column_vec.into_iter().map(Into::into).collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type = CPPType::String; + let mut cpp_str_vec = vec![]; + let mut cpp_obj_vec = vec![]; + let mut c_strings = vec![]; + for item in data_vec { + match item { + StringColumn::String(str) => { + let c_string = str.to_cstring(); + cpp_str_vec.push(c_string.as_ptr()); + c_strings.push(c_string); + } + StringColumn::Column(obj) => { + cpp_type = Identifier::get_cpp_type(obj.as_identifier()); + cpp_obj_vec.push(CppObject::get(obj)); + } + } + } + if !cpp_str_vec.is_empty() { + unsafe { + WCDBRustJoin_configUsingColumn( + self.get_cpp_obj(), + CPPType::String as c_int, + std::ptr::null(), + cpp_str_vec.as_ptr(), + cpp_str_vec.len(), + ); + } + } else { + unsafe { + WCDBRustJoin_configUsingColumn( + self.get_cpp_obj(), + CPPType::Column as c_int, + cpp_obj_vec.as_ptr(), + std::ptr::null(), + cpp_obj_vec.len(), + ); + } + } + self + } +} diff --git a/src/rust/wcdb/src/winq/literal_value.rs b/src/rust/wcdb/src/winq/literal_value.rs new file mode 100644 index 000000000..7bbf9e807 --- /dev/null +++ b/src/rust/wcdb/src/winq/literal_value.rs @@ -0,0 +1,110 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_basic_expression::BasicExpression; +use crate::winq::expression_convertible::ExpressionConvertibleTrait; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use std::ffi::{c_char, c_double, c_int, c_void}; + +extern "C" { + fn WCDBRustLiteralValue_create( + value_type: c_int, + value_long: i64, + value_double: c_double, + value_string: *const c_char, + ) -> *mut c_void; + + fn WCDBRustLiteralValue_createWithCurrentTime() -> *mut c_void; + + fn WCDBRustLiteralValue_createWithCurrentDate() -> *mut c_void; + + fn WCDBRustLiteralValue_createWithCurrentTimestamp() -> *mut c_void; +} + +pub struct LiteralValue { + pub(crate) identifier: Identifier, +} + +impl CppObjectTrait for LiteralValue { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for LiteralValue { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for LiteralValue { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for LiteralValue { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl ExpressionConvertibleTrait for LiteralValue {} + +impl LiteralValue { + pub fn new<'a, T>(param: T) -> Self + where + T: Into>, + { + let (arg_type, arg_long, arg_double, arg_string_opt) = param.into().get_params(); + let arg_string_ptr = match arg_string_opt.as_ref() { + Some(s) => s.as_ptr(), + None => std::ptr::null(), + }; + let cpp_obj = unsafe { + WCDBRustLiteralValue_create(arg_type as c_int, arg_long, arg_double, arg_string_ptr) + }; + LiteralValue { + identifier: Identifier::new(CPPType::LiteralValue, Some(cpp_obj)), + } + } + + fn default() -> Self { + LiteralValue { + identifier: Identifier::new(CPPType::LiteralValue, None), + } + } + + pub fn current_time() -> Self { + let mut ret = LiteralValue::default(); + let cpp_obj = unsafe { WCDBRustLiteralValue_createWithCurrentTime() }; + ret.set_cpp_obj(cpp_obj); + ret + } + + pub fn current_date() -> Self { + let mut ret = LiteralValue::default(); + let cpp_obj = unsafe { WCDBRustLiteralValue_createWithCurrentDate() }; + ret.set_cpp_obj(cpp_obj); + ret + } + + pub fn current_time_stamp() -> Self { + let mut ret = LiteralValue::default(); + let cpp_obj = unsafe { WCDBRustLiteralValue_createWithCurrentTimestamp() }; + ret.set_cpp_obj(cpp_obj); + ret + } +} diff --git a/src/rust/wcdb/src/winq/mod.rs b/src/rust/wcdb/src/winq/mod.rs new file mode 100644 index 000000000..45cbc3296 --- /dev/null +++ b/src/rust/wcdb/src/winq/mod.rs @@ -0,0 +1,56 @@ +pub mod bind_parameter; +pub mod column; +pub mod column_constraint; +pub mod column_def; +pub mod column_type; +pub mod common_table_expression; +pub mod conflict_action; +pub mod expression; +pub mod expression_convertible; +pub mod expression_operable; +pub mod foreign_key; +pub mod frame_spec; +pub mod identifier; +pub mod identifier_convertible; +pub mod indexed_column; +pub mod indexed_column_convertible; +pub mod join; +pub mod literal_value; +pub mod multi_type_array; +pub mod object; +pub mod ordering_term; +pub mod pragma; +pub mod qualified_table; +pub mod result_column; +pub mod result_column_convertible_trait; +pub mod schema; +pub mod statement; +pub mod statement_alter_table; +pub mod statement_analyze; +pub mod statement_attach; +pub mod statement_begin; +pub mod statement_commit; +pub mod statement_create_index; +pub mod statement_create_table; +pub mod statement_create_trigger; +pub mod statement_create_view; +pub mod statement_create_virtual_table; +pub mod statement_delete; +pub mod statement_detach; +pub mod statement_drop_index; +pub mod statement_drop_table; +pub mod statement_drop_trigger; +pub mod statement_drop_view; +pub mod statement_explain; +pub mod statement_insert; +pub mod statement_pragma; +pub mod statement_reindex; +pub mod statement_release; +pub mod statement_rollback; +pub mod statement_select; +pub mod statement_update; +pub mod statement_vacuum; +pub mod table_constraint; +pub mod table_or_subquery_convertible_trait; +pub mod upsert; +pub mod window_def; diff --git a/src/rust/wcdb/src/winq/multi_type_array.rs b/src/rust/wcdb/src/winq/multi_type_array.rs new file mode 100644 index 000000000..3dffaebba --- /dev/null +++ b/src/rust/wcdb/src/winq/multi_type_array.rs @@ -0,0 +1,187 @@ +use crate::base::cpp_object::CppObject; +use crate::base::value::Value; +use crate::winq::column_type::ColumnType; +use crate::winq::identifier::{CPPType, Identifier}; +use crate::winq::object::Object; +use std::any::Any; +use std::ffi::c_double; + +#[repr(i32)] +pub enum ObjectType { + Null, + Bool, + Char, + Byte, + Short, + Int, + Long = 6, + Float, + Double, + String, + Identifier, + Value, + Unknown, +} + +pub struct MultiTypeArray { + pub(crate) types: Vec, + pub(crate) long_values: Vec, + pub(crate) double_values: Vec, + pub(crate) string_values: Option>, +} + +impl MultiTypeArray { + pub fn new_with_objects(values: &Vec) -> Self { + let value_count = values.len(); + + let mut types: Vec = vec![0; value_count]; + let mut long_values: Vec = vec![0; value_count]; + let mut double_values = vec![0.0; value_count]; + let mut string_values = vec![String::new(); value_count]; + + let mut long_index = 0; + let mut double_index = 0; + let mut string_index = 0; + + for (i, obj) in values.iter().enumerate() { + match obj { + Object::Null => { + types[i] = CPPType::Null as i32; + long_index += 1; + } + Object::Bool(b) => { + types[i] = CPPType::Bool as i32; + long_values[long_index] = if *b { 1 } else { 0 } as i64; + long_index += 1; + } + Object::Byte(b) => { + types[i] = CPPType::Int as i32; + long_values[long_index] = *b as i64; + long_index += 1; + } + Object::Char(c) => { + types[i] = CPPType::Int as i32; + long_values[long_index] = *c as i64; + long_index += 1; + } + Object::Short(s) => { + types[i] = CPPType::Int as i32; + long_values[long_index] = *s as i64; + long_index += 1; + } + Object::Int(int) => { + types[i] = CPPType::Int as i32; + long_values[long_index] = *int as i64; + long_index += 1; + } + Object::Long(l) => { + types[i] = CPPType::Int as i32; + long_values[long_index] = *l; + long_index += 1; + } + Object::Float(f) => { + types[i] = CPPType::Double as i32; + double_values[double_index] = *f as c_double; + double_index += 1; + } + Object::Double(d) => { + types[i] = CPPType::Double as i32; + double_values[double_index] = *d as c_double; + double_index += 1; + } + Object::String(s) => { + types[i] = CPPType::String as i32; + string_values[string_index] = s.clone(); + string_index += 1; + } + Object::Identifier(identifier) => { + types[i] = Identifier::get_cpp_type(identifier) as i32; + long_values[long_index] = CppObject::get(identifier) as i64; + long_index += 1; + } + Object::Value(value_obj) => match value_obj.get_type() { + ColumnType::Null => { + types[i] = CPPType::Null as i32; + long_index += 1; + } + ColumnType::Integer => { + types[i] = CPPType::Int as i32; + long_values[long_index] = value_obj.get_i64(); + long_index += 1; + } + ColumnType::Float => { + types[i] = CPPType::Double as i32; + double_values[double_index] = value_obj.get_f64() as c_double; + double_index += 1; + } + ColumnType::Text => { + types[i] = CPPType::String as i32; + string_values[string_index] = value_obj.get_text(); + string_index += 1; + } + _ => {} + }, + } + } + + let string_values = if string_values.len() as f64 * 0.75 > string_index as f64 { + if string_index == 0 { + None + } else { + Some(string_values[0..string_index].to_vec()) + } + } else { + Some(string_values) + }; + + MultiTypeArray { + types, + long_values, + double_values, + string_values, + } + } + + pub fn get_object_type(val: Box) -> ObjectType { + if val.is::() { + return ObjectType::Identifier; + } else if val.is::<&str>() { + return ObjectType::String; + } else if val.is::() { + return ObjectType::Int; + } else if val.is::() { + return ObjectType::Float; + } else if val.is::() { + return ObjectType::Double; + } else if val.is::() { + return ObjectType::Bool; + } else if val.is::() { + return ObjectType::Short; + } else if val.is::() { + return ObjectType::Long; + } else if val.is::() { + return ObjectType::Char; + } else if val.is::() { + return ObjectType::Byte; + } else if val.is::() { + return ObjectType::Value; + } + ObjectType::Unknown + } + + pub fn types(&self) -> &Vec { + &self.types + } + + pub fn long_values(&self) -> &Vec { + &self.long_values + } + + pub fn double_values(&self) -> &Vec { + &self.double_values + } + + pub fn string_values(&self) -> &Option> { + &self.string_values + } +} diff --git a/src/rust/wcdb/src/winq/object.rs b/src/rust/wcdb/src/winq/object.rs new file mode 100644 index 000000000..6cccfc128 --- /dev/null +++ b/src/rust/wcdb/src/winq/object.rs @@ -0,0 +1,18 @@ +use crate::base::value::Value; +use crate::winq::identifier::Identifier; + +#[derive(Debug)] +pub enum Object { + Null, + Bool(bool), + Byte(i8), + Char(char), + Short(i16), + Int(i32), + Long(i64), + Float(f32), + Double(f64), + String(String), + Identifier(Identifier), + Value(Value), +} diff --git a/src/rust/wcdb/src/winq/ordering_term.rs b/src/rust/wcdb/src/winq/ordering_term.rs new file mode 100644 index 000000000..b282b57f1 --- /dev/null +++ b/src/rust/wcdb/src/winq/ordering_term.rs @@ -0,0 +1,87 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::winq::expression_convertible::ExpressionConvertibleTrait; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use std::ffi::{c_int, c_void}; + +extern "C" { + fn WCDBRustOrderingTerm_create(cpp_type: c_int, expression: *mut c_void) -> *mut c_void; + + fn WCDBRustOrderingTerm_configOrder(cpp_obj: *mut c_void, order: c_int); +} + +pub enum Order { + Asc, + Desc, +} + +#[derive(Debug)] +pub struct OrderingTerm { + identifier: Identifier, +} + +impl CppObjectTrait for OrderingTerm { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for OrderingTerm { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for OrderingTerm { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for OrderingTerm { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl AsRef for OrderingTerm { + fn as_ref(&self) -> &OrderingTerm { + self + } +} + +impl OrderingTerm { + pub fn new(expression: &T) -> Self { + let cpp_obj = unsafe { + WCDBRustOrderingTerm_create( + Identifier::get_cpp_type(expression) as c_int, + CppObject::get(expression), + ) + }; + OrderingTerm { + identifier: Identifier::new(CPPType::OrderingTerm, Some(cpp_obj)), + } + } +} + +impl OrderingTerm { + pub fn order(self, order: Order) -> Self { + unsafe { + WCDBRustOrderingTerm_configOrder(self.get_cpp_obj(), (order as i32) + 1); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/pragma.rs b/src/rust/wcdb/src/winq/pragma.rs new file mode 100644 index 000000000..c46a478f5 --- /dev/null +++ b/src/rust/wcdb/src/winq/pragma.rs @@ -0,0 +1,391 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use std::ffi::{c_char, c_void}; + +extern "C" { + fn WCDBRustPragma_create(name: *const c_char) -> *mut c_void; +} + +pub struct Pragma { + identifier: Identifier, +} + +impl CppObjectTrait for Pragma { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object() + } +} + +impl CppObjectConvertibleTrait for Pragma { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for Pragma { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for Pragma { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl Pragma { + pub fn new(name: &str) -> Self { + let c_name = name.to_cstring(); + let cpp_obj = unsafe { WCDBRustPragma_create(c_name.as_ptr()) }; + Pragma { + identifier: Identifier::new(CPPType::Pragma, Some(cpp_obj)), + } + } + + pub fn application_id() -> Self { + Pragma::new("application_id") + } + pub fn auto_vacuum() -> Self { + Pragma::new("auto_vacuum") + } + + pub fn automatic_index() -> Self { + Pragma::new("automatic_index") + } + + pub fn busy_timeout() -> Self { + Pragma::new("busy_timeout") + } + + pub fn cache_size() -> Self { + Pragma::new("cache_size") + } + + pub fn cache_spill() -> Self { + Pragma::new("cache_spill") + } + + pub fn case_sensitive_like() -> Self { + Pragma::new("case_sensitive_like") + } + + pub fn cell_size_check() -> Self { + Pragma::new("cell_size_check") + } + + pub fn checkpoint_fullfsync() -> Self { + Pragma::new("checkpoint_fullfsync") + } + + pub fn function_list() -> Self { + Pragma::new("function_list") + } + + pub fn cipher() -> Self { + Pragma::new("cipher") + } + + pub fn cipher_add_random() -> Self { + Pragma::new("cipher_add_random") + } + + pub fn cipher_default_kdf_iter() -> Self { + Pragma::new("cipher_default_kdf_iter") + } + + pub fn cipher_default_page_size() -> Self { + Pragma::new("cipher_default_page_size") + } + + pub fn cipher_default_use_hmac() -> Self { + Pragma::new("cipher_default_use_hmac") + } + + pub fn cipher_migrate() -> Self { + Pragma::new("cipher_migrate") + } + + pub fn cipher_profile() -> Self { + Pragma::new("cipher_profile") + } + + pub fn cipher_provider() -> Self { + Pragma::new("cipher_provider") + } + + pub fn cipher_provider_version() -> Self { + Pragma::new("cipher_provider_version") + } + + pub fn cipher_use_hmac() -> Self { + Pragma::new("cipher_use_hmac") + } + + pub fn cipher_version() -> Self { + Pragma::new("cipher_version") + } + + pub fn cipher_page_size() -> Self { + Pragma::new("cipher_page_size") + } + + pub fn collation_list() -> Self { + Pragma::new("collation_list") + } + + pub fn compile_options() -> Self { + Pragma::new("compile_options") + } + + pub fn count_changes() -> Self { + Pragma::new("count_changes") + } + + pub fn data_store_directory() -> Self { + Pragma::new("data_store_directory") + } + + pub fn data_version() -> Self { + Pragma::new("data_version") + } + + pub fn database_list() -> Self { + Pragma::new("database_list") + } + + pub fn default_cache_size() -> Self { + Pragma::new("default_cache_size") + } + + pub fn defer_foreign_keys() -> Self { + Pragma::new("defer_foreign_keys") + } + + pub fn empty_result_callbacks() -> Self { + Pragma::new("empty_result_callbacks") + } + + pub fn encoding() -> Self { + Pragma::new("encoding") + } + + pub fn foreign_key_check() -> Self { + Pragma::new("foreign_key_check") + } + + pub fn foreign_key_list() -> Self { + Pragma::new("foreign_key_list") + } + + pub fn foreign_keys() -> Self { + Pragma::new("foreign_keys") + } + + pub fn freelist_count() -> Self { + Pragma::new("freelist_count") + } + + pub fn full_column_names() -> Self { + Pragma::new("full_column_names") + } + + pub fn fullfsync() -> Self { + Pragma::new("fullfsync") + } + + pub fn ignore_check_constraints() -> Self { + Pragma::new("ignore_check_constraints") + } + + pub fn incremental_vacuum() -> Self { + Pragma::new("incremental_vacuum") + } + + pub fn index_info() -> Self { + Pragma::new("index_info") + } + + pub fn index_list() -> Self { + Pragma::new("index_list") + } + + pub fn index_x_info() -> Self { + Pragma::new("index_xinfo") + } + + pub fn integrity_check() -> Self { + Pragma::new("integrity_check") + } + + pub fn journal_mode() -> Self { + Pragma::new("journal_mode") + } + + pub fn journal_size_limit() -> Self { + Pragma::new("journal_size_limit") + } + + pub fn key() -> Self { + Pragma::new("key") + } + + pub fn kdf_iter() -> Self { + Pragma::new("kdf_iter") + } + + pub fn legacy_file_format() -> Self { + Pragma::new("legacy_file_format") + } + + pub fn locking_mode() -> Self { + Pragma::new("locking_mode") + } + + pub fn max_page_count() -> Self { + Pragma::new("max_page_count") + } + + pub fn mmap_size() -> Self { + Pragma::new("mmap_size") + } + + pub fn module_list() -> Self { + Pragma::new("module_list") + } + + pub fn optimize() -> Self { + Pragma::new("optimize") + } + + pub fn page_count() -> Self { + Pragma::new("page_count") + } + + pub fn page_size() -> Self { + Pragma::new("page_size") + } + + pub fn parser_trace() -> Self { + Pragma::new("parser_trace") + } + + pub fn pragma_list() -> Self { + Pragma::new("pragma_list") + } + + pub fn query_only() -> Self { + Pragma::new("query_only") + } + + pub fn quick_check() -> Self { + Pragma::new("quick_check") + } + + pub fn read_uncommitted() -> Self { + Pragma::new("read_uncommitted") + } + + pub fn recursive_triggers() -> Self { + Pragma::new("recursive_triggers") + } + + pub fn rekey() -> Self { + Pragma::new("rekey") + } + + pub fn reverse_unordered_selects() -> Self { + Pragma::new("reverse_unordered_selects") + } + + pub fn schema_version() -> Self { + Pragma::new("schema_version") + } + + pub fn secure_delete() -> Self { + Pragma::new("secure_delete") + } + + pub fn short_column_names() -> Self { + Pragma::new("short_column_names") + } + + pub fn shrink_memory() -> Self { + Pragma::new("shrink_memory") + } + + pub fn soft_heap_limit() -> Self { + Pragma::new("soft_heap_limit") + } + + pub fn stats() -> Self { + Pragma::new("stats") + } + + pub fn synchronous() -> Self { + Pragma::new("synchronous") + } + + pub fn table_info() -> Self { + Pragma::new("table_info") + } + + pub fn temp_store() -> Self { + Pragma::new("temp_store") + } + + pub fn temp_store_directory() -> Self { + Pragma::new("temp_store_directory") + } + + pub fn threads() -> Self { + Pragma::new("threads") + } + + pub fn user_version() -> Self { + Pragma::new("user_version") + } + + pub fn vdbe_addoptrace() -> Self { + Pragma::new("vdbe_addoptrace") + } + + pub fn vdbe_debug() -> Self { + Pragma::new("vdbe_debug") + } + + pub fn vdbe_listing() -> Self { + Pragma::new("vdbe_listing") + } + + pub fn vdbe_trace() -> Self { + Pragma::new("vdbe_trace") + } + + pub fn wal_autocheckpoint() -> Self { + Pragma::new("wal_autocheckpoint") + } + + pub fn wal_checkpoint() -> Self { + Pragma::new("wal_checkpoint") + } + + pub fn writable_schema() -> Self { + Pragma::new("writable_schema") + } +} diff --git a/src/rust/wcdb/src/winq/qualified_table.rs b/src/rust/wcdb/src/winq/qualified_table.rs new file mode 100644 index 000000000..91318690d --- /dev/null +++ b/src/rust/wcdb/src/winq/qualified_table.rs @@ -0,0 +1,111 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_schema::StringSchema; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustQualifiedTable_create(table_name: *const c_char) -> *mut c_void; + fn WCDBRustQualifiedTable_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + schema_name: *const c_char, + ); + + fn WCDBRustQualifiedTable_configAlias(result_column: *mut c_void, alias: *const c_char); + + fn WCDBRustQualifiedTable_configIndex(cpp_obj: *mut c_void, index_name: *const c_char); + + fn WCDBRustQualifiedTable_configNotIndexed(cpp_obj: *mut c_void); + +} +pub struct QualifiedTable { + identifier: Identifier, +} + +impl CppObjectTrait for QualifiedTable { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj) + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object() + } +} + +impl CppObjectConvertibleTrait for QualifiedTable { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for QualifiedTable { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for QualifiedTable { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl QualifiedTable { + pub fn new(table_name: &str) -> Self { + let cpp_obj = unsafe { WCDBRustQualifiedTable_create(table_name.to_cstring().as_ptr()) }; + QualifiedTable { + identifier: Identifier::new(CPPType::QualifiedTableName, Some(cpp_obj)), + } + } + + pub fn of<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustQualifiedTable_configSchema( + self.get_cpp_obj(), + cpp_type as i32, + cpp_obj, + name_ptr, + ) + } + self + } + + pub fn as_(&self, alias: &str) -> &Self { + unsafe { + WCDBRustQualifiedTable_configAlias(self.get_cpp_obj(), alias.to_cstring().as_ptr()) + } + self + } + + pub fn indexed(&self, index_name: &str) -> &Self { + unsafe { + WCDBRustQualifiedTable_configIndex(self.get_cpp_obj(), index_name.to_cstring().as_ptr()) + } + self + } + + pub fn not_indexed(&self) -> &Self { + unsafe { WCDBRustQualifiedTable_configNotIndexed(self.get_cpp_obj()) } + self + } +} diff --git a/src/rust/wcdb/src/winq/result_column.rs b/src/rust/wcdb/src/winq/result_column.rs new file mode 100644 index 000000000..c0e404ab5 --- /dev/null +++ b/src/rust/wcdb/src/winq/result_column.rs @@ -0,0 +1,101 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_result_column::StringResultColumn; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::result_column_convertible_trait::ResultColumnConvertibleTrait; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustResultColumn_create( + r#type: c_int, + convertible_obj: *mut c_void, + column_name: *const c_char, + ) -> *mut c_void; + + fn WCDBRustResultColumn_configAlias(result_column: *mut c_void, alias: *const c_char); +} + +pub struct ResultColumn { + identifier: Identifier, +} + +impl CppObjectTrait for ResultColumn { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj) + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object() + } +} + +impl CppObjectConvertibleTrait for ResultColumn { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for ResultColumn { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for ResultColumn { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl ResultColumnConvertibleTrait for ResultColumn {} + +impl ResultColumn { + pub(crate) fn new_with_cpp_obj(cpp_obj: *mut c_void) -> Self { + ResultColumn { + identifier: Identifier::new(CPPType::ResultColumn, Some(cpp_obj)), + } + } + + pub fn new<'a, T>(param: T) -> Self + where + T: Into>, + { + let cpp_obj = match param.into() { + StringResultColumn::String(column_name) => { + let cstr = column_name.to_cstring(); + unsafe { + WCDBRustResultColumn_create( + CPPType::String as i32, + 0 as *mut c_void, + cstr.as_ptr(), + ) + } + } + StringResultColumn::ResultColumn(result_column_convertible) => unsafe { + WCDBRustResultColumn_create( + Identifier::get_cpp_type(result_column_convertible) as c_int, + CppObject::get(result_column_convertible), + std::ptr::null(), + ) + }, + }; + ResultColumn { + identifier: Identifier::new(CPPType::ResultColumn, Some(cpp_obj)), + } + } + + pub fn as_(&self, alias: &str) -> &ResultColumn { + unsafe { WCDBRustResultColumn_configAlias(self.get_cpp_obj(), alias.to_cstring().as_ptr()) } + self + } +} diff --git a/src/rust/wcdb/src/winq/result_column_convertible_trait.rs b/src/rust/wcdb/src/winq/result_column_convertible_trait.rs new file mode 100644 index 000000000..86bdee1ee --- /dev/null +++ b/src/rust/wcdb/src/winq/result_column_convertible_trait.rs @@ -0,0 +1,3 @@ +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; + +pub trait ResultColumnConvertibleTrait: IdentifierConvertibleTrait {} diff --git a/src/rust/wcdb/src/winq/schema.rs b/src/rust/wcdb/src/winq/schema.rs new file mode 100644 index 000000000..475735fcd --- /dev/null +++ b/src/rust/wcdb/src/winq/schema.rs @@ -0,0 +1,86 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use std::ffi::{c_char, c_void}; + +extern "C" { + fn WCDBRustSchema_createWithName(table_name: *const c_char) -> *mut c_void; + + fn WCDBRustSchema_main() -> *mut c_void; + + fn WCDBRustSchema_temp() -> *mut c_void; +} +pub struct Schema { + identifier: Identifier, +} + +pub trait SchemaTrait: IdentifierTrait { + fn main() -> Schema; + fn temp() -> Schema; +} + +impl CppObjectTrait for Schema { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj) + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object() + } +} + +impl CppObjectConvertibleTrait for Schema { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for Schema { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for Schema { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl SchemaTrait for Schema { + fn main() -> Schema { + let cpp_obj = unsafe { WCDBRustSchema_main() }; + Schema::new_with_cpp_obj(cpp_obj) + } + + fn temp() -> Schema { + let cpp_obj = unsafe { WCDBRustSchema_temp() }; + Schema::new_with_cpp_obj(cpp_obj) + } +} + +impl Schema { + pub fn new(name: &str) -> Self { + let cstr = name.to_cstring(); + let cpp_obj = unsafe { WCDBRustSchema_createWithName(cstr.as_ptr()) }; + Schema { + identifier: Identifier::new(CPPType::Schema, Some(cpp_obj)), + } + } + + pub(crate) fn new_with_cpp_obj(cpp_obj: *mut c_void) -> Self { + Schema { + identifier: Identifier::new(CPPType::Schema, Some(cpp_obj)), + } + } +} diff --git a/src/rust/wcdb/src/winq/statement.rs b/src/rust/wcdb/src/winq/statement.rs new file mode 100644 index 000000000..26413a434 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement.rs @@ -0,0 +1,67 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::winq::identifier::{ + CPPType, Identifier, IdentifierTrait, WCDBRustWinq_isWriteStatement, +}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use std::ffi::c_void; +use std::fmt::Debug; + +#[derive(Debug)] +pub struct Statement { + identifier: Identifier, +} + +impl CppObjectTrait for Statement { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for Statement { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for Statement { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for Statement { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +pub trait StatementTrait: IdentifierTrait { + fn is_write_statement(&self) -> bool; +} + +impl StatementTrait for Statement { + fn is_write_statement(&self) -> bool { + unsafe { WCDBRustWinq_isWriteStatement(self.get_cpp_obj()) } + } +} + +impl Statement { + pub fn new(cpp_type: CPPType, cpp_obj_opt: Option<*mut c_void>) -> Statement { + Statement { + identifier: Identifier::new(cpp_type, cpp_obj_opt), + } + } +} diff --git a/src/rust/wcdb/src/winq/statement_alter_table.rs b/src/rust/wcdb/src/winq/statement_alter_table.rs new file mode 100644 index 000000000..c850ec26c --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_alter_table.rs @@ -0,0 +1,190 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_column::StringColumn; +use crate::base::param::enum_string_schema::StringSchema; +use crate::winq::column_def::ColumnDef; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_int, c_void, CString}; + +extern "C" { + fn WCDBRustStatementAlterTable_createCppObj() -> *mut c_void; + + fn WCDBRustStatementAlterTable_configTable(cpp_obj: *mut c_void, table_name: *const c_char); + + fn WCDBRustStatementAlterTable_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + schema_cpp_obj: *mut c_void, + schema_name: *const c_char, + ); + + fn WCDBRustStatementAlterTable_configRenameToTable( + cpp_obj: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustStatementAlterTable_configRenameColumn( + cpp_obj: *mut c_void, + cpp_type: c_int, + column_cpp_obj: *mut c_void, + column_name: *const c_char, + ); + + fn WCDBRustStatementAlterTable_configRenameToColumn( + cpp_obj: *mut c_void, + cpp_type: c_int, + column_cpp_obj: *mut c_void, + column_name: *const c_char, + ); + + fn WCDBRustStatementAlterTable_configAddColumn( + cpp_obj: *mut c_void, + column_def_cpp_obj: *mut c_void, + ); +} + +pub struct StatementAlterTable { + statement: Statement, +} + +impl CppObjectTrait for StatementAlterTable { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementAlterTable { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementAlterTable { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementAlterTable { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementAlterTable { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementAlterTable { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementAlterTable_createCppObj() }; + StatementAlterTable { + statement: Statement::new(CPPType::AlterTableSTMT, Some(cpp_obj)), + } + } + + pub fn alter_table(&self, table_name: &str) -> &Self { + let c_table_name = CString::new(table_name).unwrap_or_default(); + unsafe { + WCDBRustStatementAlterTable_configTable(self.get_cpp_obj(), c_table_name.as_ptr()); + } + self + } + + pub fn of<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementAlterTable_configSchema( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } + + pub fn rename_to(&self, table_name: &str) -> &Self { + let c_table_name = CString::new(table_name).unwrap_or_default(); + unsafe { + WCDBRustStatementAlterTable_configRenameToTable( + self.get_cpp_obj(), + c_table_name.as_ptr(), + ); + } + self + } + + pub fn rename_column<'a, T>(&self, column: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = column.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementAlterTable_configRenameColumn( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } + + pub fn to_column<'a, T>(&self, column: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = column.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementAlterTable_configRenameToColumn( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } + + pub fn add_column(&self, column_def: &ColumnDef) -> &Self { + unsafe { + WCDBRustStatementAlterTable_configAddColumn( + self.get_cpp_obj(), + column_def.get_cpp_obj(), + ); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_analyze.rs b/src/rust/wcdb/src/winq/statement_analyze.rs new file mode 100644 index 000000000..311eddc0b --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_analyze.rs @@ -0,0 +1,124 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_schema::StringSchema; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementAnalyze_createCppObj() -> *mut c_void; + + fn WCDBRustStatementAnalyze_toAnalyze(cpp_obj: *mut c_void); + + fn WCDBRustStatementAnalyze_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *const c_void, + schema_name: *const c_char, + ); + + fn WCDBRustStatementAnalyze_configTable(cpp_obj: *mut c_void, table_name: *const c_char); + + fn WCDBRustStatementAnalyze_configIndex(cpp_obj: *mut c_void, index_name: *const c_char); +} + +#[derive(Debug)] +pub struct StatementAnalyze { + statement: Statement, +} + +impl CppObjectTrait for StatementAnalyze { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementAnalyze { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementAnalyze { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementAnalyze { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementAnalyze { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementAnalyze { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementAnalyze_createCppObj() }; + StatementAnalyze { + statement: Statement::new(CPPType::AnalyzeSTMT, Some(cpp_obj)), + } + } + + pub fn analyze(&self) -> &Self { + unsafe { + WCDBRustStatementAnalyze_toAnalyze(self.get_cpp_obj()); + } + self + } + + pub fn schema<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementAnalyze_configSchema( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } + + pub fn table(&self, table_name: &str) -> &Self { + let c_str = table_name.to_string().to_cstring(); + unsafe { + WCDBRustStatementAnalyze_configTable(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } + + pub fn index(&self, index_name: &str) -> &Self { + let c_str = index_name.to_string().to_cstring(); + unsafe { + WCDBRustStatementAnalyze_configIndex(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_attach.rs b/src/rust/wcdb/src/winq/statement_attach.rs new file mode 100644 index 000000000..ce031e815 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_attach.rs @@ -0,0 +1,149 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_bind_parameter::StringBindParameter; +use crate::base::param::enum_string_schema::StringSchema; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementAttach_createCppObj() -> *mut c_void; + + fn WCDBRustStatementAttach_configPath( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *const c_void, + path: *const c_char, + ); + + fn WCDBRustStatementAttach_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *const c_void, + path: *const c_char, + ); + + fn WCDBRustStatementAttach_configKey( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *const c_void, + path: *const c_char, + ); +} + +#[derive(Debug)] +pub struct StatementAttach { + statement: Statement, +} + +impl CppObjectTrait for StatementAttach { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementAttach { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementAttach { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementAttach { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementAttach { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementAttach { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementAttach_createCppObj() }; + StatementAttach { + statement: Statement::new(CPPType::AttachSTMT, Some(cpp_obj)), + } + } + + pub fn attach<'a, T>(&self, param: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = param.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementAttach_configPath( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } + + pub fn as_<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementAttach_configSchema( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } + + pub fn key<'a, T>(&self, param: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = param.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementAttach_configKey( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_begin.rs b/src/rust/wcdb/src/winq/statement_begin.rs new file mode 100644 index 000000000..5afe9b8b7 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_begin.rs @@ -0,0 +1,86 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_int, c_void}; + +pub struct TransactionType; +impl TransactionType { + pub const DEFERRED: i32 = 0; + pub const IMMEDIATE: i32 = 1; + pub const EXCLUSIVE: i32 = 2; +} + +extern "C" { + fn WCDBRustStatementBegin_create(type_i: c_int) -> *mut c_void; +} + +#[derive(Debug)] +pub struct StatementBegin { + statement: Statement, +} + +impl CppObjectTrait for StatementBegin { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementBegin { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementBegin { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementBegin { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementBegin { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementBegin { + pub fn new(cpp_type: Option) -> Self { + let transaction_type: i32 = cpp_type.unwrap_or_else(|| TransactionType::DEFERRED); + let cpp_obj = unsafe { WCDBRustStatementBegin_create(transaction_type) }; + StatementBegin { + statement: Statement::new(CPPType::CommitSTMT, Some(cpp_obj)), + } + } + + pub fn begin_deferred() -> StatementBegin { + StatementBegin::new(Some(TransactionType::DEFERRED)) + } + + pub fn begin_immediate() -> StatementBegin { + StatementBegin::new(Some(TransactionType::IMMEDIATE)) + } + + pub fn begin_exclusive() -> StatementBegin { + StatementBegin::new(Some(TransactionType::EXCLUSIVE)) + } +} diff --git a/src/rust/wcdb/src/winq/statement_commit.rs b/src/rust/wcdb/src/winq/statement_commit.rs new file mode 100644 index 000000000..a1c05d85d --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_commit.rs @@ -0,0 +1,66 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::c_void; + +extern "C" { + fn WCDBRustStatementCommit_create() -> *mut c_void; +} + +#[derive(Debug)] +pub struct StatementCommit { + statement: Statement, +} + +impl CppObjectTrait for StatementCommit { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementCommit { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementCommit { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementCommit { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementCommit { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementCommit { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementCommit_create() }; + StatementCommit { + statement: Statement::new(CPPType::CommitSTMT, Some(cpp_obj)), + } + } +} diff --git a/src/rust/wcdb/src/winq/statement_create_index.rs b/src/rust/wcdb/src/winq/statement_create_index.rs new file mode 100644 index 000000000..abccb0925 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_create_index.rs @@ -0,0 +1,209 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_indexed_column::StringIndexedColumn; +use crate::base::param::enum_string_schema::StringSchema; +use crate::utils::ToCString; +use crate::winq::expression::Expression; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use core::ffi::c_size_t; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementCreateIndex_create() -> *mut c_void; + + fn WCDBRustStatementCreateIndex_configIndex(cpp_obj: *mut c_void, index_name: *const c_char); + + fn WCDBRustStatementCreateIndex_configUnique(cpp_obj: *mut c_void); + + fn WCDBRustStatementCreateIndex_configSchemaName( + cpp_obj: *mut c_void, + schema_name: *const c_char, + ); + + fn WCDBRustStatementCreateIndex_configIfNotExist(cpp_obj: *mut c_void); + + fn WCDBRustStatementCreateIndex_configIndexedColumns( + cpp_obj: *mut c_void, + columns_type: c_int, + columns_void_vec: *const *mut c_void, + columns_string_vec: *const *const c_char, + columns_vec_len: c_size_t, + ); + + fn WCDBRustStatementCreateIndex_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + schema: *mut c_void, + schema_name: *const c_char, + ); + + fn WCDBRustStatementCreateIndex_configTable(cpp_obj: *mut c_void, table_name: *const c_char); + + fn WCDBRustStatementCreateIndex_configWhere(cpp_obj: *mut c_void, condition: *mut c_void); +} + +pub struct StatementCreateIndex { + statement: Statement, +} + +impl CppObjectTrait for StatementCreateIndex { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementCreateIndex { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementCreateIndex { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementCreateIndex { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementCreateIndex { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementCreateIndex { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementCreateIndex_create() }; + StatementCreateIndex { + statement: Statement::new(CPPType::CreateIndexSTMT, Some(cpp_obj)), + } + } + + pub fn create_index(&self, index_name: &str) -> &Self { + unsafe { + WCDBRustStatementCreateIndex_configIndex( + self.get_cpp_obj(), + index_name.to_cstring().as_ptr(), + ) + } + self + } + + pub fn unique(&self) -> &Self { + unsafe { WCDBRustStatementCreateIndex_configUnique(self.get_cpp_obj()) } + self + } + + pub fn if_not_exist(&self) -> &Self { + unsafe { + WCDBRustStatementCreateIndex_configIfNotExist(self.get_cpp_obj()); + } + self + } + + pub fn of<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementCreateIndex_configSchema( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ) + } + self + } + + pub fn on(&self, table_name: &str) -> &Self { + unsafe { + WCDBRustStatementCreateIndex_configTable( + self.get_cpp_obj(), + table_name.to_cstring().as_ptr(), + ) + } + self + } + + pub fn indexed_by<'a, I, S>(&self, column_vec: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = column_vec.into_iter().map(Into::into).collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type = CPPType::String; + let mut cpp_str_vec = vec![]; + let mut cpp_obj_vec = vec![]; + let mut c_strings = Vec::new(); + for item in data_vec { + match item { + StringIndexedColumn::String(str) => { + let c_string = str.to_cstring(); + cpp_str_vec.push(c_string.as_ptr()); + c_strings.push(c_string); + } + StringIndexedColumn::IndexedColumnConvertible(obj) => { + cpp_type = Identifier::get_cpp_type(obj.as_identifier()); + cpp_obj_vec.push(CppObject::get(obj)); + } + } + } + if !cpp_str_vec.is_empty() { + unsafe { + WCDBRustStatementCreateIndex_configIndexedColumns( + self.get_cpp_obj(), + CPPType::String as c_int, + std::ptr::null(), + cpp_str_vec.as_ptr(), + cpp_str_vec.len(), + ); + } + } else { + unsafe { + WCDBRustStatementCreateIndex_configIndexedColumns( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj_vec.as_ptr(), + std::ptr::null(), + cpp_obj_vec.len(), + ); + } + } + self + } + + pub fn where_(&self, condition: &Expression) -> &Self { + unsafe { + WCDBRustStatementCreateIndex_configWhere(self.get_cpp_obj(), CppObject::get(condition)) + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_create_table.rs b/src/rust/wcdb/src/winq/statement_create_table.rs new file mode 100644 index 000000000..606963d39 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_create_table.rs @@ -0,0 +1,107 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::utils::ToCString; +use crate::winq::column_def::ColumnDef; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementCreateTable_create() -> *mut c_void; + + fn WCDBRustStatementCreateTable_configTableName( + cpp_obj: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustStatementCreateTable_configColumns( + cpp_obj: *mut c_void, + columns_void_vec: *const *mut c_void, + columns_vec_len: c_int, + ); +} + +pub struct StatementCreateTable { + statement: Statement, +} + +impl CppObjectTrait for StatementCreateTable { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementCreateTable { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementCreateTable { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementCreateTable { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementCreateTable { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementCreateTable { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementCreateTable_create() }; + StatementCreateTable { + statement: Statement::new(CPPType::CreateTableSTMT, Some(cpp_obj)), + } + } + + pub fn create_table(&self, table_name: &str) -> &Self { + unsafe { + WCDBRustStatementCreateTable_configTableName( + self.get_cpp_obj(), + table_name.to_cstring().as_ptr(), + ); + } + self + } + + pub fn define(&self, column_defs: Vec<&ColumnDef>) -> &Self { + if column_defs.is_empty() { + return self; + } + let len = column_defs.len() as i32; + let mut c_void_vec: Vec<*mut c_void> = Vec::with_capacity(column_defs.len()); + for column_def in column_defs { + c_void_vec.push(column_def.get_cpp_obj()); + } + unsafe { + WCDBRustStatementCreateTable_configColumns( + self.get_cpp_obj(), + c_void_vec.as_ptr(), + len, + ); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_create_trigger.rs b/src/rust/wcdb/src/winq/statement_create_trigger.rs new file mode 100644 index 000000000..e07c33f30 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_create_trigger.rs @@ -0,0 +1,315 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_column::StringColumn; +use crate::base::param::enum_string_schema::StringSchema; +use crate::base::param::enum_trigger_statement::TriggerStatement; +use crate::utils::ToCString; +use crate::winq::expression::Expression; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use crate::winq::statement_delete::StatementDelete; +use crate::winq::statement_insert::StatementInsert; +use crate::winq::statement_select::StatementSelect; +use crate::winq::statement_update::StatementUpdate; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementCreateTrigger_createCppObj() -> *mut c_void; + + fn WCDBRustStatementCreateTrigger_configTrigger(cpp_obj: *mut c_void, name: *const c_char); + + fn WCDBRustStatementCreateTrigger_configTemp(cpp_obj: *mut c_void); + + fn WCDBRustStatementCreateTrigger_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *const c_void, + path: *const c_char, + ); + + fn WCDBRustStatementCreateTrigger_configIfNotExist(cpp_obj: *mut c_void); + + fn WCDBRustStatementCreateTrigger_configBefore(cpp_obj: *mut c_void); + + fn WCDBRustStatementCreateTrigger_configAfter(cpp_obj: *mut c_void); + + fn WCDBRustStatementCreateTrigger_configInsteadOf(cpp_obj: *mut c_void); + + fn WCDBRustStatementCreateTrigger_configDelete(cpp_obj: *mut c_void); + + fn WCDBRustStatementCreateTrigger_configInsert(cpp_obj: *mut c_void); + + fn WCDBRustStatementCreateTrigger_configUpdate(cpp_obj: *mut c_void); + + fn WCDBRustStatementCreateTrigger_configColumns( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *const *mut c_void, + column_names: *const *const c_char, + len: c_int, + ); + + fn WCDBRustStatementCreateTrigger_configTable(cpp_obj: *mut c_void, table: *const c_char); + + fn WCDBRustStatementCreateTrigger_configForEachRow(cpp_obj: *mut c_void); + + fn WCDBRustStatementCreateTrigger_configWhen(cpp_obj: *mut c_void, condition: *const c_void); + + fn WCDBRustStatementCreateTrigger_executeInsert(cpp_obj: *mut c_void, insert: *const c_void); + fn WCDBRustStatementCreateTrigger_executeUpdate(cpp_obj: *mut c_void, insert: *const c_void); + fn WCDBRustStatementCreateTrigger_executeDelete(cpp_obj: *mut c_void, insert: *const c_void); + fn WCDBRustStatementCreateTrigger_executeSelect(cpp_obj: *mut c_void, insert: *const c_void); +} + +#[derive(Debug)] +pub struct StatementCreateTrigger { + statement: Statement, +} + +impl CppObjectTrait for StatementCreateTrigger { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementCreateTrigger { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementCreateTrigger { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementCreateTrigger { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementCreateTrigger { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementCreateTrigger { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementCreateTrigger_createCppObj() }; + StatementCreateTrigger { + statement: Statement::new(CPPType::CreateTriggerSTMT, Some(cpp_obj)), + } + } + + pub fn create_trigger(&self, name: &str) -> &Self { + let c_str = name.to_string().to_cstring(); + unsafe { + WCDBRustStatementCreateTrigger_configTrigger(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } + + pub fn create_temp_trigger(&self, name: &str) -> &Self { + let c_str = name.to_string().to_cstring(); + unsafe { + WCDBRustStatementCreateTrigger_configTrigger(self.get_cpp_obj(), c_str.as_ptr()); + + WCDBRustStatementCreateTrigger_configTemp(self.get_cpp_obj()); + } + self + } + + pub fn of_schema<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementCreateTrigger_configSchema( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } + + pub fn if_not_exist(&self) -> &Self { + unsafe { + WCDBRustStatementCreateTrigger_configIfNotExist(self.get_cpp_obj()); + } + self + } + + pub fn before(&self) -> &Self { + unsafe { + WCDBRustStatementCreateTrigger_configBefore(self.get_cpp_obj()); + } + self + } + + pub fn after(&self) -> &Self { + unsafe { + WCDBRustStatementCreateTrigger_configAfter(self.get_cpp_obj()); + } + self + } + + pub fn instead_of(&self) -> &Self { + unsafe { + WCDBRustStatementCreateTrigger_configInsteadOf(self.get_cpp_obj()); + } + self + } + + pub fn delete(&self) -> &Self { + unsafe { + WCDBRustStatementCreateTrigger_configDelete(self.get_cpp_obj()); + } + self + } + + pub fn insert(&self) -> &Self { + unsafe { + WCDBRustStatementCreateTrigger_configInsert(self.get_cpp_obj()); + } + self + } + + pub fn update(&self) -> &Self { + unsafe { + WCDBRustStatementCreateTrigger_configUpdate(self.get_cpp_obj()); + } + self + } + + pub fn of_columns<'a, I, S>(&self, column_vec: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = column_vec.into_iter().map(Into::into).collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type = CPPType::String; + let mut cpp_str_vec = vec![]; + let mut cpp_obj_vec = vec![]; + let mut c_strings = vec![]; + for item in data_vec { + match item { + StringColumn::String(str) => { + let c_string = str.to_cstring(); + cpp_str_vec.push(c_string.as_ptr()); + c_strings.push(c_string); + } + StringColumn::Column(obj) => { + cpp_type = Identifier::get_cpp_type(obj.as_identifier()); + cpp_obj_vec.push(CppObject::get(obj)); + } + } + } + if !cpp_str_vec.is_empty() { + unsafe { + WCDBRustStatementCreateTrigger_configColumns( + self.get_cpp_obj(), + CPPType::String as c_int, + std::ptr::null_mut(), + cpp_str_vec.as_ptr(), + cpp_str_vec.len() as c_int, + ); + } + } else { + unsafe { + WCDBRustStatementCreateTrigger_configColumns( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj_vec.as_ptr(), + std::ptr::null_mut(), + cpp_obj_vec.len() as c_int, + ); + } + } + self + } + + pub fn on_table(&self, table_name: &str) -> &Self { + let c_str = table_name.to_string().to_cstring(); + unsafe { + WCDBRustStatementCreateTrigger_configTable(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } + + pub fn for_each_row(&self) -> &Self { + unsafe { + WCDBRustStatementCreateTrigger_configForEachRow(self.get_cpp_obj()); + } + self + } + + pub fn when(&self, condition: &Expression) -> &Self { + unsafe { + WCDBRustStatementCreateTrigger_configWhen( + self.get_cpp_obj(), + CppObject::get(condition), + ); + } + self + } + + pub fn execute<'a, T>(&self, statement: T) -> &Self + where + T: Into>, + { + let statement_enum = statement.into(); + match statement_enum { + TriggerStatement::Insert(insert) => unsafe { + WCDBRustStatementCreateTrigger_executeInsert( + self.get_cpp_obj(), + CppObject::get(insert), + ); + }, + TriggerStatement::Delete(delete) => unsafe { + WCDBRustStatementCreateTrigger_executeDelete( + self.get_cpp_obj(), + CppObject::get(delete), + ); + }, + TriggerStatement::Update(update) => unsafe { + WCDBRustStatementCreateTrigger_executeUpdate( + self.get_cpp_obj(), + CppObject::get(update), + ); + }, + TriggerStatement::Select(select) => unsafe { + WCDBRustStatementCreateTrigger_executeSelect( + self.get_cpp_obj(), + CppObject::get(select), + ); + }, + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_create_view.rs b/src/rust/wcdb/src/winq/statement_create_view.rs new file mode 100644 index 000000000..1c809c5b8 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_create_view.rs @@ -0,0 +1,194 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_column::StringColumn; +use crate::base::param::enum_string_schema::StringSchema; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use crate::winq::statement_select::StatementSelect; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementCreateView_createCppObj() -> *mut c_void; + + fn WCDBRustStatementCreateView_configView(cpp_obj: *mut c_void, name: *const c_char); + + fn WCDBRustStatementCreateView_configTemp(cpp_obj: *mut c_void); + + fn WCDBRustStatementCreateView_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *const c_void, + path: *const c_char, + ); + + fn WCDBRustStatementCreateView_configIfNotExist(cpp_obj: *mut c_void); + + fn WCDBRustStatementCreateView_configColumns( + cpp_obj: *mut c_void, + cpp_obj_type: c_int, + columns: *const *mut c_void, + columns_string_vec: *const *const c_char, + vec_len: c_int, + ); + + fn WCDBRustStatementCreateView_configAs(cpp_obj: *mut c_void, select: *const c_void); +} + +#[derive(Debug)] +pub struct StatementCreateView { + statement: Statement, +} + +impl CppObjectTrait for StatementCreateView { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementCreateView { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementCreateView { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementCreateView { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementCreateView { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementCreateView { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementCreateView_createCppObj() }; + StatementCreateView { + statement: Statement::new(CPPType::CreateViewSTMT, Some(cpp_obj)), + } + } + + pub fn create_view(&self, name: &str) -> &Self { + let c_str = name.to_string().to_cstring(); + unsafe { + WCDBRustStatementCreateView_configView(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } + + pub fn create_temp_view(&self, name: &str) -> &Self { + let c_str = name.to_string().to_cstring(); + unsafe { + WCDBRustStatementCreateView_configView(self.get_cpp_obj(), c_str.as_ptr()); + WCDBRustStatementCreateView_configTemp(self.get_cpp_obj()); + } + self + } + + pub fn of<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementCreateView_configSchema( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } + + pub fn if_not_exist(&self) -> &Self { + unsafe { + WCDBRustStatementCreateView_configIfNotExist(self.get_cpp_obj()); + } + self + } + + pub fn with_columns<'a, I, S>(&self, column_vec: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = column_vec.into_iter().map(Into::into).collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type = CPPType::String; + let mut cpp_str_vec = vec![]; + let mut cpp_obj_vec = vec![]; + let mut c_strings = vec![]; + for item in data_vec { + match item { + StringColumn::String(str) => { + let c_string = str.to_cstring(); + cpp_str_vec.push(c_string.as_ptr()); + c_strings.push(c_string); + } + StringColumn::Column(obj) => { + cpp_type = Identifier::get_cpp_type(obj.as_identifier()); + cpp_obj_vec.push(CppObject::get(obj)); + } + } + } + if !cpp_str_vec.is_empty() { + unsafe { + WCDBRustStatementCreateView_configColumns( + self.get_cpp_obj(), + CPPType::String as c_int, + std::ptr::null_mut(), + cpp_str_vec.as_ptr(), + cpp_obj_vec.len() as c_int, + ) + } + } else { + unsafe { + WCDBRustStatementCreateView_configColumns( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj_vec.as_ptr(), + std::ptr::null_mut(), + cpp_obj_vec.len() as c_int, + ) + } + } + self + } + + pub fn as_statement_select(&self, select: &StatementSelect) -> &Self { + unsafe { + WCDBRustStatementCreateView_configAs(self.get_cpp_obj(), CppObject::get(select)); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_create_virtual_table.rs b/src/rust/wcdb/src/winq/statement_create_virtual_table.rs new file mode 100644 index 000000000..c7202bff5 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_create_virtual_table.rs @@ -0,0 +1,149 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_schema::StringSchema; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use core::ffi::c_size_t; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementCreateVirtualTable_createCppObj() -> *mut c_void; + + fn WCDBRustStatementCreateVirtualTable_configTable(cpp_obj: *mut c_void, name: *const c_char); + + fn WCDBRustStatementCreateVirtualTable_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *const c_void, + path: *const c_char, + ); + + fn WCDBRustStatementCreateVirtualTable_configIfNotExist(cpp_obj: *mut c_void) -> *mut c_void; + + fn WCDBRustStatementCreateVirtualTable_configModule( + cpp_obj: *mut c_void, + module: *const c_char, + ) -> *mut c_void; + + fn WCDBRustStatementCreateVirtualTable_configArguments( + cpp_obj: *mut c_void, + argument: *const *const c_char, + argument_len: c_size_t, + ) -> *mut c_void; +} + +#[derive(Debug)] +pub struct StatementCreateVirtualTable { + statement: Statement, +} + +impl CppObjectTrait for StatementCreateVirtualTable { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementCreateVirtualTable { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementCreateVirtualTable { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementCreateVirtualTable { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementCreateVirtualTable { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementCreateVirtualTable { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementCreateVirtualTable_createCppObj() }; + StatementCreateVirtualTable { + statement: Statement::new(CPPType::CreateVirtualTableSTMT, Some(cpp_obj)), + } + } + + pub fn create_virtual_table(&self, name: &str) -> &Self { + let c_str = name.to_string().to_cstring(); + unsafe { + WCDBRustStatementCreateVirtualTable_configTable(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } + + pub fn of<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementCreateVirtualTable_configSchema( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } + + pub fn if_not_exist(&self) -> &Self { + unsafe { + WCDBRustStatementCreateVirtualTable_configIfNotExist(self.get_cpp_obj()); + } + self + } + + pub fn using_module(&self, module: &str) -> &Self { + let c_str = module.to_string().to_cstring(); + unsafe { + WCDBRustStatementCreateVirtualTable_configModule(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } + + pub fn arguments(&self, arguments: Vec) -> &Self { + let mut c_string_array: Vec<*const c_char> = Vec::with_capacity(arguments.len()); + for x in arguments { + c_string_array.push(x.to_cstring().into_raw()); + } + unsafe { + WCDBRustStatementCreateVirtualTable_configArguments( + self.get_cpp_obj(), + c_string_array.as_ptr(), + c_string_array.len(), + ); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_delete.rs b/src/rust/wcdb/src/winq/statement_delete.rs new file mode 100644 index 000000000..b7aa644bd --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_delete.rs @@ -0,0 +1,169 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::winq::expression::Expression; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::ordering_term::OrderingTerm; +use crate::winq::statement::{Statement, StatementTrait}; +use core::ffi::c_size_t; +use std::ffi::{c_char, c_int, c_void, CString}; +use std::fmt::Debug; + +extern "C" { + fn WCDBRustStatementDelete_create() -> *mut c_void; + + fn WCDBRustStatementDelete_configTable( + cpp_obj: *mut c_void, + table_type: c_int, + table_long: i64, + table_string: *const c_char, + ); + + fn WCDBRustStatementDelete_configCondition(cpp_obj: *mut c_void, condition: *mut c_void); + + fn WCDBRustStatementDelete_configOrders( + cpp_obj: *mut c_void, + orders: *const *mut c_void, + vec_len: c_size_t, + ); + + fn WCDBRustStatementDelete_configLimitCount( + cpp_obj: *mut c_void, + config_type: c_int, + limit: i64, + ); + fn WCDBRustStatementDelete_configOffset(cpp_obj: *mut c_void, config_type: c_int, offset: i64); + + fn WCDBRustStatementDelete_configLimitRange( + cpp_obj: *mut c_void, + from_type: c_int, + from: i64, + to_type: c_int, + to: i64, + ); +} + +#[derive(Debug)] +pub struct StatementDelete { + statement: Statement, +} + +impl CppObjectTrait for StatementDelete { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementDelete { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementDelete { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementDelete { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementDelete { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementDelete { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementDelete_create() }; + StatementDelete { + statement: Statement::new(CPPType::DeleteSTMT, Some(cpp_obj)), + } + } + + pub fn delete_from(&self, table_name: &str) -> &Self { + let c_table_name = CString::new(table_name).unwrap_or_default(); + unsafe { + WCDBRustStatementDelete_configTable( + self.get_cpp_obj(), + CPPType::String as i32, + 0, + c_table_name.as_ptr(), + ); + } + self + } + + pub fn where_(&self, condition: &Expression) -> &Self { + unsafe { + WCDBRustStatementDelete_configCondition(self.get_cpp_obj(), CppObject::get(condition)); + } + self + } + + pub fn order_by(&self, orders: Vec<&OrderingTerm>) -> &Self { + if orders.is_empty() { + return self; + } + let mut order_raw_vec = Vec::with_capacity(orders.len()); + for order in orders { + order_raw_vec.push(order.get_cpp_obj()); + } + unsafe { + WCDBRustStatementDelete_configOrders( + self.get_cpp_obj(), + order_raw_vec.as_ptr(), + order_raw_vec.len(), + ); + } + self + } + + pub fn limit(&self, count: i64) -> &Self { + unsafe { + WCDBRustStatementDelete_configLimitCount( + self.get_cpp_obj(), + CPPType::Int as i32, + count, + ); + } + self + } + + pub fn offset(&self, offset: i64) -> &Self { + unsafe { + WCDBRustStatementDelete_configOffset(self.get_cpp_obj(), CPPType::Int as i32, offset); + } + self + } + + pub fn limit_range(&self, from: i64, to: i64) -> &Self { + unsafe { + WCDBRustStatementDelete_configLimitRange( + self.get_cpp_obj(), + CPPType::Int as i32, + from, + CPPType::Int as i32, + to, + ); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_detach.rs b/src/rust/wcdb/src/winq/statement_detach.rs new file mode 100644 index 000000000..ea9f842ec --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_detach.rs @@ -0,0 +1,94 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_schema::StringSchema; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementDetach_createCppObj() -> *mut c_void; + + fn WCDBRustStatementDetach_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *const c_void, + path: *const c_char, + ); +} + +#[derive(Debug)] +pub struct StatementDetach { + statement: Statement, +} + +impl CppObjectTrait for StatementDetach { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementDetach { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementDetach { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementDetach { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementDetach { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementDetach { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementDetach_createCppObj() }; + StatementDetach { + statement: Statement::new(CPPType::DetachSTMT, Some(cpp_obj)), + } + } + + pub fn detach<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementDetach_configSchema( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_drop_index.rs b/src/rust/wcdb/src/winq/statement_drop_index.rs new file mode 100644 index 000000000..3773bc932 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_drop_index.rs @@ -0,0 +1,115 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_schema::StringSchema; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementDropIndex_createCppObj() -> *mut c_void; + + fn WCDBRustStatementDropIndex_configIndex(cpp_obj: *mut c_void, index_name: *const c_char); + + fn WCDBRustStatementDropIndex_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + schema_cpp_obj: *mut c_void, + schema_name: *const c_char, + ); + + fn WCDBRustStatementDropIndex_configIfExist(cpp_obj: *mut c_void); +} + +pub struct StatementDropIndex { + statement: Statement, +} + +impl CppObjectTrait for StatementDropIndex { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementDropIndex { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementDropIndex { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementDropIndex { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementDropIndex { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementDropIndex { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementDropIndex_createCppObj() }; + StatementDropIndex { + statement: Statement::new(CPPType::DropIndexSTMT, Some(cpp_obj)), + } + } + + pub fn drop_index(&self, index_name: &str) -> &Self { + unsafe { + WCDBRustStatementDropIndex_configIndex( + self.get_cpp_obj(), + index_name.to_cstring().as_ptr(), + ); + } + self + } + + pub fn of<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementDropIndex_configSchema( + self.get_cpp_obj(), + cpp_type as i32, + cpp_obj, + name_ptr, + ); + } + self + } + + pub fn if_exist(&self) -> &Self { + unsafe { + WCDBRustStatementDropIndex_configIfExist(self.get_cpp_obj()); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_drop_table.rs b/src/rust/wcdb/src/winq/statement_drop_table.rs new file mode 100644 index 000000000..9e20c274e --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_drop_table.rs @@ -0,0 +1,115 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_schema::StringSchema; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementDropTable_create() -> *mut c_void; + + fn WCDBRustStatementDropTable_configTableName(cpp_obj: *mut c_void, table_name: *const c_char); + + fn WCDBRustStatementDropTable_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + schema_cpp_obj: *mut c_void, + schema_name: *const c_char, + ); + + fn WCDBRustStatementDropTable_configIfExist(cpp_obj: *mut c_void); +} + +pub struct StatementDropTable { + statement: Statement, +} + +impl CppObjectTrait for StatementDropTable { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementDropTable { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementDropTable { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementDropTable { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementDropTable { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementDropTable { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementDropTable_create() }; + StatementDropTable { + statement: Statement::new(CPPType::DropTableSTMT, Some(cpp_obj)), + } + } + + pub fn drop_table(&self, table_name: &str) -> &Self { + unsafe { + WCDBRustStatementDropTable_configTableName( + self.get_cpp_obj(), + table_name.to_cstring().as_ptr(), + ); + } + self + } + + pub fn of<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementDropTable_configSchema( + self.get_cpp_obj(), + cpp_type as i32, + cpp_obj, + name_ptr, + ) + } + self + } + + pub fn if_exist(&self) -> &Self { + unsafe { + WCDBRustStatementDropTable_configIfExist(self.get_cpp_obj()); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_drop_trigger.rs b/src/rust/wcdb/src/winq/statement_drop_trigger.rs new file mode 100644 index 000000000..e12473201 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_drop_trigger.rs @@ -0,0 +1,117 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_schema::StringSchema; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementDropTrigger_createCppObj() -> *mut c_void; + + fn WCDBRustStatementDropTrigger_configTrigger( + cpp_obj: *mut c_void, + trigger_name: *const c_char, + ); + + fn WCDBRustStatementDropTrigger_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *const c_void, + path: *const c_char, + ); + + fn WCDBRustStatementDropTrigger_configIfExist(cpp_obj: *mut c_void); +} + +#[derive(Debug)] +pub struct StatementDropTrigger { + statement: Statement, +} + +impl CppObjectTrait for StatementDropTrigger { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementDropTrigger { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementDropTrigger { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementDropTrigger { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementDropTrigger { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementDropTrigger { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementDropTrigger_createCppObj() }; + StatementDropTrigger { + statement: Statement::new(CPPType::DropTriggerSTMT, Some(cpp_obj)), + } + } + + pub fn drop_trigger(&self, trigger_name: &str) -> &Self { + let c_str = trigger_name.to_string().to_cstring(); + unsafe { + WCDBRustStatementDropTrigger_configTrigger(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } + + pub fn of<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementDropTrigger_configSchema( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } + + pub fn if_exist(&self) -> &Self { + unsafe { + WCDBRustStatementDropTrigger_configIfExist(self.get_cpp_obj()); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_drop_view.rs b/src/rust/wcdb/src/winq/statement_drop_view.rs new file mode 100644 index 000000000..71fdfc715 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_drop_view.rs @@ -0,0 +1,114 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_schema::StringSchema; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementDropView_createCppObj() -> *mut c_void; + + fn WCDBRustStatementDropView_configView(cpp_obj: *mut c_void, view_name: *const c_char); + + fn WCDBRustStatementDropView_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *const c_void, + path: *const c_char, + ); + + fn WCDBRustStatementDropView_configIfExist(cpp_obj: *mut c_void); +} + +#[derive(Debug)] +pub struct StatementDropView { + statement: Statement, +} + +impl CppObjectTrait for StatementDropView { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementDropView { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementDropView { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementDropView { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementDropView { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementDropView { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementDropView_createCppObj() }; + StatementDropView { + statement: Statement::new(CPPType::DropViewSTMT, Some(cpp_obj)), + } + } + + pub fn drop_view(&self, view_name: &str) -> &Self { + let c_str = view_name.to_string().to_cstring(); + unsafe { + WCDBRustStatementDropView_configView(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } + + pub fn of<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementDropView_configSchema( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } + + pub fn if_exist(&self) -> &Self { + unsafe { + WCDBRustStatementDropView_configIfExist(self.get_cpp_obj()); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_explain.rs b/src/rust/wcdb/src/winq/statement_explain.rs new file mode 100644 index 000000000..1daa31481 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_explain.rs @@ -0,0 +1,89 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::c_void; + +extern "C" { + fn WCDBRustStatementExplain_createCppObj() -> *mut c_void; + + fn WCDBRustStatementExplain_explain( + cpp_obj: *mut c_void, + statement: *mut c_void, + query_plan: bool, + ); +} + +#[derive(Debug)] +pub struct StatementExplain { + statement: Statement, +} + +impl CppObjectTrait for StatementExplain { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementExplain { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementExplain { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementExplain { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementExplain { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementExplain { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementExplain_createCppObj() }; + StatementExplain { + statement: Statement::new(CPPType::ExplainSTMT, Some(cpp_obj)), + } + } + + pub fn explain(&self, statement: &T) -> &Self { + unsafe { + WCDBRustStatementExplain_explain(self.get_cpp_obj(), CppObject::get(statement), false); + } + self + } + + pub fn explain_query_plan( + &self, + statement: &T, + ) -> &Self { + unsafe { + WCDBRustStatementExplain_explain(self.get_cpp_obj(), CppObject::get(statement), true); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_insert.rs b/src/rust/wcdb/src/winq/statement_insert.rs new file mode 100644 index 000000000..f5f1b6ae6 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_insert.rs @@ -0,0 +1,285 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_column::StringColumn; +use crate::utils::ToCString; +use crate::winq::conflict_action::ConflictAction; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::multi_type_array::MultiTypeArray; +use crate::winq::object::Object; +use crate::winq::statement::{Statement, StatementTrait}; +use crate::winq::statement_select::StatementSelect; +use crate::winq::upsert::Upsert; +use std::ffi::{c_char, c_double, c_int, c_longlong, c_void, CString}; +use std::fmt::Debug; + +extern "C" { + fn WCDBRustStatementInsert_create() -> *mut c_void; + + fn WCDBRustStatementInsert_configTableName(cpp_obj: *mut c_void, table_name: *const c_char); + + fn WCDBRustStatementInsert_configConflictAction(cpp_obj: *mut c_void, action: c_int); + + fn WCDBRustStatementInsert_configColumns( + cpp_obj: *mut c_void, + columns_type: c_int, + columns_void_vec: *const *mut c_void, + columns_string_vec: *const *const c_char, + columns_vec_len: c_int, + ); + + fn WCDBRustStatementInsert_configValuesWithBindParameters(cpp_obj: *mut c_void, count: c_int); + + fn WCDBRustStatementInsert_configDefaultValues(cpp_obj: *mut c_void); + + fn WCDBRustStatementInsert_configValues( + cpp_obj: *mut c_void, + types: *const c_int, + long_values: *const c_longlong, + double_values: *const c_double, + string_values: *const *const c_char, + value_len: c_int, + ); + + fn WCDBRustStatementInsert_configSelect(cpp_obj: *mut c_void, select: *mut c_void); + + fn WCDBRustStatementInsert_configUpsert(cpp_obj: *mut c_void, upsert: *mut c_void); +} + +#[derive(Debug)] +pub struct StatementInsert { + statement: Statement, +} + +impl CppObjectTrait for StatementInsert { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementInsert { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementInsert { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementInsert { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementInsert { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementInsert { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementInsert_create() }; + StatementInsert { + statement: Statement::new(CPPType::InsertSTMT, Some(cpp_obj)), + } + } + + pub fn insert_into(&self, table_name: &str) -> &Self { + unsafe { + WCDBRustStatementInsert_configTableName( + self.get_cpp_obj(), + table_name.to_cstring().as_ptr(), + ); + } + self + } + + pub fn or_replace(&self) -> &Self { + unsafe { + WCDBRustStatementInsert_configConflictAction( + self.get_cpp_obj(), + ConflictAction::Replace as i32, + ); + } + self + } + + pub fn or_rollback(&self) -> &Self { + unsafe { + WCDBRustStatementInsert_configConflictAction( + self.get_cpp_obj(), + ConflictAction::Rollback as i32, + ); + } + self + } + + pub fn or_abort(&self) -> &Self { + unsafe { + WCDBRustStatementInsert_configConflictAction( + self.get_cpp_obj(), + ConflictAction::Abort as i32, + ); + } + self + } + + pub fn or_fail(&self) -> &Self { + unsafe { + WCDBRustStatementInsert_configConflictAction( + self.get_cpp_obj(), + ConflictAction::Fail as i32, + ); + } + self + } + + pub fn or_ignore(&self) -> &Self { + unsafe { + WCDBRustStatementInsert_configConflictAction( + self.get_cpp_obj(), + ConflictAction::Ignore as i32, + ); + } + self + } + + pub fn columns<'a, I, S>(&self, columns: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = columns.into_iter().map(Into::into).collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type = CPPType::String; + let mut cpp_str_vec = vec![]; + let mut cpp_obj_vec = vec![]; + let mut c_strings = Vec::new(); + for item in data_vec { + match item { + StringColumn::String(str) => { + let c_string = str.to_cstring(); + cpp_str_vec.push(c_string.as_ptr()); + c_strings.push(c_string); + } + StringColumn::Column(obj) => { + cpp_type = Identifier::get_cpp_type(obj.as_identifier()); + cpp_obj_vec.push(CppObject::get(obj)); + } + } + } + if !cpp_str_vec.is_empty() { + unsafe { + WCDBRustStatementInsert_configColumns( + self.get_cpp_obj(), + CPPType::String as c_int, + std::ptr::null_mut(), + cpp_str_vec.as_ptr(), + cpp_str_vec.len() as c_int, + ); + } + } else { + unsafe { + WCDBRustStatementInsert_configColumns( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj_vec.as_ptr(), + std::ptr::null(), + cpp_obj_vec.len() as c_int, + ); + } + } + self + } + + pub fn values_with_bind_parameters(&self, parameters_count: usize) -> &Self { + unsafe { + WCDBRustStatementInsert_configValuesWithBindParameters( + self.get_cpp_obj(), + parameters_count as i32, + ) + }; + self + } + + pub fn default_values(&self) -> &Self { + unsafe { + WCDBRustStatementInsert_configDefaultValues(self.get_cpp_obj()); + } + self + } + + pub fn values(&self, values: Option>) -> &Self { + let mut c_name_vec = vec![]; + match values { + None => return self, + Some(v) if v.is_empty() => return self, + Some(v) => { + let array = MultiTypeArray::new_with_objects(&v); + let mut c_vec: Vec<*const c_char> = Vec::new(); + let mut string_values_len: usize = 0; + match array.string_values { + None => {} + Some(val) => { + string_values_len = val.len(); + for x in val { + let c_name = CString::new(x).unwrap_or_default(); + c_vec.push(c_name.as_ptr()); + c_name_vec.push(c_name); + } + } + } + let value_len = array + .types + .len() + .max(array.long_values.len()) + .max(array.double_values.len()) + .max(string_values_len); + unsafe { + WCDBRustStatementInsert_configValues( + self.get_cpp_obj(), + array.types.as_ptr(), + array.long_values.as_ptr(), + array.double_values.as_ptr(), + c_vec.as_ptr(), + value_len as c_int, + ); + } + } + } + self + } + + pub fn values_statement_select(&self, statement: &StatementSelect) -> &Self { + unsafe { + WCDBRustStatementInsert_configSelect(self.get_cpp_obj(), CppObject::get(statement)); + } + self + } + + pub fn upsert(&self, upsert: &Upsert) -> &Self { + unsafe { + WCDBRustStatementInsert_configUpsert(self.get_cpp_obj(), CppObject::get(upsert)); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_pragma.rs b/src/rust/wcdb/src/winq/statement_pragma.rs new file mode 100644 index 000000000..19c072ac2 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_pragma.rs @@ -0,0 +1,116 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::pragma::Pragma; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_double, c_int, c_longlong, c_void}; + +extern "C" { + fn WCDBRustStatementPragma_create() -> *mut c_void; + + fn WCDBRustStatementPragma_configPragma( + cpp_obj: *mut c_void, + pragma: *mut c_void, + ) -> *mut c_void; + + fn WCDBRustStatementPragma_configToValue( + cpp_obj: *mut c_void, + val_type: c_int, + long_value: c_longlong, + double_value: c_double, + string_value: *const c_char, + ); +} + +pub struct StatementPragma { + statement: Statement, +} + +impl CppObjectTrait for StatementPragma { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object() + } +} + +impl CppObjectConvertibleTrait for StatementPragma { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementPragma { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementPragma { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementPragma { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementPragma { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementPragma_create() }; + StatementPragma { + statement: Statement::new(CPPType::PragmaSTMT, Some(cpp_obj)), + } + } + + pub fn pragma(&self, pragma: Pragma) -> &StatementPragma { + unsafe { + WCDBRustStatementPragma_configPragma( + self.statement.get_cpp_obj(), + CppObject::get(&pragma), + ) + }; + self + } + + pub fn to_value(&self, value: i32) -> &StatementPragma { + unsafe { + WCDBRustStatementPragma_configToValue( + self.statement.get_cpp_obj(), + CPPType::Int as c_int, + value as c_longlong, + 0 as c_double, + std::ptr::null(), + ); + } + self + } + + pub fn to_value_bool(&self, value: bool) -> &StatementPragma { + let value = if value { 1 } else { 0 } as i64; + unsafe { + WCDBRustStatementPragma_configToValue( + self.statement.get_cpp_obj(), + CPPType::Bool as c_int, + value, + 0 as c_double, + std::ptr::null(), + ); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_reindex.rs b/src/rust/wcdb/src/winq/statement_reindex.rs new file mode 100644 index 000000000..6eb06d12d --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_reindex.rs @@ -0,0 +1,125 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_schema::StringSchema; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementReindex_createCppObj() -> *mut c_void; + + fn WCDBRustStatementReindex_configCollation(cpp_obj: *mut c_void, collation: *const c_char); + + fn WCDBRustStatementReindex_configTable(cpp_obj: *mut c_void, table: *const c_char); + + fn WCDBRustStatementReindex_configIndex(cpp_obj: *mut c_void, index_name: *const c_char); + + fn WCDBRustStatementReindex_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + path: *const c_char, + ); +} + +#[derive(Debug)] +pub struct StatementReindex { + statement: Statement, +} + +impl CppObjectTrait for StatementReindex { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementReindex { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementReindex { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementReindex { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementReindex { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementReindex { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementReindex_createCppObj() }; + StatementReindex { + statement: Statement::new(CPPType::ReindexSTMT, Some(cpp_obj)), + } + } + + pub fn reindex_collation(&self, collation: &str) -> &Self { + let c_str = collation.to_string().to_cstring(); + unsafe { + WCDBRustStatementReindex_configCollation(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } + + pub fn reindex_table(&self, table: &str) -> &Self { + let c_str = table.to_string().to_cstring(); + unsafe { + WCDBRustStatementReindex_configTable(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } + + pub fn reindex(&self, index: &str) -> &Self { + let c_str = index.to_string().to_cstring(); + unsafe { + WCDBRustStatementReindex_configIndex(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } + + pub fn of<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementReindex_configSchema( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ) + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_release.rs b/src/rust/wcdb/src/winq/statement_release.rs new file mode 100644 index 000000000..19a08ff79 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_release.rs @@ -0,0 +1,77 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_void}; + +extern "C" { + fn WCDBRustStatementRelease_createCppObj() -> *mut c_void; + + fn WCDBRustStatementRelease_configSavepoint(cpp_obj: *mut c_void, savepoint: *const c_char); +} + +#[derive(Debug)] +pub struct StatementRelease { + statement: Statement, +} + +impl CppObjectTrait for StatementRelease { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementRelease { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementRelease { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementRelease { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementRelease { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementRelease { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementRelease_createCppObj() }; + StatementRelease { + statement: Statement::new(CPPType::ReleaseSTMT, Some(cpp_obj)), + } + } + + pub fn release(&self, savepoint: &str) -> &Self { + let c_str = savepoint.to_string().to_cstring(); + unsafe { + WCDBRustStatementRelease_configSavepoint(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_rollback.rs b/src/rust/wcdb/src/winq/statement_rollback.rs new file mode 100644 index 000000000..f682df875 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_rollback.rs @@ -0,0 +1,77 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_void}; + +extern "C" { + fn WCDBRustStatementRollback_createCppObj() -> *mut c_void; + + fn WCDBRustStatementRollback_configSavepoint(cpp_obj: *mut c_void, savepoint: *const c_char); +} + +#[derive(Debug)] +pub struct StatementRollback { + statement: Statement, +} + +impl CppObjectTrait for StatementRollback { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementRollback { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementRollback { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementRollback { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementRollback { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementRollback { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementRollback_createCppObj() }; + StatementRollback { + statement: Statement::new(CPPType::RollbackSTMT, Some(cpp_obj)), + } + } + + pub fn rollback_to(&self, savepoint: &str) -> &Self { + let c_str = savepoint.to_string().to_cstring(); + unsafe { + WCDBRustStatementRollback_configSavepoint(self.get_cpp_obj(), c_str.as_ptr()); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_select.rs b/src/rust/wcdb/src/winq/statement_select.rs new file mode 100644 index 000000000..e71ffc8d3 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_select.rs @@ -0,0 +1,305 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_expression::StringExpression; +use crate::base::param::enum_string_result_column::StringResultColumn; +use crate::base::param::enum_string_table_or_subquery::StringTableOrSubquery; +use crate::utils::ToCString; +use crate::winq::expression::Expression; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::ordering_term::OrderingTerm; +use crate::winq::statement::{Statement, StatementTrait}; +use crate::winq::table_or_subquery_convertible_trait::TableOrSubqueryConvertibleTrait; +use core::ffi::c_size_t; +use std::ffi::{c_char, c_double, c_int, c_longlong, c_void}; +use std::fmt::Debug; + +extern "C" { + fn WCDBRustStatementSelect_create() -> *mut c_void; + + fn WCDBRustStatementSelect_configResultColumns( + cpp_obj: *mut c_void, + type_vec: *const c_int, + column_vec: *const c_longlong, + unused_vec: *const c_double, + column_name_vec: *const *const c_char, + vec_len: c_size_t, + ); + + fn WCDBRustStatementSelect_configTableOrSubqueries( + cpp_obj: *mut c_void, + type_vec: *const c_int, + long_vec: *const c_longlong, + unused_vec: *const c_double, + table_name_vec: *const *const c_char, + vec_len: c_size_t, + ); + + fn WCDBRustStatementSelect_configCondition(cpp_obj: *mut c_void, condition: *mut c_void); + + fn WCDBRustStatementSelect_configOrders( + cpp_obj: *mut c_void, + order_vec: *const c_longlong, + orders_length: c_size_t, + ); + + fn WCDBRustStatementSelect_configGroups( + cpp_obj: *mut c_void, + type_vec: *const c_int, + exps_vec: *const c_longlong, + unused_vec: *const c_double, + colum_name_vec: *const *const c_char, + length: c_size_t, + ); + + fn WCDBRustStatementSelect_configUnion(cpp_obj: *mut c_void); + + fn WCDBRustStatementSelect_configUnionAll(cpp_obj: *mut c_void); + + fn WCDBRustStatementSelect_configLimitCount( + cpp_obj: *mut c_void, + cpp_type: c_int, + count: c_longlong, + ); + + fn WCDBRustStatementSelect_configOffset( + cpp_obj: *mut c_void, + cpp_type: c_int, + count: c_longlong, + ); +} + +#[derive(Debug)] +pub struct StatementSelect { + statement: Statement, +} + +impl CppObjectTrait for StatementSelect { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementSelect { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementSelect { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementSelect { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl TableOrSubqueryConvertibleTrait for StatementSelect {} + +impl StatementTrait for StatementSelect { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementSelect { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementSelect_create() }; + StatementSelect { + statement: Statement::new(CPPType::SelectSTMT, Some(cpp_obj)), + } + } + + pub fn select<'a, I, S>(&self, column_vec: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = column_vec.into_iter().map(Into::into).collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type_vec = vec![]; + let mut c_strings: Vec = vec![]; + let mut cpp_str_ptrs: Vec<*const c_char> = vec![]; + let mut cpp_obj_vec = vec![]; + for item in data_vec { + match item { + StringResultColumn::String(str) => { + cpp_type_vec.push(CPPType::String as c_int); + let c = str.as_str().to_cstring(); + cpp_str_ptrs.push(c.as_ptr()); + c_strings.push(c); + } + StringResultColumn::ResultColumn(obj) => { + cpp_type_vec.push(Identifier::get_cpp_type(obj) as c_int); + cpp_obj_vec.push(CppObject::get(obj) as c_longlong); + } + } + } + unsafe { + WCDBRustStatementSelect_configResultColumns( + self.get_cpp_obj(), + cpp_type_vec.as_ptr(), + cpp_obj_vec.as_ptr(), + std::ptr::null(), + cpp_str_ptrs.as_ptr(), + cpp_type_vec.len(), + ); + } + self + } + + pub fn from<'a, I, S>(&self, table_arg_vec: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = table_arg_vec + .into_iter() + .map(Into::into) + .collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type_vec = vec![]; + let mut c_strings: Vec = vec![]; + let mut cpp_str_ptrs: Vec<*const c_char> = vec![]; + let mut cpp_obj_vec = vec![]; + + for item in data_vec { + match item { + StringTableOrSubquery::String(str) => { + cpp_type_vec.push(CPPType::String as c_int); + let c = str.as_str().to_cstring(); + cpp_str_ptrs.push(c.as_ptr()); + c_strings.push(c); + } + StringTableOrSubquery::TableOrSubquery(obj) => { + cpp_type_vec.push(Identifier::get_cpp_type(obj.as_identifier()) as c_int); + cpp_obj_vec.push(CppObject::get(obj) as c_longlong); + } + } + } + unsafe { + WCDBRustStatementSelect_configTableOrSubqueries( + self.get_cpp_obj(), + cpp_type_vec.as_ptr(), + cpp_obj_vec.as_ptr(), + std::ptr::null(), + cpp_str_ptrs.as_ptr(), + cpp_type_vec.len(), + ); + } + self + } + + pub fn where_(&self, condition: &Expression) -> &Self { + unsafe { + WCDBRustStatementSelect_configCondition(self.get_cpp_obj(), CppObject::get(condition)); + } + self + } + + pub fn group_by<'a, I, S>(&self, column_vec: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = column_vec.into_iter().map(Into::into).collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type_vec = vec![]; + let mut cpp_obj_vec = vec![]; + let mut cpp_str_ptrs: Vec<*const c_char> = vec![]; + let mut c_strings = vec![]; + for item in data_vec { + let (cpp_type, cpp_obj, c_str_opt) = item.get_params(); + cpp_type_vec.push(cpp_type as c_int); + cpp_obj_vec.push(cpp_obj as c_longlong); + if let Some(c) = c_str_opt { + cpp_str_ptrs.push(c.as_ptr()); + c_strings.push(c); + } + } + unsafe { + WCDBRustStatementSelect_configGroups( + self.get_cpp_obj(), + cpp_type_vec.as_ptr(), + cpp_obj_vec.as_ptr(), + std::ptr::null(), + cpp_str_ptrs.as_ptr(), + cpp_type_vec.len(), + ); + } + self + } + + pub fn union(&self) -> &Self { + unsafe { + WCDBRustStatementSelect_configUnion(self.get_cpp_obj()); + } + self + } + + pub fn union_all(&self) -> &Self { + unsafe { + WCDBRustStatementSelect_configUnion(self.get_cpp_obj()); + } + self + } + + pub fn order_by(&self, order_vec: Vec<&OrderingTerm>) -> &Self { + if order_vec.is_empty() { + return self; + } + let mut cpp_orders = vec![]; + for order in order_vec { + cpp_orders.push(order.get_cpp_obj() as c_longlong); + } + let orders_length = cpp_orders.len(); + unsafe { + WCDBRustStatementSelect_configOrders( + self.get_cpp_obj(), + cpp_orders.as_ptr(), + orders_length, + ) + } + self + } + + pub fn limit(&self, count: i64) -> &Self { + unsafe { + WCDBRustStatementSelect_configLimitCount( + self.get_cpp_obj(), + CPPType::Int as c_int, + count, + ) + } + self + } + + pub fn offset(&self, offset: i64) -> &Self { + unsafe { + WCDBRustStatementSelect_configOffset(self.get_cpp_obj(), CPPType::Int as c_int, offset) + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_update.rs b/src/rust/wcdb/src/winq/statement_update.rs new file mode 100644 index 000000000..07d4d6512 --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_update.rs @@ -0,0 +1,465 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_basic_expression::BasicExpression; +use crate::base::param::enum_int_expression::IntExpression; +use crate::base::param::enum_string_column::StringColumn; +use crate::base::param::enum_string_qualified_table::StringQualifiedTable; +use crate::utils::ToCString; +use crate::winq::common_table_expression::CommonTableExpression; +use crate::winq::conflict_action::ConflictAction; +use crate::winq::expression::Expression; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::ordering_term::OrderingTerm; +use crate::winq::statement::{Statement, StatementTrait}; +use core::ffi::c_size_t; +use std::ffi::{c_char, c_double, c_int, c_longlong, c_void}; +use std::fmt::Debug; +use std::ptr::{null, null_mut}; + +extern "C" { + fn WCDBRustStatementUpdate_create() -> *mut c_void; + + fn WCDBRustStatementUpdate_configWith( + cpp_obj: *mut c_void, + expressions: *const *mut c_void, + expressions_length: c_int, + ); + + fn WCDBRustStatementUpdate_configRecursive(cpp_obj: *mut c_void); + + fn WCDBRustStatementUpdate_configTable( + cpp_obj: *mut c_void, + type_i: c_int, + table: *mut c_void, + table_name: *const c_char, + ); + + fn WCDBRustStatementUpdate_configColumnsToBindParameters( + cpp_obj: *mut c_void, + columns_type: c_int, + columns_void_vec: *const *mut c_void, + columns_string_vec: *const *const c_char, + columns_vec_len: c_size_t, + ); + + fn WCDBRustStatementUpdate_configCondition(cpp_obj: *mut c_void, condition: *mut c_void); + + fn WCDBRustStatementUpdate_configOrders( + cpp_obj: *mut c_void, + orders: *const *mut c_void, + vec_len: c_size_t, + ); + fn WCDBRustStatementUpdate_configLimitCount( + cpp_obj: *mut c_void, + config_type: c_int, + limit: i64, + ); + + fn WCDBRustStatementUpdate_configOffset( + cpp_obj: *mut c_void, + config_type: c_int, + offset: *mut c_void, + ); + + fn WCDBRustStatementUpdate_configConfliction(cpp_obj: *mut c_void, action: c_int); + + fn WCDBRustStatementUpdate_configValue( + cpp_obj: *mut c_void, + cpp_type: c_int, + // todo denxudong 补充 *mut c_void + // arg_cpp_obj: *mut c_void, + int_value: c_longlong, + double_value: c_double, + string_value: *const c_char, + ); + + fn WCDBRustStatementUpdate_configColumns( + cpp_obj: *mut c_void, + cpp_type: c_int, + columns: *const *mut c_void, + column_names: *const *const c_char, + vec_len: c_size_t, + ); + + fn WCDBRustStatementUpdate_configLimitRange( + cpp_obj: *mut c_void, + from_type: c_int, + from: c_longlong, + to_type: c_int, + to: c_longlong, + ); +} + +#[derive(Debug)] +pub struct StatementUpdate { + statement: Statement, +} + +impl CppObjectTrait for StatementUpdate { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementUpdate { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementUpdate { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementUpdate { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementUpdate { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementUpdate { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementUpdate_create() }; + StatementUpdate { + statement: Statement::new(CPPType::UpdateSTMT, Some(cpp_obj)), + } + } + + pub fn with(&self, expressions: &Vec) -> &Self { + if expressions.is_empty() { + return self; + } + let mut cpp_obj_vec: Vec<*mut c_void> = Vec::with_capacity(expressions.len()); + for x in expressions { + cpp_obj_vec.push(CppObject::get(x)); + } + unsafe { + WCDBRustStatementUpdate_configWith( + self.get_cpp_obj(), + cpp_obj_vec.as_ptr(), + cpp_obj_vec.len() as c_int, + ); + } + self + } + + pub fn with_recursive(&self, expressions: &Vec) -> &Self { + if expressions.is_empty() { + return self; + } + let mut cpp_obj_vec: Vec<*mut c_void> = Vec::with_capacity(expressions.len()); + for x in expressions { + cpp_obj_vec.push(CppObject::get(x)); + } + unsafe { + WCDBRustStatementUpdate_configWith( + self.get_cpp_obj(), + cpp_obj_vec.as_ptr(), + cpp_obj_vec.len() as c_int, + ); + } + unsafe { WCDBRustStatementUpdate_configRecursive(self.get_cpp_obj()) } + self + } + + pub fn update<'a, S>(&self, table_vec: S) -> &Self + where + S: Into>, + { + let value = table_vec.into(); + let mut c_string_opt = None; // 持有 CString ,避免被提前释放 + let (cpp_type, table, table_name) = match value { + StringQualifiedTable::String(str) => { + let table_name = str.as_str().to_cstring(); + let c_ptr = table_name.as_ptr(); + c_string_opt = Some(table_name); + (CPPType::String, null_mut(), c_ptr) + } + StringQualifiedTable::QualifiedTable(obj) => { + let cpp_type = Identifier::get_cpp_type(obj.as_identifier()); + (cpp_type, CppObject::get(obj), null()) + } + }; + unsafe { + WCDBRustStatementUpdate_configTable( + self.get_cpp_obj(), + cpp_type as c_int, + table, + table_name, + ); + } + self + } + + pub fn or_replace(&self) -> &Self { + unsafe { + WCDBRustStatementUpdate_configConfliction( + self.get_cpp_obj(), + ConflictAction::Replace as c_int, + ) + } + self + } + + pub fn or_rollback(&self) -> &Self { + unsafe { + WCDBRustStatementUpdate_configConfliction( + self.get_cpp_obj(), + ConflictAction::Rollback as c_int, + ) + } + self + } + + pub fn or_abort(&self) -> &Self { + unsafe { + WCDBRustStatementUpdate_configConfliction( + self.get_cpp_obj(), + ConflictAction::Abort as c_int, + ) + } + self + } + + pub fn or_fail(&self) -> &Self { + unsafe { + WCDBRustStatementUpdate_configConfliction( + self.get_cpp_obj(), + ConflictAction::Fail as c_int, + ) + } + self + } + + pub fn or_ignore(&self) -> &Self { + unsafe { + WCDBRustStatementUpdate_configConfliction( + self.get_cpp_obj(), + ConflictAction::Ignore as c_int, + ) + } + self + } + + pub fn set_columns_to_bind_parameters<'a, I, S>(&self, column_vec: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = column_vec.into_iter().map(Into::into).collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type = CPPType::String; + let mut cpp_str_vec = vec![]; + let mut cpp_obj_vec = vec![]; + let mut c_strings = vec![]; + for item in data_vec { + match item { + StringColumn::String(str) => { + let c_string = str.to_cstring(); + cpp_str_vec.push(c_string.as_ptr()); + c_strings.push(c_string); + } + StringColumn::Column(obj) => { + cpp_type = Identifier::get_cpp_type(obj.as_identifier()); + cpp_obj_vec.push(CppObject::get(obj)); + } + } + } + if !cpp_str_vec.is_empty() { + unsafe { + WCDBRustStatementUpdate_configColumnsToBindParameters( + self.get_cpp_obj(), + CPPType::String as i32, + null(), + cpp_str_vec.as_ptr(), + cpp_str_vec.len(), + ); + } + } else { + unsafe { + WCDBRustStatementUpdate_configColumnsToBindParameters( + self.get_cpp_obj(), + CPPType::Column as c_int, + cpp_obj_vec.as_ptr(), + null(), + cpp_obj_vec.len(), + ); + } + } + self + } + + // todo dengxudong 重要不紧急 + // public StatementUpdate setColumnsToValues(@NotNull Column[] columns, @NotNull Object[] values) + + pub fn set<'a, I, S>(&self, column_vec: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = column_vec.into_iter().map(Into::into).collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type = CPPType::String; + let mut cpp_str_vec = vec![]; + let mut cpp_obj_vec = vec![]; + let mut c_strings = vec![]; + for item in data_vec { + match item { + StringColumn::String(str) => { + let c_string = str.to_cstring(); + cpp_str_vec.push(c_string.as_ptr()); + c_strings.push(c_string); + } + StringColumn::Column(obj) => { + cpp_type = Identifier::get_cpp_type(obj.as_identifier()); + cpp_obj_vec.push(CppObject::get(obj)); + } + } + } + if !cpp_str_vec.is_empty() { + unsafe { + WCDBRustStatementUpdate_configColumns( + self.get_cpp_obj(), + CPPType::String as c_int, + null(), + cpp_str_vec.as_ptr(), + cpp_str_vec.len(), + ) + } + } else { + unsafe { + WCDBRustStatementUpdate_configColumns( + self.get_cpp_obj(), + CPPType::Column as i32, + cpp_obj_vec.as_ptr(), + null(), + cpp_obj_vec.len(), + ); + } + } + self + } + + pub fn to<'a, V>(&self, value: V) -> &Self + where + V: Into>, + { + let (cpp_type, int_value, double_value, string_value_opt) = value.into().get_params(); + let string_ptr = match string_value_opt.as_ref() { + Some(s) => s.as_ptr(), + None => null(), + }; + unsafe { + WCDBRustStatementUpdate_configValue( + self.get_cpp_obj(), + cpp_type as c_int, + int_value, + double_value, + string_ptr, + ) + } + self + } + + pub fn where_(&self, condition: &Expression) -> &Self { + unsafe { + WCDBRustStatementUpdate_configCondition(self.get_cpp_obj(), CppObject::get(condition)); + } + self + } + + pub fn order_by(&self, orders: Vec<&OrderingTerm>) -> &Self { + if orders.is_empty() { + return self; + } + let mut order_raw_vec = Vec::with_capacity(orders.len()); + for order in orders { + order_raw_vec.push(order.get_cpp_obj()); + } + unsafe { + WCDBRustStatementUpdate_configOrders( + self.get_cpp_obj(), + order_raw_vec.as_ptr(), + order_raw_vec.len(), + ); + } + self + } + + pub fn limit<'a, T>(&self, count: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_value) = count.into().get_params(); + unsafe { + WCDBRustStatementUpdate_configLimitCount( + self.get_cpp_obj(), + cpp_type as i32, + cpp_value, + ); + } + self + } + + pub fn limit_range<'a, V, T>(&self, from: V, to: T) -> &Self + where + V: Into>, + T: Into>, + { + let (from_cpp_type, from_cpp_value) = from.into().get_params(); + let (to_cpp_type, to_cpp_value) = to.into().get_params(); + unsafe { + WCDBRustStatementUpdate_configLimitRange( + self.get_cpp_obj(), + from_cpp_type as c_int, + from_cpp_value, + to_cpp_type as c_int, + to_cpp_value as c_longlong, + ) + } + self + } + + pub fn offset<'a, V>(&self, offset: V) -> &Self + where + V: Into>, + { + let offset = offset.into(); + let (config_type, offset) = match offset { + IntExpression::Int(value) => (CPPType::Int, value as *mut c_void), + IntExpression::ExpressionConvertible(value_opt) => match value_opt { + None => (CPPType::Null, 0 as *mut c_void), + Some(value) => unsafe { (Identifier::get_cpp_type(value), CppObject::get(value)) }, + }, + }; + unsafe { + WCDBRustStatementUpdate_configOffset(self.get_cpp_obj(), config_type as c_int, offset); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/statement_vacuum.rs b/src/rust/wcdb/src/winq/statement_vacuum.rs new file mode 100644 index 000000000..ce1df2abc --- /dev/null +++ b/src/rust/wcdb/src/winq/statement_vacuum.rs @@ -0,0 +1,94 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_schema::StringSchema; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::statement::{Statement, StatementTrait}; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustStatementVacuum_createCppObj() -> *mut c_void; + + fn WCDBRustStatementVacuum_configSchema( + cpp_obj: *mut c_void, + cpp_type: c_int, + object: *mut c_void, + path: *const c_char, + ); +} + +#[derive(Debug)] +pub struct StatementVacuum { + statement: Statement, +} + +impl CppObjectTrait for StatementVacuum { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.statement.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.statement.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.statement.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for StatementVacuum { + fn as_cpp_object(&self) -> &CppObject { + self.statement.as_cpp_object() + } +} + +impl IdentifierTrait for StatementVacuum { + fn get_type(&self) -> CPPType { + self.statement.get_type() + } + + fn get_description(&self) -> String { + self.statement.get_description() + } +} + +impl IdentifierConvertibleTrait for StatementVacuum { + fn as_identifier(&self) -> &Identifier { + self.statement.as_identifier() + } +} + +impl StatementTrait for StatementVacuum { + fn is_write_statement(&self) -> bool { + self.statement.is_write_statement() + } +} + +impl StatementVacuum { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustStatementVacuum_createCppObj() }; + StatementVacuum { + statement: Statement::new(CPPType::VacuumSTMT, Some(cpp_obj)), + } + } + + pub fn vacuum<'a, T>(&self, schema: T) -> &Self + where + T: Into>, + { + let (cpp_type, cpp_obj, name_opt) = schema.into().get_params(); + let name_ptr = name_opt + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(std::ptr::null()); + unsafe { + WCDBRustStatementVacuum_configSchema( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj, + name_ptr, + ); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/table_constraint.rs b/src/rust/wcdb/src/winq/table_constraint.rs new file mode 100644 index 000000000..4cca66518 --- /dev/null +++ b/src/rust/wcdb/src/winq/table_constraint.rs @@ -0,0 +1,140 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_indexed_column::StringIndexedColumn; +use crate::utils::ToCString; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use core::ffi::c_size_t; +use std::ffi::{c_char, c_int, c_void}; + +extern "C" { + fn WCDBRustTableConstraint_create(name: *const c_char) -> *mut c_void; + + fn WCDBRustTableConstraint_configPrimaryKey(cpp_obj: *mut c_void); + + fn WCDBRustTableConstraint_configUnique(cpp_obj: *mut c_void); + + fn WCDBRustTableConstraint_configIndexedColumn( + cpp_obj: *mut c_void, + columns_type: c_int, + column_vec: *const *mut c_void, + column_name_vec: *const *const c_char, + column_vec_len: c_size_t, + ); +} + +pub struct TableConstraint { + identifier: Identifier, +} + +impl CppObjectTrait for TableConstraint { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for TableConstraint { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for TableConstraint { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for TableConstraint { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl TableConstraint { + pub fn new(name_opt: Option<&str>) -> Self { + let cpp_obj = match name_opt { + Some(name) => unsafe { WCDBRustTableConstraint_create(name.to_cstring().as_ptr()) }, + None => unsafe { WCDBRustTableConstraint_create(std::ptr::null_mut()) }, + }; + Self { + identifier: Identifier::new(CPPType::TableConstraint, Some(cpp_obj)), + } + } + + pub fn primary_key(&self) -> &Self { + unsafe { + WCDBRustTableConstraint_configPrimaryKey(self.get_cpp_obj()); + } + self + } + + pub fn unique(&self) -> &Self { + unsafe { + WCDBRustTableConstraint_configUnique(self.get_cpp_obj()); + } + self + } + + pub fn indexed_by<'a, I, S>(&self, column_vec: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = column_vec.into_iter().map(Into::into).collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type = CPPType::String; + let mut cpp_str_vec = vec![]; + let mut cpp_obj_vec = vec![]; + let mut c_strings = Vec::new(); + for item in data_vec { + match item { + StringIndexedColumn::String(str) => { + let c_string = str.to_cstring(); + cpp_str_vec.push(c_string.as_ptr()); + c_strings.push(c_string); + } + StringIndexedColumn::IndexedColumnConvertible(obj) => { + cpp_type = Identifier::get_cpp_type(obj.as_identifier()); + cpp_obj_vec.push(CppObject::get(obj)); + } + } + } + if !cpp_str_vec.is_empty() { + unsafe { + WCDBRustTableConstraint_configIndexedColumn( + self.get_cpp_obj(), + CPPType::String as c_int, + std::ptr::null(), + cpp_str_vec.as_ptr(), + cpp_str_vec.len(), + ); + } + } else { + unsafe { + WCDBRustTableConstraint_configIndexedColumn( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj_vec.as_ptr(), + std::ptr::null(), + cpp_obj_vec.len(), + ); + } + } + self + } +} diff --git a/src/rust/wcdb/src/winq/table_or_subquery_convertible_trait.rs b/src/rust/wcdb/src/winq/table_or_subquery_convertible_trait.rs new file mode 100644 index 000000000..a294df47a --- /dev/null +++ b/src/rust/wcdb/src/winq/table_or_subquery_convertible_trait.rs @@ -0,0 +1,3 @@ +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; + +pub trait TableOrSubqueryConvertibleTrait: IdentifierConvertibleTrait {} diff --git a/src/rust/wcdb/src/winq/upsert.rs b/src/rust/wcdb/src/winq/upsert.rs new file mode 100644 index 000000000..a35f71fbe --- /dev/null +++ b/src/rust/wcdb/src/winq/upsert.rs @@ -0,0 +1,239 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_basic_expression::BasicExpression; +use crate::base::param::enum_string_column::StringColumn; +use crate::base::param::enum_string_indexed_column::StringIndexedColumn; +use crate::utils::ToCString; +use crate::winq::expression::Expression; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use std::ffi::{c_char, c_double, c_int, c_longlong, c_void}; + +extern "C" { + fn WCDBRustUpsert_createCppObj() -> *mut c_void; + + fn WCDBRustUpsert_configIndexedColumn( + cpp_obj: *mut c_void, + cpp_obj_type: c_int, + columns: *const *mut c_void, + columns_string_vec: *const *const c_char, + vec_len: c_int, + ); + + fn WCDBRustUpsert_configWhere(cpp_obj: *mut c_void, condition: *mut c_void); + + fn WCDBRustUpsert_configDoNothing(cpp_obj: *mut c_void); + + fn WCDBRustUpsert_configDoUpdate(cpp_obj: *mut c_void); + + fn WCDBRustUpsert_configSetColumns( + cpp_obj: *mut c_void, + cpp_obj_type: c_int, + columns: *const *mut c_void, + columns_string_vec: *const *const c_char, + vec_len: c_int, + ); + + fn WCDBRustUpsert_configToValue( + cpp_obj: *mut c_void, + cpp_obj_type: c_int, + int_value: c_longlong, + double_value: c_double, + string_value: *const c_char, + ); +} + +pub struct Upsert { + identifier: Identifier, +} + +impl CppObjectTrait for Upsert { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj) + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object() + } +} + +impl CppObjectConvertibleTrait for Upsert { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for Upsert { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for Upsert { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl Upsert { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustUpsert_createCppObj() }; + Upsert { + identifier: Identifier::new(CPPType::UpsertClause, Some(cpp_obj)), + } + } + + pub fn on_conflict(&self) -> &Self { + self + } + + pub fn indexed_by<'a, I, S>(&self, column_vec: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = column_vec.into_iter().map(Into::into).collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type = CPPType::String; + let mut cpp_str_vec = vec![]; + let mut cpp_obj_vec = vec![]; + let mut c_strings = Vec::new(); + for item in data_vec { + match item { + StringIndexedColumn::String(str) => { + let c_string = str.to_cstring(); + cpp_str_vec.push(c_string.as_ptr()); + c_strings.push(c_string); + } + StringIndexedColumn::IndexedColumnConvertible(obj) => { + cpp_type = Identifier::get_cpp_type(obj.as_identifier()); + cpp_obj_vec.push(CppObject::get(obj)); + } + } + } + if !cpp_str_vec.is_empty() { + unsafe { + WCDBRustUpsert_configIndexedColumn( + self.get_cpp_obj(), + CPPType::String as c_int, + std::ptr::null_mut(), + cpp_str_vec.as_ptr(), + cpp_str_vec.len() as c_int, + ); + } + } else { + unsafe { + WCDBRustUpsert_configIndexedColumn( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj_vec.as_ptr(), + std::ptr::null(), + cpp_obj_vec.len() as c_int, + ); + } + } + self + } + + pub fn where_(&self, condition: &Expression) -> &Self { + unsafe { + WCDBRustUpsert_configWhere(self.get_cpp_obj(), CppObject::get(condition)); + } + self + } + + pub fn do_no_thing(&self) -> &Self { + unsafe { + WCDBRustUpsert_configDoNothing(self.get_cpp_obj()); + } + self + } + + pub fn do_update(&self) -> &Self { + unsafe { + WCDBRustUpsert_configDoUpdate(self.get_cpp_obj()); + } + self + } + + pub fn set<'a, I, S>(&self, column_vec: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = column_vec.into_iter().map(Into::into).collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type = CPPType::String; + let mut cpp_str_vec = vec![]; + let mut cpp_obj_vec = vec![]; + let mut c_strings = vec![]; + for item in data_vec { + match item { + StringColumn::String(str) => { + let c_string = str.to_cstring(); + cpp_str_vec.push(c_string.as_ptr()); + c_strings.push(c_string); + } + StringColumn::Column(obj) => { + cpp_type = Identifier::get_cpp_type(obj.as_identifier()); + cpp_obj_vec.push(CppObject::get(obj)); + } + } + } + if !cpp_str_vec.is_empty() { + unsafe { + WCDBRustUpsert_configSetColumns( + self.get_cpp_obj(), + CPPType::String as c_int, + std::ptr::null_mut(), + cpp_str_vec.as_ptr(), + cpp_str_vec.len() as c_int, + ); + } + } else { + unsafe { + WCDBRustUpsert_configSetColumns( + self.get_cpp_obj(), + cpp_type as c_int, + cpp_obj_vec.as_ptr(), + std::ptr::null_mut(), + cpp_obj_vec.len() as c_int, + ) + } + } + self + } + + pub fn to<'a, V>(&self, value: V) -> &Self + where + V: Into>, + { + let (cpp_type, int_value, double_value, string_value_opt) = value.into().get_params(); + let string_ptr: *const c_char = match string_value_opt.as_ref() { + Some(s) => s.as_ptr(), + None => std::ptr::null(), + }; + unsafe { + WCDBRustUpsert_configToValue( + self.get_cpp_obj(), + cpp_type as c_int, + int_value, + double_value, + string_ptr, + ); + } + self + } +} diff --git a/src/rust/wcdb/src/winq/window_def.rs b/src/rust/wcdb/src/winq/window_def.rs new file mode 100644 index 000000000..a524f9c7d --- /dev/null +++ b/src/rust/wcdb/src/winq/window_def.rs @@ -0,0 +1,137 @@ +use crate::base::cpp_object::{CppObject, CppObjectTrait}; +use crate::base::cpp_object_convertible::CppObjectConvertibleTrait; +use crate::base::param::enum_string_expression::StringExpression; +use crate::winq::frame_spec::FrameSpec; +use crate::winq::identifier::{CPPType, Identifier, IdentifierTrait}; +use crate::winq::identifier_convertible::IdentifierConvertibleTrait; +use crate::winq::ordering_term::OrderingTerm; +use std::ffi::{c_char, c_double, c_int, c_void}; + +extern "C" { + fn WCDBRustWindowDef_createCppObj() -> *mut c_void; + + fn WCDBRustWindowDef_configPartitions( + cpp_obj: *mut c_void, + types: *const c_int, + columns: *const *mut c_void, + unused: *const *mut c_double, + column_names: *const *const c_char, + array_len: c_int, + ); + + fn WCDBRustWindowDef_configOrders( + cpp_obj: *mut c_void, + columns: *const *mut c_void, + total_length: c_int, + ); + + fn WCDBRustWindowDef_configFrameSpec(cpp_obj: *mut c_void, frame_spec: *mut c_void); +} + +#[derive(Debug)] +pub struct WindowDef { + identifier: Identifier, +} + +impl CppObjectTrait for WindowDef { + fn set_cpp_obj(&mut self, cpp_obj: *mut c_void) { + self.identifier.set_cpp_obj(cpp_obj); + } + + fn get_cpp_obj(&self) -> *mut c_void { + self.identifier.get_cpp_obj() + } + + fn release_cpp_object(&mut self) { + self.identifier.release_cpp_object(); + } +} + +impl CppObjectConvertibleTrait for WindowDef { + fn as_cpp_object(&self) -> &CppObject { + self.identifier.as_cpp_object() + } +} + +impl IdentifierTrait for WindowDef { + fn get_type(&self) -> CPPType { + self.identifier.get_type() + } + + fn get_description(&self) -> String { + self.identifier.get_description() + } +} + +impl IdentifierConvertibleTrait for WindowDef { + fn as_identifier(&self) -> &Identifier { + self.identifier.as_identifier() + } +} + +impl WindowDef { + pub fn new() -> Self { + let cpp_obj = unsafe { WCDBRustWindowDef_createCppObj() }; + WindowDef { + identifier: Identifier::new(CPPType::WindowDef, Some(cpp_obj)), + } + } + + pub fn partition_by<'a, I, S>(&self, column_names_or_expressions: I) -> &Self + where + I: IntoIterator, + S: Into>, + { + let data_vec = column_names_or_expressions + .into_iter() + .map(Into::into) + .collect::>(); + if data_vec.is_empty() { + return self; + } + let mut cpp_type_vec = vec![]; + let mut cpp_obj_vec = vec![]; + let mut cpp_str_ptrs: Vec<*const c_char> = vec![]; + let mut c_strings = vec![]; + for item in data_vec { + let (cpp_type, cpp_obj, c_str_opt) = item.get_params(); + cpp_type_vec.push(cpp_type as c_int); + cpp_obj_vec.push(cpp_obj); + if let Some(c) = c_str_opt { + cpp_str_ptrs.push(c.as_ptr()); + c_strings.push(c); + } + } + unsafe { + WCDBRustWindowDef_configPartitions( + self.get_cpp_obj(), + cpp_type_vec.as_ptr(), + cpp_obj_vec.as_ptr(), + std::ptr::null(), + cpp_str_ptrs.as_ptr(), + cpp_type_vec.len() as c_int, + ); + } + self + } + + pub fn order_by(&self, orders: &Vec<&OrderingTerm>) -> &Self { + if orders.is_empty() { + return self; + } + let size = orders.len(); + let mut cpp_objs: Vec<*mut c_void> = Vec::with_capacity(size); + for item in orders { + cpp_objs.push(item.get_cpp_obj()); + } + unsafe { + WCDBRustWindowDef_configOrders(self.get_cpp_obj(), cpp_objs.as_ptr(), size as c_int); + } + self + } + + pub fn frame_spec(&self, frame_spec: &FrameSpec) -> &Self { + unsafe { WCDBRustWindowDef_configFrameSpec(self.get_cpp_obj(), CppObject::get(frame_spec)) } + self + } +} diff --git a/src/rust/wcdb_derive/Cargo.toml b/src/rust/wcdb_derive/Cargo.toml new file mode 100644 index 000000000..42c18b703 --- /dev/null +++ b/src/rust/wcdb_derive/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "wcdb_derive" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "2.0.90", features = ["full", "extra-traits"] } +proc-macro2 = "1.0.92" +quote = "1.0.37" +darling = "0.20.10" +once_cell = "1.19.0" diff --git a/src/rust/wcdb_derive/src/compiler/mod.rs b/src/rust/wcdb_derive/src/compiler/mod.rs new file mode 100644 index 000000000..5c9f1c50f --- /dev/null +++ b/src/rust/wcdb_derive/src/compiler/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod resolved_info; +pub(crate) mod rust_code_generator; +pub(crate) mod rust_field_orm_info; diff --git a/src/rust/wcdb_derive/src/compiler/resolved_info/column_info.rs b/src/rust/wcdb_derive/src/compiler/resolved_info/column_info.rs new file mode 100644 index 000000000..f5bfd839e --- /dev/null +++ b/src/rust/wcdb_derive/src/compiler/resolved_info/column_info.rs @@ -0,0 +1,225 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use crate::compiler::resolved_info::default_value_info::DefaultValueInfo; +use crate::compiler::rust_field_orm_info::{RustFieldORMInfo, RUST_FIELD_ORM_INFO_MAP}; +use crate::macros::wcdb_field::WCDBField; + +#[derive(Clone, Debug)] +pub struct ColumnInfo { + property_name: String, + property_type: String, + nullable: bool, + column_name: String, + is_primary: bool, + is_auto_increment: bool, + enable_auto_increment_for_existing_table: bool, + default_value: Option, + is_unique: bool, + is_not_null: bool, + is_not_indexed: bool, + has_index: bool, + index_name: String, + index_is_unique: bool, +} + +impl ColumnInfo { + pub fn new() -> Self { + ColumnInfo { + property_name: "".to_string(), + property_type: "".to_string(), + nullable: false, + column_name: "".to_string(), + is_primary: false, + is_auto_increment: false, + enable_auto_increment_for_existing_table: false, + default_value: None, + is_unique: false, + is_not_null: false, + is_not_indexed: false, + has_index: false, + index_name: "".to_string(), + index_is_unique: false, + } + } + + /// ColumnInfo(propertyName='multiPrimary1', propertyType='int', nullable=false, columnName='', + /// isPrimary=false, isAutoIncrement=false, enableAutoIncrementForExistingTable=false, + /// defaultValue=null, isUnique=false, isNotNull=false, isNotIndexed=false, hasIndex=false, + /// indexName='', indexIsUnique=false) + pub fn resolve(field: &WCDBField) -> ColumnInfo { + let mut column_info = ColumnInfo::new(); + column_info.property_name = field + .ident() + .iter() + .map(|field_name| field_name.to_string()) + .collect::(); + column_info.property_type = + WCDBField::get_field_type_string(&field.ty()).unwrap_or(String::from("None")); + column_info.nullable = field.is_not_null(); + column_info.column_name = field.column_name(); + column_info.is_primary = field.is_primary(); + column_info.is_auto_increment = field.is_auto_increment(); + column_info.enable_auto_increment_for_existing_table = + field.enable_auto_increment_for_existing_table(); + column_info.is_unique = field.is_unique(); + column_info.is_not_null = field.is_not_null(); + column_info.is_not_indexed = field.is_not_indexed(); + column_info.default_value = DefaultValueInfo::resolve(field.default()); + + match field.index() { + None => { + column_info.has_index = false; + } + Some(index) => { + column_info.has_index = true; + column_info.index_name = index.name(); + column_info.index_is_unique = index.is_unique(); + } + } + + column_info + } + + pub fn property_name(&self) -> String { + self.property_name.clone() + } + + pub fn property_type(&self) -> String { + self.property_type.clone() + } + + pub fn nullable(&self) -> bool { + self.nullable + } + + pub fn column_name(&self) -> String { + self.column_name.clone() + } + + pub fn is_primary(&self) -> bool { + self.is_primary + } + + pub fn is_auto_increment(&self) -> bool { + self.is_auto_increment + } + + pub fn enable_auto_increment_for_existing_table(&self) -> bool { + self.enable_auto_increment_for_existing_table + } + + pub fn default_value(&self) -> &Option { + &self.default_value + } + + pub fn is_unique(&self) -> bool { + self.is_unique + } + + pub fn is_not_null(&self) -> bool { + self.is_not_null + } + + pub fn is_not_indexed(&self) -> bool { + self.is_not_indexed + } + + pub fn has_index(&self) -> bool { + self.has_index + } + + pub fn index_name(&self) -> String { + self.index_name.clone() + } + + pub fn index_is_unique(&self) -> bool { + self.index_is_unique + } + + pub fn get_field_orm_info(&self) -> &RustFieldORMInfo { + let property_type = self.property_type(); + let field_orm_info_opt = RUST_FIELD_ORM_INFO_MAP.get(property_type.as_str()); + assert!( + field_orm_info_opt.is_some(), + "filed not support {}", + property_type.as_str() + ); + field_orm_info_opt.unwrap() + } + + pub fn set_property_name(&mut self, property_name: String) { + self.property_name = property_name; + } + + pub fn set_property_type(&mut self, property_type: String) { + self.property_type = property_type; + } + + pub fn set_nullable(&mut self, nullable: bool) { + self.nullable = nullable; + } + + pub fn set_column_name(&mut self, column_name: String) { + self.column_name = column_name; + } + + pub fn set_is_primary(&mut self, is_primary: bool) { + self.is_primary = is_primary; + } + + pub fn set_is_auto_increment(&mut self, is_auto_increment: bool) { + self.is_auto_increment = is_auto_increment; + } + + pub fn set_enable_auto_increment_for_existing_table( + &mut self, + enable_auto_increment_for_existing_table: bool, + ) { + self.enable_auto_increment_for_existing_table = enable_auto_increment_for_existing_table; + } + + pub fn set_default_value(&mut self, default_value: Option) { + self.default_value = default_value; + } + + pub fn set_is_unique(&mut self, is_unique: bool) { + self.is_unique = is_unique; + } + + pub fn set_is_not_null(&mut self, is_not_null: bool) { + self.is_not_null = is_not_null; + } + + pub fn set_is_not_indexed(&mut self, is_not_indexed: bool) { + self.is_not_indexed = is_not_indexed; + } + + pub fn set_has_index(&mut self, has_index: bool) { + self.has_index = has_index; + } + + pub fn set_index_name(&mut self, index_name: String) { + self.index_name = index_name; + } + + pub fn set_index_is_unique(&mut self, index_is_unique: bool) { + self.index_is_unique = index_is_unique; + } +} diff --git a/src/rust/wcdb_derive/src/compiler/resolved_info/default_value_info.rs b/src/rust/wcdb_derive/src/compiler/resolved_info/default_value_info.rs new file mode 100644 index 000000000..51344ca50 --- /dev/null +++ b/src/rust/wcdb_derive/src/compiler/resolved_info/default_value_info.rs @@ -0,0 +1,64 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use crate::macros::wcdb_default::WCDBDefault; + +#[derive(Clone, Debug)] +pub struct DefaultValueInfo { + i32_value: i64, + f64_value: f64, + text_value: String, +} + +impl DefaultValueInfo { + pub fn new() -> Self { + DefaultValueInfo { + i32_value: 0, + f64_value: 0.0, + text_value: "".to_string(), + } + } + + pub fn i32_value(&self) -> i64 { + self.i32_value + } + + pub fn f64_value(&self) -> f64 { + self.f64_value + } + + pub fn text_value(&self) -> String { + self.text_value.clone() + } + + pub(crate) fn resolve(default_opt: &Option) -> Option { + match default_opt { + None => None, + Some(default) => Some(DefaultValueInfo::create(&default)), + } + } + + pub(crate) fn create(default_value: &WCDBDefault) -> DefaultValueInfo { + let mut info = DefaultValueInfo::new(); + info.i32_value = default_value.i32_value(); + info.f64_value = default_value.f64_value(); + info.text_value = default_value.text_value().to_string(); + info + } +} diff --git a/src/rust/wcdb_derive/src/compiler/resolved_info/fts_module_info.rs b/src/rust/wcdb_derive/src/compiler/resolved_info/fts_module_info.rs new file mode 100644 index 000000000..b65860b74 --- /dev/null +++ b/src/rust/wcdb_derive/src/compiler/resolved_info/fts_module_info.rs @@ -0,0 +1,68 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use crate::macros::fts_module::FTSModule; + +pub struct FTSModuleInfo { + fts_version: String, + tokenizer: String, + tokenizer_parameters: Vec, + external_table: String, +} + +impl FTSModuleInfo { + pub fn new() -> Self { + FTSModuleInfo { + fts_version: "".to_string(), + tokenizer: "".to_string(), + tokenizer_parameters: vec![], + external_table: "".to_string(), + } + } + + pub fn resolve(fts_module: &FTSModule) -> Self { + let tokenizer_parameters = fts_module + .tokenizer_parameters() + .iter() + .map(|s| s.value()) + .collect::>(); + Self { + fts_version: fts_module.version().to_string(), + tokenizer: fts_module.tokenizer().to_string(), + tokenizer_parameters, + external_table: fts_module.external_table().to_string(), + } + } + + pub fn fts_version(&self) -> String { + self.fts_version.clone() + } + + pub fn tokenizer(&self) -> String { + self.tokenizer.clone() + } + + pub fn tokenizer_parameters(&self) -> &Vec { + &self.tokenizer_parameters + } + + pub fn external_table(&self) -> String { + self.external_table.clone() + } +} diff --git a/src/rust/wcdb_derive/src/compiler/resolved_info/mod.rs b/src/rust/wcdb_derive/src/compiler/resolved_info/mod.rs new file mode 100644 index 000000000..98623953d --- /dev/null +++ b/src/rust/wcdb_derive/src/compiler/resolved_info/mod.rs @@ -0,0 +1,7 @@ +pub(crate) mod column_info; +pub(crate) mod default_value_info; +pub(crate) mod fts_module_info; +pub(crate) mod multi_indexes_info; +pub(crate) mod multi_primary_info; +pub(crate) mod multi_unique_info; +pub(crate) mod table_config_info; diff --git a/src/rust/wcdb_derive/src/compiler/resolved_info/multi_indexes_info.rs b/src/rust/wcdb_derive/src/compiler/resolved_info/multi_indexes_info.rs new file mode 100644 index 000000000..b7d2bc0cc --- /dev/null +++ b/src/rust/wcdb_derive/src/compiler/resolved_info/multi_indexes_info.rs @@ -0,0 +1,90 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use crate::compiler::resolved_info::column_info::ColumnInfo; +use crate::macros::multi_indexes::MultiIndexes; +use proc_macro2::{Ident, Span}; +use std::collections::HashMap; + +pub struct MultiIndexesInfo { + name: String, + columns: Vec, +} + +impl MultiIndexesInfo { + pub fn new() -> Self { + MultiIndexesInfo { + name: "".to_string(), + columns: vec![], + } + } + + pub fn get_index_name_ident(&self) -> Ident { + let mut index_name = self.name.clone(); + if self.name.is_empty() { + let join_str = self.columns().join("_"); + index_name = format!("{}{}{}", "_", join_str, "_index"); + } + Ident::new(&index_name, Span::call_site()) + } + + pub(crate) fn get_index_column_name_ident_vec( + &self, + all_columns_map: &HashMap, + ) -> Vec { + if self.columns.is_empty() { + return vec![]; + } + let mut ret_vec = vec![]; + for column in self.columns.iter() { + let mut property_name = column.clone(); + match all_columns_map.get(&property_name) { + None => {} + Some(val) => { + property_name = val.property_name(); + } + } + ret_vec.push(Ident::new(property_name.as_str(), Span::call_site())); + } + ret_vec + } + + pub fn name(&self) -> String { + self.name.clone() + } + + pub fn columns(&self) -> &Vec { + &self.columns + } + + pub fn resolve(multi_indexes: &MultiIndexes) -> MultiIndexesInfo { + let mut info = MultiIndexesInfo::new(); + match multi_indexes.name() { + None => {} + Some(val) => { + info.name = val.value(); + } + } + for lit_str in multi_indexes.columns() { + info.columns.push(lit_str.value()); + } + + info + } +} diff --git a/src/rust/wcdb_derive/src/compiler/resolved_info/multi_primary_info.rs b/src/rust/wcdb_derive/src/compiler/resolved_info/multi_primary_info.rs new file mode 100644 index 000000000..acc7d4afd --- /dev/null +++ b/src/rust/wcdb_derive/src/compiler/resolved_info/multi_primary_info.rs @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use crate::compiler::resolved_info::column_info::ColumnInfo; +use crate::macros::multi_primary::MultiPrimary; +use proc_macro2::{Ident, Span}; +use std::collections::HashMap; + +pub struct MultiPrimaryInfo { + columns: Vec, +} + +impl MultiPrimaryInfo { + pub fn new() -> Self { + MultiPrimaryInfo { columns: vec![] } + } + + pub fn resolve(multi_primary: &MultiPrimary) -> MultiPrimaryInfo { + let mut info = MultiPrimaryInfo::new(); + for x in multi_primary.columns() { + info.columns.push(x.value()); + } + info + } + + pub fn columns(&self) -> &Vec { + &self.columns + } + + pub fn columns_ident_vec(&self, all_columns_map: &HashMap) -> Vec { + let mut ident_vec: Vec = Vec::new(); + for column_item in self.columns.iter() { + let mut property_name = column_item.clone(); + match all_columns_map.get(column_item) { + None => {} + Some(val) => property_name = val.property_name(), + } + ident_vec.push(Ident::new(property_name.as_str(), Span::call_site())); + } + ident_vec + } +} diff --git a/src/rust/wcdb_derive/src/compiler/resolved_info/multi_unique_info.rs b/src/rust/wcdb_derive/src/compiler/resolved_info/multi_unique_info.rs new file mode 100644 index 000000000..6cf1d423f --- /dev/null +++ b/src/rust/wcdb_derive/src/compiler/resolved_info/multi_unique_info.rs @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making + * WCDB available. + * + * Copyright (C) 2017 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of + * the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use crate::compiler::resolved_info::column_info::ColumnInfo; +use crate::macros::multi_unique::MultiUnique; +use proc_macro2::{Ident, Span}; +use std::collections::HashMap; + +pub struct MultiUniqueInfo { + columns: Vec, +} + +impl MultiUniqueInfo { + pub fn new() -> Self { + MultiUniqueInfo { columns: vec![] } + } + + pub fn resolve(multi_unique: &MultiUnique) -> MultiUniqueInfo { + let mut info = MultiUniqueInfo::new(); + for x in multi_unique.columns() { + info.columns.push(x.value().to_string()); + } + info + } + + pub fn columns(&self) -> &Vec { + &self.columns + } + + pub fn columns_ident_vec(&self, all_columns_map: &HashMap) -> Vec { + let mut ident_vec: Vec = Vec::new(); + for column_item in self.columns.iter() { + let property_name = all_columns_map.get(column_item).unwrap().property_name(); + ident_vec.push(Ident::new(property_name.as_str(), Span::call_site())); + } + ident_vec + } +} diff --git a/src/rust/wcdb_derive/src/compiler/resolved_info/table_config_info.rs b/src/rust/wcdb_derive/src/compiler/resolved_info/table_config_info.rs new file mode 100644 index 000000000..66a3740f3 --- /dev/null +++ b/src/rust/wcdb_derive/src/compiler/resolved_info/table_config_info.rs @@ -0,0 +1,95 @@ +use crate::compiler::resolved_info::fts_module_info::FTSModuleInfo; +use crate::compiler::resolved_info::multi_indexes_info::MultiIndexesInfo; +use crate::compiler::resolved_info::multi_primary_info::MultiPrimaryInfo; +use crate::compiler::resolved_info::multi_unique_info::MultiUniqueInfo; +use crate::WCDBTable; + +pub struct TableConfigInfo { + multi_indexes: Option>, + multi_primaries: Option>, + multi_unique: Option>, + fts_module: Option, + is_without_row_id: bool, +} + +impl TableConfigInfo { + pub fn new() -> Self { + TableConfigInfo { + multi_indexes: None, + multi_primaries: None, + multi_unique: None, + fts_module: None, + is_without_row_id: false, + } + } + + pub fn resolve(table: &WCDBTable) -> TableConfigInfo { + let mut resolved_annotation = TableConfigInfo::new(); + resolved_annotation.is_without_row_id = table.is_without_row_id(); + for multi_indexes_item in table.multi_indexes() { + resolved_annotation + .multi_indexes + .get_or_insert(vec![]) + .push(MultiIndexesInfo::resolve(&multi_indexes_item)); + } + + for multi_primary in table.multi_primaries() { + resolved_annotation + .multi_primaries + .get_or_insert(vec![]) + .push(MultiPrimaryInfo::resolve(multi_primary)); + } + for multi_unique in table.multi_unique() { + resolved_annotation + .multi_unique + .get_or_insert(vec![]) + .push(MultiUniqueInfo::resolve(multi_unique)) + } + + if let Some(fts_module) = table.get_fts_module() { + resolved_annotation.fts_module = Some(FTSModuleInfo::resolve(fts_module)); + } + resolved_annotation + } + + pub fn multi_indexes_is_empty(&self) -> bool { + if let Some(multi_indexes) = &self.multi_indexes { + return multi_indexes.is_empty(); + } + true + } + + pub fn multi_indexes(&self) -> &Option> { + &self.multi_indexes + } + + pub fn multi_primaries_is_empty(&self) -> bool { + if let Some(multi_primaries) = &self.multi_primaries { + return multi_primaries.is_empty(); + } + true + } + + pub fn multi_primaries(&self) -> &Option> { + &self.multi_primaries + } + + pub fn multi_unique_is_empty(&self) -> bool { + if let Some(multi_unique) = &self.multi_unique { + return multi_unique.is_empty(); + } + true + } + + pub fn multi_unique(&self) -> &Option> { + &self.multi_unique + } + + pub fn fts_module(&self) -> &Option { + &self.fts_module + } + + pub fn is_without_row_id(&self) -> bool { + self.is_without_row_id + } +} diff --git a/src/rust/wcdb_derive/src/compiler/rust_code_generator.rs b/src/rust/wcdb_derive/src/compiler/rust_code_generator.rs new file mode 100644 index 000000000..69c6239a7 --- /dev/null +++ b/src/rust/wcdb_derive/src/compiler/rust_code_generator.rs @@ -0,0 +1,693 @@ +use crate::compiler::resolved_info::column_info::ColumnInfo; +use crate::compiler::resolved_info::table_config_info::TableConfigInfo; +use crate::macros::fts_version::FTSVersion; +use crate::macros::wcdb_table::WCDBTable; +use proc_macro2::{Ident, Span}; +use quote::quote; +use std::collections::HashMap; + +/// 将驼峰命名转换为下划线命名 +fn camel_to_snake_upper(s: &str) -> String { + let mut result = String::new(); + let chars: Vec = s.chars().collect(); + let mut i = 0; + + while i < chars.len() { + let ch = chars[i]; + if ch.is_uppercase() { + // 检查是否有连续的大写字母 + let mut consecutive_uppercase = 1; + let mut j = i + 1; + // 计算连续大写字母的数量 + while j < chars.len() && chars[j].is_uppercase() { + consecutive_uppercase += 1; + j += 1; + } + // 情况1: 连续大写字母不在最后,且前面有字符 + if consecutive_uppercase > 1 && j < chars.len() && i > 0 { + // 将前面的大写字母跟最后一位以下划线分割 + for k in 0..consecutive_uppercase - 1 { + result.push(chars[i + k]); + } + result.push('_'); + result.push(chars[i + consecutive_uppercase - 1]); + i += consecutive_uppercase; + } + // 情况2: 连续大写字母在最后 + else if consecutive_uppercase > 1 && j >= chars.len() { + // 全部作为整体跟前面的字符以下划线分割 + if i > 0 { + result.push('_'); + } + for k in 0..consecutive_uppercase { + result.push(chars[i + k]); + } + i += consecutive_uppercase; + } + // 情况3: 单个大写字母 + else { + if i != 0 { + result.push('_'); + } + result.push(ch); + i += 1; + } + } else { + result.push(ch.to_ascii_uppercase()); + i += 1; + } + } + result +} + +pub struct RustCodeGenerator { + class_name: String, // StructName + orm_class_name: String, // DB{StructName} + table_constraint_info: Option, + all_column_info: Vec, +} + +impl RustCodeGenerator { + pub(crate) fn new() -> Self { + RustCodeGenerator { + class_name: "".to_string(), + orm_class_name: "".to_string(), + table_constraint_info: None, + all_column_info: vec![], + } + } + + pub(crate) fn set_class_name(&mut self, class_name: String) { + self.class_name = class_name; + } + + pub(crate) fn set_orm_class_name(&mut self, orm_class_name: String) { + self.orm_class_name = orm_class_name; + } + + pub(crate) fn set_table_constraint_info( + &mut self, + table_constraint_info: Option, + ) { + self.table_constraint_info = table_constraint_info; + } + + pub(crate) fn set_all_column_info(&mut self, all_column_info: Vec) { + self.all_column_info = all_column_info; + } +} + +impl RustCodeGenerator { + pub(crate) fn generate(&self, table: &WCDBTable) -> syn::Result { + let table_ident = table.ident(); + let db_table_ident = table.get_db_table_ident(); + + let table_name_snake = camel_to_snake_upper(&db_table_ident.to_string()); + let binding = format!("{}_BINDING", table_name_snake); + let binding_ident = Ident::new(&binding, Span::call_site()); + let instance = format!("{}_INSTANCE", table_name_snake); + let instance_ident = Ident::new(&instance, Span::call_site()); + let field_ident_vec = table.get_field_ident_vec(); + + let generate_fields = self.generate_fields( + &table_ident, + &db_table_ident, + &instance_ident, + &field_ident_vec, + )?; + + let generate_columns = self.generate_columns(&binding_ident)?; + let generate_table_config = self.generate_table_config(&binding_ident)?; + + let generate_binding_type = self.generate_binding_type(&table_ident)?; + let generate_binding_fields = + self.generate_binding_fields(&table_ident, &field_ident_vec)?; + let generate_base_binding = self.generate_base_binding(&binding_ident)?; + + let generate_extract_object = self.generate_extract_object(&table_ident)?; + let generate_bind_object = self.generate_bind_object(&table_ident)?; + + let generate_auto_increment_config = self.generate_auto_increment_config(&table_ident)?; + + Ok(quote::quote! { + pub static #binding_ident: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| { + wcdb::orm::binding::Binding::new() + }); + pub static #instance_ident: once_cell::sync::Lazy<#db_table_ident> = once_cell::sync::Lazy::new(|| { + let mut instance = #db_table_ident::default(); + let instance_raw = unsafe { &instance as *const #db_table_ident }; + + #generate_columns + + #generate_table_config + + instance + }); + + #generate_fields + + impl Default for #table_ident { + fn default() -> Self { + Self { + #( + #field_ident_vec: Default::default() + ),* + } + } + } + + pub struct #db_table_ident { + #( + pub #field_ident_vec: *const wcdb::orm::field::Field<#table_ident> + ),* + } + + impl Default for #db_table_ident { + fn default() -> Self { + Self { + #( + #field_ident_vec: std::ptr::null() + ),* + } + } + } + + impl Drop for #db_table_ident { + fn drop(&mut self) { + unsafe { + #( + if !self.#field_ident_vec.is_null() { + Box::from_raw(self.#field_ident_vec as *mut wcdb::orm::field::Field<#table_ident>); + } + )* + } + } + } + + unsafe impl Send for #db_table_ident {} + unsafe impl Sync for #db_table_ident {} + + impl wcdb::orm::table_binding::TableBinding<#table_ident> for #db_table_ident { + #generate_binding_type + + #generate_binding_fields + + #generate_base_binding + + #generate_extract_object + + #generate_bind_object + + #generate_auto_increment_config + } + }) + } +} + +impl RustCodeGenerator { + fn generate_fields( + &self, + table_ident: &Ident, + db_table_ident: &Ident, + instance_ident: &Ident, + field_ident_vec: &Vec<&Ident>, + ) -> syn::Result { + let mut fields_token_stream = proc_macro2::TokenStream::new(); + for column_info in &self.all_column_info { + let name = &column_info.property_name(); + let name_ident = Ident::new(&*name, Span::call_site()); + fields_token_stream.extend(quote! { + pub fn #name_ident() -> &'static wcdb::orm::field::Field<#table_ident> { + unsafe { &*#instance_ident.#name_ident } + } + }); + } + + Ok(quote! { + impl #db_table_ident { + pub fn all_fields() -> Vec<&'static wcdb::orm::field::Field<#table_ident>> { + unsafe { vec![ + #( + &*#instance_ident.#field_ident_vec, + )* + ] } + } + + #fields_token_stream + } + }) + } + + fn generate_columns(&self, binding_ident: &Ident) -> syn::Result { + if self.all_column_info.is_empty() { + return Ok(quote::quote! {}.into()); + } + + let mut token_stream = proc_macro2::TokenStream::new(); + let mut field_id: usize = 1; + for column_info in &self.all_column_info { + let field_orm_info = column_info.get_field_orm_info(); + let property_name = column_info.property_name(); + let mut column_name = column_info.column_name(); + if column_name.is_empty() { + column_name = property_name.clone(); + } + + let column_name_ident = Ident::new(&*column_name, Span::call_site()); + let field_ident = Ident::new(&*property_name, Span::call_site()); + let field_def_ident = Ident::new( + &format!("{}_def", field_ident.to_string()), + Span::call_site(), + ); + + let is_primary_key = column_info.is_primary(); + let is_auto_increment = column_info.is_auto_increment(); + let column_type_ident = + Ident::new(field_orm_info.column_type.as_str(), Span::call_site()); + + token_stream.extend(quote! { + let field = Box::new(wcdb::orm::field::Field::new( + stringify!(#column_name_ident), + instance_raw, + #field_id, + #is_auto_increment, + #is_primary_key + )); + }); + field_id += 1; + + token_stream.extend(quote! { + let param = wcdb::base::param::enum_string_column_def::StringColumnDef::Column( + &field.get_column(), + Some(wcdb::winq::column_type::ColumnType::#column_type_ident) + ); + let #field_def_ident = wcdb::winq::column_def::ColumnDef::new(param); + }); + + token_stream.extend(quote! { + let column_constraint = wcdb::winq::column_constraint::ColumnConstraint::new(None); + }); + + if is_primary_key { + token_stream.extend(quote! { + column_constraint.primary_key(); + }); + if is_auto_increment { + token_stream.extend(quote! { + column_constraint.auto_increment(); + }); + } + } + + match column_info.default_value() { + None => {} + Some(default) => { + let field_orm_info = column_info.get_field_orm_info(); + if field_orm_info.column_type == "Integer" { + let int_value = default.i32_value(); + token_stream.extend(quote::quote! { + column_constraint.default_to(#int_value); + }); + } else if field_orm_info.column_type == "Float" { + let double_value = default.f64_value(); + token_stream.extend(quote::quote! { + column_constraint.default_to(#double_value); + }); + } else { + let text_value = default.text_value(); + token_stream.extend(quote::quote! { + column_constraint.default_to(#text_value); + }); + } + } + } + + if column_info.is_unique() { + token_stream.extend(quote! { + column_constraint.unique(); + }); + } + + if column_info.is_not_null() { + token_stream.extend(quote! { + column_constraint.not_null(); + }); + } + + if column_info.is_not_indexed() { + token_stream.extend(quote! { + column_constraint.un_index(); + }); + } + + token_stream.extend(quote! { + #field_def_ident.constraint(&column_constraint); + }); + + token_stream.extend(quote! { + instance.#field_ident = unsafe { Box::into_raw(field) }; + #binding_ident.add_column_def(#field_def_ident); + }); + + if column_info.enable_auto_increment_for_existing_table() { + token_stream.extend(quote! { + #binding_ident.enable_auto_increment_for_existing_table(); + }); + } + + if !column_info.has_index() { + continue; + } + let mut index_name = column_info.index_name(); + let mut is_full_name: bool = true; + if index_name.is_empty() { + is_full_name = false; + index_name = format!("{}{}{}", "_", column_name.clone().as_str(), "_index"); + } + + let index_is_unique = column_info.index_is_unique(); + let index_name_ident = Ident::new(&*index_name.clone(), Span::call_site()); + let property_name_ident = Ident::new(&*property_name.clone(), Span::call_site()); + token_stream.extend(quote::quote! { + let create_index = wcdb::winq::statement_create_index::StatementCreateIndex::new(); + create_index.if_not_exist(); + if #index_is_unique { + create_index.unique(); + } + let mut column_names: Vec = Vec::new(); + column_names.push(stringify!(#property_name_ident).to_string()); + create_index.indexed_by(column_names); + #binding_ident.add_index(stringify!(#index_name_ident), #is_full_name, create_index); + }); + } + Ok(token_stream) + } + + fn generate_table_config( + &self, + binding_ident: &Ident, + ) -> syn::Result { + let mut all_columns_map: HashMap = HashMap::new(); + for column_info in &self.all_column_info { + let key; + if column_info.column_name().is_empty() { + key = column_info.property_name(); + } else { + key = column_info.column_name(); + } + all_columns_map.insert(key, column_info.clone()); + } + + let mut token_stream = proc_macro2::TokenStream::new(); + + if let Some(table_config_info) = &self.table_constraint_info { + if let Some(multi_indexes) = table_config_info.multi_indexes() { + for multi_index in multi_indexes { + let index_name_ident: Ident = multi_index.get_index_name_ident(); + let index_column_name_ident_vec: Vec = + multi_index.get_index_column_name_ident_vec(&all_columns_map); + let mut is_full_name = true; + if multi_index.name().is_empty() { + is_full_name = false; + } + token_stream.extend(quote! { + let create_index = wcdb::winq::statement_create_index::StatementCreateIndex::new(); + create_index.if_not_exist(); + create_index.indexed_by( + unsafe {vec![ + #( + (*instance.#index_column_name_ident_vec).get_column(), + )* + ]} + ); + #binding_ident.add_index(stringify!(#index_name_ident), #is_full_name, create_index); + }); + } + } + + if let Some(multi_primaries) = table_config_info.multi_primaries() { + for primaries in multi_primaries { + let ident_vec: Vec = primaries.columns_ident_vec(&all_columns_map); + token_stream.extend(quote::quote! { + let table_constraint = wcdb::winq::table_constraint::TableConstraint::new(None); + table_constraint.primary_key(); + table_constraint.indexed_by( + unsafe {vec![ + #( + (*instance.#ident_vec).get_column(), + )* + ]} + ); + #binding_ident.add_table_constraint(table_constraint); + }); + } + } + + if let Some(multi_unique_vec) = table_config_info.multi_unique() { + for uniques in multi_unique_vec { + let ident_vec: Vec = uniques.columns_ident_vec(&all_columns_map); + token_stream.extend(quote::quote! { + let table_constraint = wcdb::winq::table_constraint::TableConstraint::new(None); + table_constraint.unique(); + table_constraint.indexed_by( + unsafe {vec![ + #( + (*instance.#ident_vec).get_column(), + )* + ]} + ); + #binding_ident.add_table_constraint(table_constraint); + }); + } + } + + if table_config_info.is_without_row_id() { + token_stream.extend(quote::quote! { + #binding_ident.config_without_row_id(); + }); + } + + if let Some(module_info) = table_config_info.fts_module() { + if module_info.fts_version().eq(&FTSVersion::NONE.to_string()) { + return Ok(token_stream); + } + + let version = module_info.fts_version(); + token_stream.extend(quote::quote! { + #binding_ident.config_virtual_module(#version); + }); + + let mut tokenizer = format!("{}{}", "tokenize = ", module_info.tokenizer()); + for parameter in module_info.tokenizer_parameters() { + tokenizer.push_str(format!(" {}", parameter).as_str()); + } + token_stream.extend(quote::quote! { + #binding_ident.config_virtual_module_argument(#tokenizer); + }); + + if !module_info.external_table().is_empty() { + let content = format!("{}{}{}", "content='", module_info.external_table(), "'"); + token_stream.extend(quote::quote! { + #binding_ident.config_virtual_module_argument(#content); + }); + } + } + } + + Ok(token_stream.clone()) + } + + fn generate_binding_type(&self, table_ident: &&Ident) -> syn::Result { + Ok(quote::quote! { + fn binding_type(&self) -> std::any::TypeId { + std::any::TypeId::of::<#table_ident>() + } + }) + } + + fn generate_binding_fields( + &self, + table_ident: &&Ident, + field_ident_vec: &Vec<&Ident>, + ) -> syn::Result { + Ok(quote::quote! { + fn all_binding_fields(&self) -> Vec<&wcdb::orm::field::Field<#table_ident>> { + unsafe { vec![ + #( + &*self.#field_ident_vec, + )* + ] } + } + }) + } + + fn generate_base_binding( + &self, + binding_ident: &Ident, + ) -> syn::Result { + Ok(quote::quote! { + fn base_binding(&self) -> &wcdb::orm::binding::Binding { + &*#binding_ident + } + }) + } + + fn generate_extract_object( + &self, + table_ident: &&Ident, + ) -> syn::Result { + let all_column_info_vec = &self.all_column_info; + + let mut column_index: usize = 1; + let mut extract_token_stream_vec = vec![]; + + for column_info in all_column_info_vec { + let field_orm_info = column_info.get_field_orm_info(); + + let field_name_ident = + Ident::new(column_info.property_name().as_str(), Span::call_site()); + let extract_method_ident = + Ident::new(field_orm_info.field_getter.as_str(), Span::call_site()); + + if field_orm_info.nullable { + extract_token_stream_vec.push(quote! { + #column_index => { + if prepared_statement.get_column_type(index) != wcdb::winq::column_type::ColumnType::Null { + new_one.#field_name_ident = prepared_statement.#extract_method_ident(index); + } else { + new_one.#field_name_ident = None; + } + } + }); + } else { + extract_token_stream_vec.push(quote! { + #column_index => new_one.#field_name_ident = prepared_statement.#extract_method_ident(index) + }); + } + column_index += 1; + } + + Ok(quote::quote! { + fn extract_object( + &self, + fields: &Vec<&wcdb::orm::field::Field<#table_ident>>, + prepared_statement: &wcdb::core::prepared_statement::PreparedStatement, + ) -> #table_ident { + let mut new_one = #table_ident::default(); + let mut index = 0; + for field in fields { + match field.get_field_id() { + #( + #extract_token_stream_vec, + )* + _ => panic!("Unknown field id"), + } + index += 1; + } + new_one + } + }) + } + + fn generate_bind_object(&self, table_ident: &&Ident) -> syn::Result { + let all_column_info_vec = &self.all_column_info; + let mut column_index: usize = 1; + let mut bind_token_stream_vec = vec![]; + for column_info in all_column_info_vec { + let field_orm_info = column_info.get_field_orm_info(); + + let field_name_ident = + Ident::new(column_info.property_name().as_str(), Span::call_site()); + let bind_method_ident = + Ident::new(field_orm_info.field_setter.as_str(), Span::call_site()); + + if field_orm_info.nullable { + bind_token_stream_vec.push(quote::quote! { + #column_index => { + if object.#field_name_ident.is_some() { + prepared_statement.#bind_method_ident(object.#field_name_ident.as_ref(), index); + } else { + prepared_statement.bind_null(index); + } + } + }); + } else { + if field_orm_info.column_type == "Text".to_string() + || field_orm_info.column_type == "BLOB".to_string() + { + bind_token_stream_vec.push(quote::quote! { + #column_index => prepared_statement.#bind_method_ident(object.#field_name_ident.as_ref(), index) + }); + } else { + bind_token_stream_vec.push(quote::quote! { + #column_index => prepared_statement.#bind_method_ident(object.#field_name_ident, index) + }); + } + } + column_index += 1; + } + Ok(quote::quote! { + fn bind_field( + &self, + object: &#table_ident, + field: &wcdb::orm::field::Field<#table_ident>, + index: usize, + prepared_statement: &std::sync::Arc, + ) { + match field.get_field_id() { + #( + #bind_token_stream_vec, + )* + _ => panic!("Invalid id {} of field {} in {}", + field.get_field_id(), + wcdb::winq::identifier::IdentifierTrait::get_description(field), + stringify!(#table_ident) + ), + } + } + }) + } + + fn generate_auto_increment_config( + &self, + table_ident: &&Ident, + ) -> syn::Result { + let mut auto_increment_column_info_opt = None; + for column_info in &self.all_column_info { + if column_info.is_auto_increment() && column_info.is_primary() { + auto_increment_column_info_opt = Some(column_info); + break; + } + } + if let Some(auto_increment_column_info) = auto_increment_column_info_opt { + let field_ident = Ident::new( + auto_increment_column_info.property_name().as_str(), + Span::call_site(), + ); + let field_type_ident = Ident::new( + auto_increment_column_info.property_type().as_str(), + Span::call_site(), + ); + + Ok(quote! { + fn is_auto_increment(&self, object: &#table_ident) -> bool { + object.#field_ident == 0 + } + + fn set_last_insert_row_id(&self, object: &mut #table_ident, last_insert_row_id: i64) { + object.#field_ident = last_insert_row_id as #field_type_ident + } + }) + } else { + Ok(quote! { + fn is_auto_increment(&self, object: &#table_ident) -> bool { + false + } + + fn set_last_insert_row_id(&self, object: &mut #table_ident, last_insert_row_id: i64) { + + } + }) + } + } +} diff --git a/src/rust/wcdb_derive/src/compiler/rust_field_orm_info.rs b/src/rust/wcdb_derive/src/compiler/rust_field_orm_info.rs new file mode 100644 index 000000000..738064a3e --- /dev/null +++ b/src/rust/wcdb_derive/src/compiler/rust_field_orm_info.rs @@ -0,0 +1,97 @@ +use once_cell::sync::Lazy; +use std::collections::HashMap; + +pub struct RustFieldORMInfo { + pub(crate) column_type: String, + pub(crate) nullable: bool, + pub(crate) field_setter: String, + pub(crate) field_getter: String, +} + +impl RustFieldORMInfo { + pub fn new(column_type: &str, nullable: bool, field_setter: &str, field_getter: &str) -> Self { + Self { + column_type: column_type.to_string(), + nullable, + field_setter: field_setter.to_string(), + field_getter: field_getter.to_string(), + } + } +} + +pub static RUST_FIELD_ORM_INFO_MAP: Lazy> = Lazy::new(|| { + let mut all_info = HashMap::new(); + all_info.insert( + "bool".to_string(), + RustFieldORMInfo::new("Integer", false, "bind_bool", "get_bool"), + ); + all_info.insert( + "Option".to_string(), + RustFieldORMInfo::new("Integer", true, "bind_bool_opt", "get_bool_opt"), + ); + all_info.insert( + "i8".to_string(), + RustFieldORMInfo::new("Integer", false, "bind_i8", "get_i8"), + ); + all_info.insert( + "Option".to_string(), + RustFieldORMInfo::new("Integer", true, "bind_i8_opt", "get_i8_opt"), + ); + all_info.insert( + "i16".to_string(), + RustFieldORMInfo::new("Integer", false, "bind_i16", "get_i16"), + ); + all_info.insert( + "Option".to_string(), + RustFieldORMInfo::new("Integer", true, "bind_i16_opt", "get_i16_opt"), + ); + all_info.insert( + "i32".to_string(), + RustFieldORMInfo::new("Integer", false, "bind_i32", "get_i32"), + ); + all_info.insert( + "Option".to_string(), + RustFieldORMInfo::new("Integer", true, "bind_i32_opt", "get_i32_opt"), + ); + all_info.insert( + "i64".to_string(), + RustFieldORMInfo::new("Integer", false, "bind_i64", "get_i64"), + ); + all_info.insert( + "Option".to_string(), + RustFieldORMInfo::new("Integer", true, "bind_i64_opt", "get_i64_opt"), + ); + all_info.insert( + "f32".to_string(), + RustFieldORMInfo::new("Float", false, "bind_f32", "get_f32"), + ); + all_info.insert( + "Option".to_string(), + RustFieldORMInfo::new("Float", true, "bind_f32_opt", "get_f32_opt"), + ); + all_info.insert( + "f64".to_string(), + RustFieldORMInfo::new("Float", false, "bind_f64", "get_f64"), + ); + all_info.insert( + "Option".to_string(), + RustFieldORMInfo::new("Float", true, "bind_f64_opt", "get_f64_opt"), + ); + all_info.insert( + "String".to_string(), + RustFieldORMInfo::new("Text", false, "bind_text", "get_text"), + ); + all_info.insert( + "Option".to_string(), + RustFieldORMInfo::new("Text", true, "bind_text_opt", "get_text_opt"), + ); + all_info.insert( + "Vec".to_string(), + RustFieldORMInfo::new("BLOB", false, "bind_blob", "get_blob"), + ); + all_info.insert( + "Option>".to_string(), + RustFieldORMInfo::new("BLOB", true, "bind_blob_opt", "get_blob_opt"), + ); + all_info +}); diff --git a/src/rust/wcdb_derive/src/lib.rs b/src/rust/wcdb_derive/src/lib.rs new file mode 100644 index 000000000..28fd8b2be --- /dev/null +++ b/src/rust/wcdb_derive/src/lib.rs @@ -0,0 +1,248 @@ +#![feature(proc_macro_quote)] + +mod compiler; +mod macros; + +use crate::compiler::resolved_info::column_info::ColumnInfo; +use crate::compiler::resolved_info::table_config_info::TableConfigInfo; +use crate::compiler::rust_code_generator::RustCodeGenerator; +use crate::compiler::rust_field_orm_info::RUST_FIELD_ORM_INFO_MAP; +use crate::macros::fts_version::FTSVersion; +use crate::macros::wcdb_field::WCDBField; +use crate::macros::wcdb_table::WCDBTable; +use darling::{FromDeriveInput, FromField, FromMeta}; +use proc_macro::TokenStream; +use quote::ToTokens; +use std::any::Any; +use std::collections::HashSet; +use std::fmt::Debug; +use syn::parse::Parse; +use syn::spanned::Spanned; +use syn::{parse_macro_input, DeriveInput}; + +#[proc_macro_derive(WCDBTableCoding, attributes(WCDBTable, WCDBField))] +pub fn process(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + check_class_element(&input); + let table = WCDBTable::from_derive_input(&input).unwrap(); + check_fts_module(&table); + let table_constraint_info = TableConfigInfo::resolve(&table); + let all_column_info = table.get_all_column_info(); + check_field_element(&table, &all_column_info); + check_field_default(&all_column_info); + check_column_in_table_constraint(&table_constraint_info, &all_column_info); + match create_orm_file(&table, table_constraint_info, all_column_info) { + Ok(quote) => quote.into(), + Err(e) => e.to_compile_error().into(), + } +} + +fn create_orm_file( + table: &WCDBTable, + table_constraint_info: TableConfigInfo, + all_column_info: Vec, +) -> syn::Result { + let mut code_gen = RustCodeGenerator::new(); + code_gen.set_class_name(table.get_struct_name()); + code_gen.set_orm_class_name(table.get_db_table_name()); + code_gen.set_table_constraint_info(Option::from(table_constraint_info)); + code_gen.set_all_column_info(all_column_info); + code_gen.generate(&table) +} + +fn check_class_element(input: &DeriveInput) { + let is_struct = match &input.data { + syn::Data::Struct(_) => true, + syn::Data::Enum(_) => false, + _ => false, + }; + if !is_struct { + panic!("@WCDBTableCoding is only valid for structure"); + } + let vis_str = input.vis.to_token_stream().to_string(); + if vis_str != "pub" { + panic!( + "The visibility of the structure with @WCDBTableCoding macro definition must be pub" + ); + } +} + +fn check_fts_module(table: &WCDBTable) { + if let Some(fts_module) = table.get_fts_module() { + if fts_module.version() == FTSVersion::NONE { + if !fts_module.tokenizer().is_empty() || !fts_module.tokenizer_parameters().is_empty() { + panic!( + "You need to config fts version in @FTSModule : {}", + table.get_struct_name() + ); + } + } + if fts_module.tokenizer().is_empty() { + panic!( + "You need to config a tokenizer in @FTSModule : {}", + table.get_struct_name() + ); + } + } +} + +fn check_field_element(table: &WCDBTable, all_column_info: &Vec) { + let column_type_vec: Vec<&String> = RUST_FIELD_ORM_INFO_MAP.keys().collect(); + let mut primary_key_count = 0; + for column_info in all_column_info { + let has_contain = column_type_vec + .iter() + .any(|x| *x == column_info.property_type().as_str()); + if !has_contain { + panic!( + "The type {} of field {} in {} is Unsupported!", + column_info.property_type(), + column_info.property_name(), + table.get_struct_name() + ); + } + + let field_key = column_info.property_name(); + let field_key = field_key.as_str(); + if column_info.is_primary() { + primary_key_count += 1; + if primary_key_count > 1 { + panic!("#[WCDBField] can only configure one primary key for \"{}\". If multiple primary keys are required, configure multiPrimaries in #[WCDBTableCoding]. ", field_key) + } + if column_info.is_auto_increment() { + let field_orm_info = column_info.get_field_orm_info(); + if field_orm_info.column_type != "Integer" { + panic!( + "#[WCDBField] Auto-increment field must be integer for \"{}\".", + field_key + ); + } + } + if column_info.has_index() { + panic!("Restricted to primary key, so no @WCDBIndex configuration is required.field_key: \"{}\".", field_key); + } + } else if column_info.is_auto_increment() { + panic!( + "#[WCDBField] Auto-increment field must be primary key for \"{}\".", + field_key + ); + } + + let field_orm_info = column_info.get_field_orm_info(); + if column_info.is_not_null() && field_orm_info.nullable { + panic!( + "#[WCDBField] Not null field cannot support \"{}: {}\".", + field_key, + column_info.property_type(), + ); + } + } +} + +fn check_field_default(all_column_info: &Vec) { + for column_info in all_column_info { + let mut value_count = 0; + let mut type_miss_match = false; + let field_orm_info = column_info.get_field_orm_info(); + let column_type = field_orm_info.column_type.clone(); + let default_opt = column_info.default_value(); + if default_opt.is_none() { + continue; + } + let default = default_opt.clone().unwrap(); + if default.i32_value() != 0 { + value_count = value_count + 1; + if column_type != "Integer" { + type_miss_match = true; + } + } + if default.f64_value() != 0f64 { + value_count = value_count + 1; + if column_type != "Float" { + type_miss_match = true; + } + } + if !default.text_value().is_empty() { + value_count = value_count + 1; + if column_type != "Text" { + type_miss_match = true; + } + } + if value_count > 1 { + panic!( + "Only one default value can be configured for a field. \"{}\".", + column_info.property_name() + ); + } else if type_miss_match { + if column_type != "BLOB" { + panic!( + "Assigning a default value to BLOB is unsupported. \"{}\".", + column_info.property_name() + ); + } else { + panic!("Default value should be a \"{}\".", column_type); + } + } + } +} + +fn check_column_in_table_constraint( + table_config_info: &TableConfigInfo, + all_column_info: &Vec, +) { + if table_config_info.multi_indexes_is_empty() + && table_config_info.multi_primaries_is_empty() + && table_config_info.multi_unique_is_empty() + { + return; + } + + let mut all_columns: HashSet = HashSet::new(); + for column_info in all_column_info { + let name = if column_info.column_name().is_empty() { + column_info.property_name() + } else { + column_info.column_name() + }; + all_columns.insert(name); + } + + if let Some(indexes_info_vec) = table_config_info.multi_indexes() { + for indexes_info in indexes_info_vec { + for str in indexes_info.columns() { + if !all_columns.contains(str) { + panic!( + "Can't find column \"{}\" in class orm multi-index config.", + str + ); + } + } + } + } + + if let Some(multi_primary_vec) = table_config_info.multi_primaries() { + for primary_info in multi_primary_vec { + for name in primary_info.columns() { + if !all_columns.contains(name) { + panic!( + "Can't find column \"{}\" in class orm multi-primaries config.", + name + ); + } + } + } + } + + if let Some(multi_unique_vec) = table_config_info.multi_unique() { + for unique_info in multi_unique_vec { + for name in unique_info.columns() { + if !all_columns.contains(name) { + panic!( + "Can't find column \"{}\" in class orm multi-unique config.", + name + ); + } + } + } + } +} diff --git a/src/rust/wcdb_derive/src/macros/fts_module.rs b/src/rust/wcdb_derive/src/macros/fts_module.rs new file mode 100644 index 000000000..6ebe7c6b2 --- /dev/null +++ b/src/rust/wcdb_derive/src/macros/fts_module.rs @@ -0,0 +1,42 @@ +use crate::macros::fts_version::FTSVersion; +use darling::FromMeta; +use syn::LitStr; + +#[derive(Debug, FromMeta, Default)] +pub struct FTSModule { + #[darling(default)] + version: String, // darling 无法解析枚举值 FTSVersion,故通过 String 反转为 FTSVersion + #[darling(default)] + tokenizer: String, // todo qixinbing 考虑如何支持 BuiltinTokenizer 内的常量 + #[darling(default)] + tokenizer_parameters: Vec, + #[darling(default)] + external_table: String, +} + +impl FTSModule { + pub fn new() -> Self { + FTSModule { + version: FTSVersion::NONE.to_string(), + tokenizer: "".to_string(), + tokenizer_parameters: vec![], + external_table: "".to_string(), + } + } + + pub fn version(&self) -> FTSVersion { + FTSVersion::from_str(&self.version) + } + + pub fn tokenizer(&self) -> &str { + &self.tokenizer + } + + pub fn tokenizer_parameters(&self) -> &Vec { + &self.tokenizer_parameters + } + + pub fn external_table(&self) -> &str { + &self.external_table + } +} diff --git a/src/rust/wcdb_derive/src/macros/fts_version.rs b/src/rust/wcdb_derive/src/macros/fts_version.rs new file mode 100644 index 000000000..1a2421479 --- /dev/null +++ b/src/rust/wcdb_derive/src/macros/fts_version.rs @@ -0,0 +1,35 @@ +use darling::FromMeta; + +#[repr(i32)] +#[derive(Debug, Default, FromMeta, PartialEq)] +pub enum FTSVersion { + #[default] + NONE, + FTS3, + FTS4, + FTS5, +} + +impl FTSVersion { + pub fn to_string(&self) -> String { + match self { + FTSVersion::NONE => "NONE".to_string(), + FTSVersion::FTS3 => "FTS3".to_string(), + FTSVersion::FTS4 => "FTS4".to_string(), + FTSVersion::FTS5 => "FTS5".to_string(), + } + } + + pub fn from_str(s: &str) -> Self { + match s { + "NONE" => FTSVersion::NONE, + "FTS3" => FTSVersion::FTS3, + "FTS4" => FTSVersion::FTS4, + "FTS5" => FTSVersion::FTS5, + _ => panic!( + "Invalid FTS version {} : only support NONE/FTS3/FTS4/FTS5 ", + s + ), + } + } +} diff --git a/src/rust/wcdb_derive/src/macros/mod.rs b/src/rust/wcdb_derive/src/macros/mod.rs new file mode 100644 index 000000000..29db322b6 --- /dev/null +++ b/src/rust/wcdb_derive/src/macros/mod.rs @@ -0,0 +1,9 @@ +pub(crate) mod fts_module; +pub(crate) mod fts_version; +pub(crate) mod multi_indexes; +pub(crate) mod multi_primary; +pub(crate) mod multi_unique; +pub(crate) mod wcdb_default; +pub(crate) mod wcdb_field; +pub(crate) mod wcdb_index; +pub(crate) mod wcdb_table; diff --git a/src/rust/wcdb_derive/src/macros/multi_indexes.rs b/src/rust/wcdb_derive/src/macros/multi_indexes.rs new file mode 100644 index 000000000..184afb845 --- /dev/null +++ b/src/rust/wcdb_derive/src/macros/multi_indexes.rs @@ -0,0 +1,63 @@ +use crate::compiler::resolved_info::column_info::ColumnInfo; +use darling::FromMeta; +use proc_macro2::{Ident, Span}; +use syn::LitStr; + +#[derive(Debug, FromMeta, Clone)] +pub struct MultiIndexes { + #[darling(default)] + name: Option, + #[darling(default)] + columns: Vec, +} + +impl MultiIndexes { + pub fn get_index_name_ident(&self) -> Ident { + let index_name = match &self.name { + None => { + let columns = &self.columns; + columns + .iter() + .flat_map(|s| vec!["_".to_string(), s.value().clone()]) + .collect::() + + "_index" + } + Some(index_name) => index_name.value(), + }; + Ident::new(&index_name, Span::call_site()) + } + + pub(crate) fn get_index_column_name_ident_vec( + &self, + all_field_info_vec: &Vec, + ) -> Vec { + if self.columns.is_empty() { + return vec![]; + } + let mut ret_vec = vec![]; + for column in self.columns.iter() { + let column_name = &column.value(); + let mut property_name = column_name.clone(); + for column_info in all_field_info_vec { + if column_info.column_name() == column_name.clone() { + property_name = column_info.property_name(); + break; + } + } + ret_vec.push(Ident::new(property_name.as_str(), Span::call_site())); + } + ret_vec + } + + pub fn get_is_full_name(&self) -> bool { + self.name.is_some() + } + + pub fn name(&self) -> &Option { + &self.name + } + + pub fn columns(&self) -> &Vec { + &self.columns + } +} diff --git a/src/rust/wcdb_derive/src/macros/multi_primary.rs b/src/rust/wcdb_derive/src/macros/multi_primary.rs new file mode 100644 index 000000000..dca105b7c --- /dev/null +++ b/src/rust/wcdb_derive/src/macros/multi_primary.rs @@ -0,0 +1,18 @@ +use darling::FromMeta; +use syn::LitStr; + +#[derive(Debug, Clone, FromMeta)] +pub struct MultiPrimary { + #[darling(default)] + columns: Vec, +} + +impl MultiPrimary { + pub fn new() -> Self { + MultiPrimary { columns: vec![] } + } + + pub fn columns(&self) -> &Vec { + &self.columns + } +} diff --git a/src/rust/wcdb_derive/src/macros/multi_unique.rs b/src/rust/wcdb_derive/src/macros/multi_unique.rs new file mode 100644 index 000000000..0c71039f1 --- /dev/null +++ b/src/rust/wcdb_derive/src/macros/multi_unique.rs @@ -0,0 +1,18 @@ +use darling::FromMeta; +use syn::LitStr; + +#[derive(Debug, Clone, FromMeta)] +pub struct MultiUnique { + #[darling(default)] + columns: Vec, +} + +impl MultiUnique { + pub fn new() -> Self { + MultiUnique { columns: vec![] } + } + + pub fn columns(&self) -> &Vec { + &self.columns + } +} diff --git a/src/rust/wcdb_derive/src/macros/wcdb_default.rs b/src/rust/wcdb_derive/src/macros/wcdb_default.rs new file mode 100644 index 000000000..cedcf59ae --- /dev/null +++ b/src/rust/wcdb_derive/src/macros/wcdb_default.rs @@ -0,0 +1,33 @@ +use darling::FromMeta; + +#[derive(FromMeta, Debug)] +pub struct WCDBDefault { + #[darling(default)] + i32_value: i64, + #[darling(default)] + f64_value: f64, + #[darling(default)] + text_value: String, +} + +impl WCDBDefault { + pub fn new() -> Self { + WCDBDefault { + i32_value: 0, + f64_value: 0.0, + text_value: "".to_string(), + } + } + + pub fn i32_value(&self) -> i64 { + self.i32_value + } + + pub fn f64_value(&self) -> f64 { + self.f64_value + } + + pub fn text_value(&self) -> &str { + &self.text_value + } +} diff --git a/src/rust/wcdb_derive/src/macros/wcdb_field.rs b/src/rust/wcdb_derive/src/macros/wcdb_field.rs new file mode 100644 index 000000000..d28c66d4f --- /dev/null +++ b/src/rust/wcdb_derive/src/macros/wcdb_field.rs @@ -0,0 +1,112 @@ +use crate::macros::wcdb_default::WCDBDefault; +use crate::macros::wcdb_index::WCDBIndex; +use darling::FromField; +use proc_macro2::Ident; +use syn::{GenericArgument, Type}; + +#[derive(Debug, FromField)] +#[darling(attributes(WCDBField))] +pub struct WCDBField { + ident: Option, + ty: Type, + #[darling(default)] + column_name: String, + #[darling(default)] + is_primary: bool, + #[darling(default)] + is_auto_increment: bool, + #[darling(default)] + enable_auto_increment_for_existing_table: bool, + #[darling(default)] + is_unique: bool, + #[darling(default)] + is_not_null: bool, + #[darling(default)] + is_not_indexed: bool, + #[darling(default)] + default: Option, + #[darling(default)] + index: Option, +} + +impl WCDBField { + pub fn ident(&self) -> &Option { + &self.ident + } + + pub fn ty(&self) -> &Type { + &self.ty + } + + pub fn column_name(&self) -> String { + self.column_name.clone() + } + + pub fn is_primary(&self) -> bool { + self.is_primary + } + + pub fn is_auto_increment(&self) -> bool { + self.is_auto_increment + } + + pub fn enable_auto_increment_for_existing_table(&self) -> bool { + self.enable_auto_increment_for_existing_table + } + + pub fn is_unique(&self) -> bool { + self.is_unique + } + + pub fn is_not_null(&self) -> bool { + self.is_not_null + } + + pub fn is_not_indexed(&self) -> bool { + self.is_not_indexed + } + + pub fn default(&self) -> &Option { + &self.default + } + + pub fn index(&self) -> &Option { + &self.index + } +} + +impl WCDBField { + fn extract_type_path(ty: &Type) -> Vec { + let mut segments = Vec::new(); + if let Type::Path(type_path) = ty { + for segment in &type_path.path.segments { + segments.push(segment.ident.to_string()); + if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { + for arg in &args.args { + if let GenericArgument::Type(inner_ty) = arg { + segments.extend(Self::extract_type_path(inner_ty)); + } + } + } + } + } + segments + } + + pub fn get_field_type_string(field: &Type) -> syn::Result { + let segments = Self::extract_type_path(field); + let mut value = segments.iter().fold(String::new(), |acc, s| { + if acc.is_empty() { + s.clone() + } else { + format!("{}<{}", acc, s) + } + }); + if segments.len() > 1 { + for _ in 0..segments.len() - 1 { + value.push('>'); + } + } + Ok(value) + } +} diff --git a/src/rust/wcdb_derive/src/macros/wcdb_index.rs b/src/rust/wcdb_derive/src/macros/wcdb_index.rs new file mode 100644 index 000000000..6b9c11ea1 --- /dev/null +++ b/src/rust/wcdb_derive/src/macros/wcdb_index.rs @@ -0,0 +1,19 @@ +use darling::FromMeta; + +#[derive(FromMeta, Debug)] +pub struct WCDBIndex { + #[darling(default)] + name: String, + #[darling(default)] + is_unique: bool, +} + +impl WCDBIndex { + pub fn name(&self) -> String { + self.name.clone() + } + + pub fn is_unique(&self) -> bool { + self.is_unique + } +} diff --git a/src/rust/wcdb_derive/src/macros/wcdb_table.rs b/src/rust/wcdb_derive/src/macros/wcdb_table.rs new file mode 100644 index 000000000..36a6589c1 --- /dev/null +++ b/src/rust/wcdb_derive/src/macros/wcdb_table.rs @@ -0,0 +1,100 @@ +use crate::compiler::resolved_info::column_info::ColumnInfo; +use crate::macros::fts_module::FTSModule; +use crate::macros::multi_indexes::MultiIndexes; +use crate::macros::multi_primary::MultiPrimary; +use crate::macros::multi_unique::MultiUnique; +use crate::macros::wcdb_field::WCDBField; +use darling::ast::Data; +use darling::FromDeriveInput; +use proc_macro2::{Ident, Span}; +use syn::Generics; + +#[derive(Debug, FromDeriveInput)] +#[darling(attributes(WCDBTable))] +pub struct WCDBTable { + ident: Ident, + generics: Generics, + data: Data<(), WCDBField>, + // For fields with columnName macro, specify MultiIndexes columns as columnName; otherwise use propertyName + #[darling(default, multiple)] + multi_indexes: Vec, + // For fields with columnName macro, specify MultiPrimary columns as columnName; otherwise use propertyName + #[darling(default, multiple)] + multi_primaries: Vec, + // For fields with columnName macro, specify MultiUnique columns as columnName; otherwise use propertyName + #[darling(default, multiple)] + multi_unique: Vec, + #[darling(default)] + is_without_row_id: bool, + #[darling(default)] + fts_module: Option, +} + +impl WCDBTable { + pub fn get_db_table_ident(&self) -> Ident { + Ident::new(&format!("Db{}", self.ident), Span::call_site()) + } + + pub fn get_struct_name(&self) -> String { + self.ident.to_string() + } + + pub fn get_db_table_name(&self) -> String { + format!("Db{}", self.get_struct_name()) + } + + pub fn get_all_column_info(&self) -> Vec { + match &self.data { + Data::Struct(fields) => { + let mut all_column_info = Vec::new(); + for field in &fields.fields { + all_column_info.push(ColumnInfo::resolve(&field)); + } + all_column_info + } + _ => panic!("WCDBTable only works on structs"), + } + } + + pub fn get_field_ident_vec(&self) -> Vec<&Ident> { + match &self.data { + Data::Struct(fields) => fields + .iter() + .map(|field| field.ident().as_ref().unwrap()) + .collect(), + _ => panic!("WCDBTable only works on structs"), + } + } + + pub(crate) fn ident(&self) -> &Ident { + &self.ident + } + + pub(crate) fn generics(&self) -> &Generics { + &self.generics + } + + pub(crate) fn data(&self) -> &Data<(), WCDBField> { + &self.data + } + + pub(crate) fn multi_indexes(&self) -> &Vec { + &self.multi_indexes + } + + pub(crate) fn multi_primaries(&self) -> &Vec { + &self.multi_primaries + } + + pub(crate) fn multi_unique(&self) -> &Vec { + &self.multi_unique + } + + pub(crate) fn is_without_row_id(&self) -> bool { + self.is_without_row_id + } + + pub(crate) fn get_fts_module(&self) -> &Option { + &self.fts_module + } +} diff --git a/tools/prebuild/openssl/linux/loongarch64/libcrypto.a b/tools/prebuild/openssl/linux/loongarch64/libcrypto.a new file mode 100644 index 000000000..c221f048b Binary files /dev/null and b/tools/prebuild/openssl/linux/loongarch64/libcrypto.a differ