├── .gitignore ├── CMakeLists.txt ├── Chinese.md ├── LICENSE ├── README.md ├── docker ├── Dockerfile ├── example.proto └── google │ └── protobuf │ ├── any.proto │ ├── duration.proto │ ├── empty.proto │ ├── struct.proto │ ├── timestamp.proto │ └── wrappers.proto ├── src └── sw │ └── redis-protobuf │ ├── append_command.cpp │ ├── append_command.h │ ├── clear_command.cpp │ ├── clear_command.h │ ├── commands.cpp │ ├── commands.h │ ├── del_command.cpp │ ├── del_command.h │ ├── errors.h │ ├── field_ref.h │ ├── get_command.cpp │ ├── get_command.h │ ├── import_command.cpp │ ├── import_command.h │ ├── last_import_command.cpp │ ├── last_import_command.h │ ├── len_command.cpp │ ├── len_command.h │ ├── merge_command.cpp │ ├── merge_command.h │ ├── module_api.cpp │ ├── module_api.h │ ├── module_entry.cpp │ ├── module_entry.h │ ├── options.cpp │ ├── options.h │ ├── path.cpp │ ├── path.h │ ├── proto_factory.cpp │ ├── proto_factory.h │ ├── redis_protobuf.cpp │ ├── redis_protobuf.h │ ├── redismodule.cpp │ ├── redismodule.h │ ├── schema_command.cpp │ ├── schema_command.h │ ├── set_command.cpp │ ├── set_command.h │ ├── type_command.cpp │ ├── type_command.h │ ├── utils.cpp │ └── utils.h └── test └── src └── sw └── redis-protobuf ├── append_test.cpp ├── append_test.h ├── clear_test.cpp ├── clear_test.h ├── del_test.cpp ├── del_test.h ├── import_test.cpp ├── import_test.h ├── len_test.cpp ├── len_test.h ├── merge_test.cpp ├── merge_test.h ├── proto_test.cpp ├── proto_test.h ├── schema_test.cpp ├── schema_test.h ├── set_get_test.cpp ├── set_get_test.h ├── test_main.cpp ├── type_test.cpp ├── type_test.h └── utils.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | set(REDIS_PROTOBUF_VERSION "0.1.0") 4 | message(STATUS "redis-protobuf version: ${REDIS_PROTOBUF_VERSION}") 5 | 6 | project(redis-protobuf LANGUAGES CXX VERSION ${REDIS_PROTOBUF_VERSION}) 7 | 8 | set(REDIS_PROTOBUF_DEFAULT_BUILD_TYPE "Release") 9 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 10 | set(CMAKE_BUILD_TYPE ${REDIS_PROTOBUF_DEFAULT_BUILD_TYPE} CACHE STRING "Set build type" FORCE) 11 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo" "MinSizeRel") 12 | endif() 13 | message(STATUS "redis-protobuf build type: ${CMAKE_BUILD_TYPE}") 14 | 15 | set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Werror -fPIC -Wno-unused-parameter") 16 | 17 | set(PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR}/src/sw/redis-protobuf) 18 | 19 | file(GLOB PROJECT_SOURCE_FILES "${PROJECT_SOURCE_DIR}/*.cpp" "${PROJECT_SOURCE_DIR}/*.cc") 20 | 21 | set(SHARED_LIB shared) 22 | 23 | add_library(${SHARED_LIB} SHARED ${PROJECT_SOURCE_FILES}) 24 | 25 | # protobuf dependency 26 | find_path(PROTOBUF_HEADER google) 27 | target_include_directories(${SHARED_LIB} PUBLIC ${PROTOBUF_HEADER}) 28 | 29 | find_library(PROTOBUF_LIB libprotobuf.a) 30 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 31 | target_link_libraries(${SHARED_LIB} -Wl,-force_load ${PROTOBUF_LIB}) 32 | target_link_libraries(${SHARED_LIB} z) 33 | else() 34 | target_link_libraries(${SHARED_LIB} -Wl,--whole-archive ${PROTOBUF_LIB} -Wl,--no-whole-archive) 35 | endif() 36 | 37 | set_target_properties(${SHARED_LIB} PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) 38 | 39 | set_target_properties(${SHARED_LIB} PROPERTIES CLEAN_DIRECT_OUTPUT 1) 40 | 41 | include(GNUInstallDirs) 42 | 43 | # Install shared lib. 44 | install(TARGETS ${SHARED_LIB} 45 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 46 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 47 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 48 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 49 | -------------------------------------------------------------------------------- /Chinese.md: -------------------------------------------------------------------------------- 1 | 欢迎加入redis-protobuf微信交流群 2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | From redis:latest 2 | 3 | ENV LIBDIR /usr/lib/redis/modules 4 | ENV DEPS "make g++ curl cmake unzip" 5 | 6 | # Install dependencies 7 | RUN set -ex;\ 8 | deps="$DEPS";\ 9 | apt-get update;\ 10 | apt-get install -y --no-install-recommends $deps; 11 | 12 | # Install protobuf 13 | RUN set -ex;\ 14 | mkdir -p /usr/src;\ 15 | cd /usr/src;\ 16 | curl -L -k https://github.com/sewenew/redis-protobuf/releases/download/0.0.1/protobuf-3.8.0-map-reflection.tar.gz -o protobuf-3.8.0-map-reflection.tar.gz;\ 17 | tar xfz protobuf-3.8.0-map-reflection.tar.gz;\ 18 | cd protobuf-3.8.0-map-reflection;\ 19 | ./configure "CFLAGS=-fPIC" "CXXFLAGS=-fPIC" --prefix=/usr;\ 20 | make -j 4;\ 21 | make install; 22 | 23 | # Build redis-protobuf 24 | RUN set -ex;\ 25 | cd /usr/src;\ 26 | curl -L -k 'https://github.com/sewenew/redis-protobuf/archive/master.zip' -o redis-protobuf.zip;\ 27 | unzip redis-protobuf.zip;\ 28 | cd redis-protobuf-master;\ 29 | mkdir compile;\ 30 | cd compile;\ 31 | cmake -DCMAKE_BUILD_TYPE=Release ..;\ 32 | make; 33 | 34 | # Load redis-protobuf 35 | ENV REDISDIR /usr/lib/redis 36 | RUN set -ex;\ 37 | mkdir -p "$REDISDIR/proto/google/protobuf" "$REDISDIR/proto/google/protobuf/util" "$REDISDIR/proto/google/protobuf/compiler" "$REDISDIR/modules" "$REDISDIR/conf";\ 38 | cp /usr/src/redis-protobuf-master/docker/example.proto "$REDISDIR/proto";\ 39 | cp /usr/src/protobuf-3.8.0-map-reflection/src/google/protobuf/*.proto "$REDISDIR/proto/google/protobuf";\ 40 | cp /usr/src/redis-protobuf-master/compile/libredis-protobuf.so "$REDISDIR/modules";\ 41 | echo 'loadmodule /usr/lib/redis/modules/libredis-protobuf.so --dir /usr/lib/redis/proto' > "$REDISDIR/conf/redis.conf"; 42 | 43 | # Cleanup 44 | RUN set -ex;\ 45 | deps="$DEPS";\ 46 | apt-get purge -y --auto-remove $deps;\ 47 | rm -rf /usr/src/*; 48 | 49 | CMD ["redis-server", "/usr/lib/redis/conf/redis.conf"] 50 | -------------------------------------------------------------------------------- /docker/example.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | //package sw.redis.pb; 4 | 5 | message SubMsg { 6 | string s = 1; 7 | int32 i = 2; 8 | } 9 | 10 | message Msg { 11 | int32 i = 1; 12 | SubMsg sub = 2; 13 | repeated int32 arr = 3; 14 | map m = 4; 15 | } 16 | -------------------------------------------------------------------------------- /docker/google/protobuf/any.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option go_package = "github.com/golang/protobuf/ptypes/any"; 37 | option java_package = "com.google.protobuf"; 38 | option java_outer_classname = "AnyProto"; 39 | option java_multiple_files = true; 40 | option objc_class_prefix = "GPB"; 41 | 42 | // `Any` contains an arbitrary serialized protocol buffer message along with a 43 | // URL that describes the type of the serialized message. 44 | // 45 | // Protobuf library provides support to pack/unpack Any values in the form 46 | // of utility functions or additional generated methods of the Any type. 47 | // 48 | // Example 1: Pack and unpack a message in C++. 49 | // 50 | // Foo foo = ...; 51 | // Any any; 52 | // any.PackFrom(foo); 53 | // ... 54 | // if (any.UnpackTo(&foo)) { 55 | // ... 56 | // } 57 | // 58 | // Example 2: Pack and unpack a message in Java. 59 | // 60 | // Foo foo = ...; 61 | // Any any = Any.pack(foo); 62 | // ... 63 | // if (any.is(Foo.class)) { 64 | // foo = any.unpack(Foo.class); 65 | // } 66 | // 67 | // Example 3: Pack and unpack a message in Python. 68 | // 69 | // foo = Foo(...) 70 | // any = Any() 71 | // any.Pack(foo) 72 | // ... 73 | // if any.Is(Foo.DESCRIPTOR): 74 | // any.Unpack(foo) 75 | // ... 76 | // 77 | // Example 4: Pack and unpack a message in Go 78 | // 79 | // foo := &pb.Foo{...} 80 | // any, err := ptypes.MarshalAny(foo) 81 | // ... 82 | // foo := &pb.Foo{} 83 | // if err := ptypes.UnmarshalAny(any, foo); err != nil { 84 | // ... 85 | // } 86 | // 87 | // The pack methods provided by protobuf library will by default use 88 | // 'type.googleapis.com/full.type.name' as the type URL and the unpack 89 | // methods only use the fully qualified type name after the last '/' 90 | // in the type URL, for example "foo.bar.com/x/y.z" will yield type 91 | // name "y.z". 92 | // 93 | // 94 | // JSON 95 | // ==== 96 | // The JSON representation of an `Any` value uses the regular 97 | // representation of the deserialized, embedded message, with an 98 | // additional field `@type` which contains the type URL. Example: 99 | // 100 | // package google.profile; 101 | // message Person { 102 | // string first_name = 1; 103 | // string last_name = 2; 104 | // } 105 | // 106 | // { 107 | // "@type": "type.googleapis.com/google.profile.Person", 108 | // "firstName": , 109 | // "lastName": 110 | // } 111 | // 112 | // If the embedded message type is well-known and has a custom JSON 113 | // representation, that representation will be embedded adding a field 114 | // `value` which holds the custom JSON in addition to the `@type` 115 | // field. Example (for message [google.protobuf.Duration][]): 116 | // 117 | // { 118 | // "@type": "type.googleapis.com/google.protobuf.Duration", 119 | // "value": "1.212s" 120 | // } 121 | // 122 | message Any { 123 | // A URL/resource name that uniquely identifies the type of the serialized 124 | // protocol buffer message. This string must contain at least 125 | // one "/" character. The last segment of the URL's path must represent 126 | // the fully qualified name of the type (as in 127 | // `path/google.protobuf.Duration`). The name should be in a canonical form 128 | // (e.g., leading "." is not accepted). 129 | // 130 | // In practice, teams usually precompile into the binary all types that they 131 | // expect it to use in the context of Any. However, for URLs which use the 132 | // scheme `http`, `https`, or no scheme, one can optionally set up a type 133 | // server that maps type URLs to message definitions as follows: 134 | // 135 | // * If no scheme is provided, `https` is assumed. 136 | // * An HTTP GET on the URL must yield a [google.protobuf.Type][] 137 | // value in binary format, or produce an error. 138 | // * Applications are allowed to cache lookup results based on the 139 | // URL, or have them precompiled into a binary to avoid any 140 | // lookup. Therefore, binary compatibility needs to be preserved 141 | // on changes to types. (Use versioned type names to manage 142 | // breaking changes.) 143 | // 144 | // Note: this functionality is not currently available in the official 145 | // protobuf release, and it is not used for type URLs beginning with 146 | // type.googleapis.com. 147 | // 148 | // Schemes other than `http`, `https` (or the empty scheme) might be 149 | // used with implementation specific semantics. 150 | // 151 | string type_url = 1; 152 | 153 | // Must be a valid serialized protocol buffer of the above specified type. 154 | bytes value = 2; 155 | } 156 | -------------------------------------------------------------------------------- /docker/google/protobuf/duration.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option cc_enable_arenas = true; 37 | option go_package = "github.com/golang/protobuf/ptypes/duration"; 38 | option java_package = "com.google.protobuf"; 39 | option java_outer_classname = "DurationProto"; 40 | option java_multiple_files = true; 41 | option objc_class_prefix = "GPB"; 42 | 43 | // A Duration represents a signed, fixed-length span of time represented 44 | // as a count of seconds and fractions of seconds at nanosecond 45 | // resolution. It is independent of any calendar and concepts like "day" 46 | // or "month". It is related to Timestamp in that the difference between 47 | // two Timestamp values is a Duration and it can be added or subtracted 48 | // from a Timestamp. Range is approximately +-10,000 years. 49 | // 50 | // # Examples 51 | // 52 | // Example 1: Compute Duration from two Timestamps in pseudo code. 53 | // 54 | // Timestamp start = ...; 55 | // Timestamp end = ...; 56 | // Duration duration = ...; 57 | // 58 | // duration.seconds = end.seconds - start.seconds; 59 | // duration.nanos = end.nanos - start.nanos; 60 | // 61 | // if (duration.seconds < 0 && duration.nanos > 0) { 62 | // duration.seconds += 1; 63 | // duration.nanos -= 1000000000; 64 | // } else if (durations.seconds > 0 && duration.nanos < 0) { 65 | // duration.seconds -= 1; 66 | // duration.nanos += 1000000000; 67 | // } 68 | // 69 | // Example 2: Compute Timestamp from Timestamp + Duration in pseudo code. 70 | // 71 | // Timestamp start = ...; 72 | // Duration duration = ...; 73 | // Timestamp end = ...; 74 | // 75 | // end.seconds = start.seconds + duration.seconds; 76 | // end.nanos = start.nanos + duration.nanos; 77 | // 78 | // if (end.nanos < 0) { 79 | // end.seconds -= 1; 80 | // end.nanos += 1000000000; 81 | // } else if (end.nanos >= 1000000000) { 82 | // end.seconds += 1; 83 | // end.nanos -= 1000000000; 84 | // } 85 | // 86 | // Example 3: Compute Duration from datetime.timedelta in Python. 87 | // 88 | // td = datetime.timedelta(days=3, minutes=10) 89 | // duration = Duration() 90 | // duration.FromTimedelta(td) 91 | // 92 | // # JSON Mapping 93 | // 94 | // In JSON format, the Duration type is encoded as a string rather than an 95 | // object, where the string ends in the suffix "s" (indicating seconds) and 96 | // is preceded by the number of seconds, with nanoseconds expressed as 97 | // fractional seconds. For example, 3 seconds with 0 nanoseconds should be 98 | // encoded in JSON format as "3s", while 3 seconds and 1 nanosecond should 99 | // be expressed in JSON format as "3.000000001s", and 3 seconds and 1 100 | // microsecond should be expressed in JSON format as "3.000001s". 101 | // 102 | // 103 | message Duration { 104 | // Signed seconds of the span of time. Must be from -315,576,000,000 105 | // to +315,576,000,000 inclusive. Note: these bounds are computed from: 106 | // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years 107 | int64 seconds = 1; 108 | 109 | // Signed fractions of a second at nanosecond resolution of the span 110 | // of time. Durations less than one second are represented with a 0 111 | // `seconds` field and a positive or negative `nanos` field. For durations 112 | // of one second or more, a non-zero value for the `nanos` field must be 113 | // of the same sign as the `seconds` field. Must be from -999,999,999 114 | // to +999,999,999 inclusive. 115 | int32 nanos = 2; 116 | } 117 | -------------------------------------------------------------------------------- /docker/google/protobuf/empty.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option go_package = "github.com/golang/protobuf/ptypes/empty"; 37 | option java_package = "com.google.protobuf"; 38 | option java_outer_classname = "EmptyProto"; 39 | option java_multiple_files = true; 40 | option objc_class_prefix = "GPB"; 41 | option cc_enable_arenas = true; 42 | 43 | // A generic empty message that you can re-use to avoid defining duplicated 44 | // empty messages in your APIs. A typical example is to use it as the request 45 | // or the response type of an API method. For instance: 46 | // 47 | // service Foo { 48 | // rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); 49 | // } 50 | // 51 | // The JSON representation for `Empty` is empty JSON object `{}`. 52 | message Empty {} 53 | -------------------------------------------------------------------------------- /docker/google/protobuf/struct.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option cc_enable_arenas = true; 37 | option go_package = "github.com/golang/protobuf/ptypes/struct;structpb"; 38 | option java_package = "com.google.protobuf"; 39 | option java_outer_classname = "StructProto"; 40 | option java_multiple_files = true; 41 | option objc_class_prefix = "GPB"; 42 | 43 | // `Struct` represents a structured data value, consisting of fields 44 | // which map to dynamically typed values. In some languages, `Struct` 45 | // might be supported by a native representation. For example, in 46 | // scripting languages like JS a struct is represented as an 47 | // object. The details of that representation are described together 48 | // with the proto support for the language. 49 | // 50 | // The JSON representation for `Struct` is JSON object. 51 | message Struct { 52 | // Unordered map of dynamically typed values. 53 | map fields = 1; 54 | } 55 | 56 | // `Value` represents a dynamically typed value which can be either 57 | // null, a number, a string, a boolean, a recursive struct value, or a 58 | // list of values. A producer of value is expected to set one of that 59 | // variants, absence of any variant indicates an error. 60 | // 61 | // The JSON representation for `Value` is JSON value. 62 | message Value { 63 | // The kind of value. 64 | oneof kind { 65 | // Represents a null value. 66 | NullValue null_value = 1; 67 | // Represents a double value. 68 | double number_value = 2; 69 | // Represents a string value. 70 | string string_value = 3; 71 | // Represents a boolean value. 72 | bool bool_value = 4; 73 | // Represents a structured value. 74 | Struct struct_value = 5; 75 | // Represents a repeated `Value`. 76 | ListValue list_value = 6; 77 | } 78 | } 79 | 80 | // `NullValue` is a singleton enumeration to represent the null value for the 81 | // `Value` type union. 82 | // 83 | // The JSON representation for `NullValue` is JSON `null`. 84 | enum NullValue { 85 | // Null value. 86 | NULL_VALUE = 0; 87 | } 88 | 89 | // `ListValue` is a wrapper around a repeated field of values. 90 | // 91 | // The JSON representation for `ListValue` is JSON array. 92 | message ListValue { 93 | // Repeated field of dynamically typed values. 94 | repeated Value values = 1; 95 | } 96 | -------------------------------------------------------------------------------- /docker/google/protobuf/timestamp.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option cc_enable_arenas = true; 37 | option go_package = "github.com/golang/protobuf/ptypes/timestamp"; 38 | option java_package = "com.google.protobuf"; 39 | option java_outer_classname = "TimestampProto"; 40 | option java_multiple_files = true; 41 | option objc_class_prefix = "GPB"; 42 | 43 | // A Timestamp represents a point in time independent of any time zone or local 44 | // calendar, encoded as a count of seconds and fractions of seconds at 45 | // nanosecond resolution. The count is relative to an epoch at UTC midnight on 46 | // January 1, 1970, in the proleptic Gregorian calendar which extends the 47 | // Gregorian calendar backwards to year one. 48 | // 49 | // All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap 50 | // second table is needed for interpretation, using a [24-hour linear 51 | // smear](https://developers.google.com/time/smear). 52 | // 53 | // The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By 54 | // restricting to that range, we ensure that we can convert to and from [RFC 55 | // 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. 56 | // 57 | // # Examples 58 | // 59 | // Example 1: Compute Timestamp from POSIX `time()`. 60 | // 61 | // Timestamp timestamp; 62 | // timestamp.set_seconds(time(NULL)); 63 | // timestamp.set_nanos(0); 64 | // 65 | // Example 2: Compute Timestamp from POSIX `gettimeofday()`. 66 | // 67 | // struct timeval tv; 68 | // gettimeofday(&tv, NULL); 69 | // 70 | // Timestamp timestamp; 71 | // timestamp.set_seconds(tv.tv_sec); 72 | // timestamp.set_nanos(tv.tv_usec * 1000); 73 | // 74 | // Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. 75 | // 76 | // FILETIME ft; 77 | // GetSystemTimeAsFileTime(&ft); 78 | // UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; 79 | // 80 | // // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z 81 | // // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. 82 | // Timestamp timestamp; 83 | // timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); 84 | // timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); 85 | // 86 | // Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. 87 | // 88 | // long millis = System.currentTimeMillis(); 89 | // 90 | // Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) 91 | // .setNanos((int) ((millis % 1000) * 1000000)).build(); 92 | // 93 | // 94 | // Example 5: Compute Timestamp from current time in Python. 95 | // 96 | // timestamp = Timestamp() 97 | // timestamp.GetCurrentTime() 98 | // 99 | // # JSON Mapping 100 | // 101 | // In JSON format, the Timestamp type is encoded as a string in the 102 | // [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the 103 | // format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" 104 | // where {year} is always expressed using four digits while {month}, {day}, 105 | // {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional 106 | // seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), 107 | // are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone 108 | // is required. A proto3 JSON serializer should always use UTC (as indicated by 109 | // "Z") when printing the Timestamp type and a proto3 JSON parser should be 110 | // able to accept both UTC and other timezones (as indicated by an offset). 111 | // 112 | // For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 113 | // 01:30 UTC on January 15, 2017. 114 | // 115 | // In JavaScript, one can convert a Date object to this format using the 116 | // standard 117 | // [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) 118 | // method. In Python, a standard `datetime.datetime` object can be converted 119 | // to this format using 120 | // [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with 121 | // the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use 122 | // the Joda Time's [`ISODateTimeFormat.dateTime()`]( 123 | // http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D 124 | // ) to obtain a formatter capable of generating timestamps in this format. 125 | // 126 | // 127 | message Timestamp { 128 | // Represents seconds of UTC time since Unix epoch 129 | // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to 130 | // 9999-12-31T23:59:59Z inclusive. 131 | int64 seconds = 1; 132 | 133 | // Non-negative fractions of a second at nanosecond resolution. Negative 134 | // second values with fractions must still have non-negative nanos values 135 | // that count forward in time. Must be from 0 to 999,999,999 136 | // inclusive. 137 | int32 nanos = 2; 138 | } 139 | -------------------------------------------------------------------------------- /docker/google/protobuf/wrappers.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | // Wrappers for primitive (non-message) types. These types are useful 32 | // for embedding primitives in the `google.protobuf.Any` type and for places 33 | // where we need to distinguish between the absence of a primitive 34 | // typed field and its default value. 35 | // 36 | // These wrappers have no meaningful use within repeated fields as they lack 37 | // the ability to detect presence on individual elements. 38 | // These wrappers have no meaningful use within a map or a oneof since 39 | // individual entries of a map or fields of a oneof can already detect presence. 40 | 41 | syntax = "proto3"; 42 | 43 | package google.protobuf; 44 | 45 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 46 | option cc_enable_arenas = true; 47 | option go_package = "github.com/golang/protobuf/ptypes/wrappers"; 48 | option java_package = "com.google.protobuf"; 49 | option java_outer_classname = "WrappersProto"; 50 | option java_multiple_files = true; 51 | option objc_class_prefix = "GPB"; 52 | 53 | // Wrapper message for `double`. 54 | // 55 | // The JSON representation for `DoubleValue` is JSON number. 56 | message DoubleValue { 57 | // The double value. 58 | double value = 1; 59 | } 60 | 61 | // Wrapper message for `float`. 62 | // 63 | // The JSON representation for `FloatValue` is JSON number. 64 | message FloatValue { 65 | // The float value. 66 | float value = 1; 67 | } 68 | 69 | // Wrapper message for `int64`. 70 | // 71 | // The JSON representation for `Int64Value` is JSON string. 72 | message Int64Value { 73 | // The int64 value. 74 | int64 value = 1; 75 | } 76 | 77 | // Wrapper message for `uint64`. 78 | // 79 | // The JSON representation for `UInt64Value` is JSON string. 80 | message UInt64Value { 81 | // The uint64 value. 82 | uint64 value = 1; 83 | } 84 | 85 | // Wrapper message for `int32`. 86 | // 87 | // The JSON representation for `Int32Value` is JSON number. 88 | message Int32Value { 89 | // The int32 value. 90 | int32 value = 1; 91 | } 92 | 93 | // Wrapper message for `uint32`. 94 | // 95 | // The JSON representation for `UInt32Value` is JSON number. 96 | message UInt32Value { 97 | // The uint32 value. 98 | uint32 value = 1; 99 | } 100 | 101 | // Wrapper message for `bool`. 102 | // 103 | // The JSON representation for `BoolValue` is JSON `true` and `false`. 104 | message BoolValue { 105 | // The bool value. 106 | bool value = 1; 107 | } 108 | 109 | // Wrapper message for `string`. 110 | // 111 | // The JSON representation for `StringValue` is JSON string. 112 | message StringValue { 113 | // The string value. 114 | string value = 1; 115 | } 116 | 117 | // Wrapper message for `bytes`. 118 | // 119 | // The JSON representation for `BytesValue` is JSON string. 120 | message BytesValue { 121 | // The bytes value. 122 | bytes value = 1; 123 | } 124 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/append_command.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "append_command.h" 18 | #include "errors.h" 19 | #include "redis_protobuf.h" 20 | 21 | namespace sw { 22 | 23 | namespace redis { 24 | 25 | namespace pb { 26 | 27 | int AppendCommand::run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const { 28 | try { 29 | assert(ctx != nullptr); 30 | 31 | auto args = _parse_args(argv, argc); 32 | const auto &path = args.path; 33 | if (path.empty()) { 34 | throw Error("can only call append on array"); 35 | } 36 | 37 | auto key = api::open_key(ctx, args.key_name, api::KeyMode::WRITEONLY); 38 | assert(key); 39 | 40 | auto &m = RedisProtobuf::instance(); 41 | 42 | long long len = 0; 43 | if (!api::key_exists(key.get(), m.type())) { 44 | auto msg = m.proto_factory()->create(path.type()); 45 | 46 | assert(msg != nullptr); 47 | if (msg->GetTypeName() != path.type()) { 48 | throw Error("type mismatch"); 49 | } 50 | 51 | MutableFieldRef field(msg.get(), path); 52 | len = _append(field, args.elements); 53 | 54 | if (RedisModule_ModuleTypeSetValue(key.get(), 55 | m.type(), 56 | msg.get()) != REDISMODULE_OK) { 57 | throw Error("failed to set message"); 58 | } 59 | 60 | msg.release(); 61 | } else { 62 | auto *msg = api::get_msg_by_key(key.get()); 63 | assert(msg != nullptr); 64 | 65 | MutableFieldRef field(msg, path); 66 | // TODO: create a new message, and append to that message, then swap to this message. 67 | len = _append(field, args.elements); 68 | } 69 | 70 | RedisModule_ReplyWithLongLong(ctx, len); 71 | 72 | RedisModule_ReplicateVerbatim(ctx); 73 | 74 | return REDISMODULE_OK; 75 | } catch (const WrongArityError &err) { 76 | return RedisModule_WrongArity(ctx); 77 | } catch (const Error &err) { 78 | return api::reply_with_error(ctx, err); 79 | } 80 | 81 | return REDISMODULE_ERR; 82 | } 83 | 84 | AppendCommand::Args AppendCommand::_parse_args(RedisModuleString **argv, int argc) const { 85 | assert(argv != nullptr); 86 | 87 | if (argc < 5) { 88 | throw WrongArityError(); 89 | } 90 | 91 | Args args; 92 | args.key_name = argv[1]; 93 | args.path = Path(argv[2], argv[3]); 94 | args.elements.reserve(argc - 4); 95 | 96 | for (auto idx = 4; idx != argc; ++idx) { 97 | args.elements.emplace_back(argv[idx]); 98 | } 99 | 100 | return args; 101 | } 102 | 103 | long long AppendCommand::_append(MutableFieldRef &field, 104 | const std::vector &elements) const { 105 | if (field.is_array() && !field.is_array_element()) { 106 | for (const auto &ele : elements) { 107 | _append_arr(field, ele); 108 | } 109 | 110 | return field.size(); 111 | } else if (field.type() == gp::FieldDescriptor::CPPTYPE_STRING) { 112 | return _append_str(field, elements); 113 | } else { 114 | throw Error("not an array or string"); 115 | } 116 | } 117 | 118 | void AppendCommand::_append_arr(MutableFieldRef &field, const StringView &val) const { 119 | assert(field.is_array() && !field.is_array_element()); 120 | 121 | switch (field.type()) { 122 | case gp::FieldDescriptor::CPPTYPE_INT32: 123 | field.add_int32(util::sv_to_int32(val)); 124 | break; 125 | 126 | case gp::FieldDescriptor::CPPTYPE_INT64: 127 | field.add_int64(util::sv_to_int64(val)); 128 | break; 129 | 130 | case gp::FieldDescriptor::CPPTYPE_UINT32: 131 | field.add_uint32(util::sv_to_uint32(val)); 132 | break; 133 | 134 | case gp::FieldDescriptor::CPPTYPE_UINT64: 135 | field.add_uint64(util::sv_to_uint64(val)); 136 | break; 137 | 138 | case gp::FieldDescriptor::CPPTYPE_DOUBLE: 139 | field.add_double(util::sv_to_double(val)); 140 | break; 141 | 142 | case gp::FieldDescriptor::CPPTYPE_FLOAT: 143 | field.add_float(util::sv_to_float(val)); 144 | break; 145 | 146 | case gp::FieldDescriptor::CPPTYPE_BOOL: 147 | field.add_bool(util::sv_to_bool(val)); 148 | break; 149 | 150 | case gp::FieldDescriptor::CPPTYPE_ENUM: 151 | field.add_enum(util::sv_to_int32(val)); 152 | break; 153 | 154 | case gp::FieldDescriptor::CPPTYPE_STRING: 155 | field.add_string(util::sv_to_string(val)); 156 | break; 157 | 158 | case gp::FieldDescriptor::CPPTYPE_MESSAGE: 159 | _add_msg(field, val); 160 | break; 161 | 162 | default: 163 | throw Error("unknown type"); 164 | } 165 | } 166 | 167 | long long AppendCommand::_append_str(MutableFieldRef &field, 168 | const std::vector &elements) const { 169 | std::string str; 170 | for (const auto &ele : elements) { 171 | str += std::string(ele.data(), ele.size()); 172 | } 173 | 174 | if (field.is_array_element()) { 175 | str = field.get_repeated_string() + str; 176 | field.set_repeated_string(str); 177 | } else { 178 | // TODO: map element 179 | str = field.get_string() + str; 180 | field.set_string(str); 181 | } 182 | 183 | return str.size(); 184 | } 185 | 186 | void AppendCommand::_add_msg(MutableFieldRef &field, const StringView &val) const { 187 | auto msg = RedisProtobuf::instance().proto_factory()->create(field.msg_type(), val); 188 | assert(msg); 189 | 190 | field.add_msg(*msg); 191 | } 192 | 193 | } 194 | 195 | } 196 | 197 | } 198 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/append_command.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_APPEND_COMMANDS_H 18 | #define SEWENEW_REDISPROTOBUF_APPEND_COMMANDS_H 19 | 20 | #include "module_api.h" 21 | #include 22 | #include 23 | #include "utils.h" 24 | #include "field_ref.h" 25 | 26 | namespace sw { 27 | 28 | namespace redis { 29 | 30 | namespace pb { 31 | 32 | // command: PB.APPEND key type path element [element, element...] 33 | // return: Integer reply: return the length of the array after the append operations. 34 | // Or return the length of the string after the append operations. 35 | // error: If the path doesn't exist, or the corresponding field is not an array, or 36 | // a string, return an error reply. 37 | class AppendCommand { 38 | public: 39 | int run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const; 40 | 41 | private: 42 | struct Args { 43 | RedisModuleString *key_name; 44 | Path path; 45 | std::vector elements; 46 | }; 47 | 48 | Args _parse_args(RedisModuleString **argv, int argc) const; 49 | 50 | long long _append(MutableFieldRef &field, const std::vector &elements) const; 51 | 52 | void _append_arr(MutableFieldRef &field, const StringView &val) const; 53 | 54 | long long _append_str(MutableFieldRef &field, const std::vector &elements) const; 55 | 56 | void _add_msg(MutableFieldRef &field, const StringView &val) const; 57 | }; 58 | 59 | } 60 | 61 | } 62 | 63 | } 64 | 65 | #endif // end SEWENEW_REDISPROTOBUF_APPEND_COMMANDS_H 66 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/clear_command.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "clear_command.h" 18 | #include "errors.h" 19 | #include "redis_protobuf.h" 20 | 21 | namespace sw { 22 | 23 | namespace redis { 24 | 25 | namespace pb { 26 | 27 | int ClearCommand::run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const { 28 | try { 29 | assert(ctx != nullptr); 30 | 31 | auto args = _parse_args(argv, argc); 32 | 33 | auto key = api::open_key(ctx, args.key_name, api::KeyMode::READONLY); 34 | if (!api::key_exists(key.get(), RedisProtobuf::instance().type())) { 35 | RedisModule_ReplyWithLongLong(ctx, 0); 36 | } else { 37 | auto *msg = api::get_msg_by_key(key.get()); 38 | assert(msg != nullptr); 39 | 40 | if (msg->GetTypeName() != args.path.type()) { 41 | throw Error("type mismatch"); 42 | } 43 | 44 | _clear(*msg, args.path); 45 | 46 | RedisModule_ReplyWithLongLong(ctx, 1); 47 | } 48 | 49 | RedisModule_ReplicateVerbatim(ctx); 50 | 51 | return REDISMODULE_OK; 52 | } catch (const WrongArityError &err) { 53 | return RedisModule_WrongArity(ctx); 54 | } catch (const Error &err) { 55 | return api::reply_with_error(ctx, err); 56 | } 57 | 58 | return REDISMODULE_ERR; 59 | } 60 | 61 | ClearCommand::Args ClearCommand::_parse_args(RedisModuleString **argv, int argc) const { 62 | assert(argv != nullptr); 63 | 64 | if (argc != 3 && argc != 4) { 65 | throw WrongArityError(); 66 | } 67 | 68 | Path path; 69 | if (argc == 3) { 70 | path = Path(argv[2]); 71 | } else { 72 | path = Path(argv[2], argv[3]); 73 | } 74 | return {argv[1], std::move(path)}; 75 | } 76 | 77 | void ClearCommand::_clear(gp::Message &msg, const Path &path) const { 78 | if (path.empty()) { 79 | // Clear the message. 80 | msg.Clear(); 81 | } else { 82 | // Clear a field. 83 | MutableFieldRef field(&msg, path); 84 | field.clear(); 85 | } 86 | } 87 | 88 | } 89 | 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/clear_command.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_CLEAR_COMMANDS_H 18 | #define SEWENEW_REDISPROTOBUF_CLEAR_COMMANDS_H 19 | 20 | #include "module_api.h" 21 | #include 22 | #include "utils.h" 23 | #include "field_ref.h" 24 | 25 | namespace sw { 26 | 27 | namespace redis { 28 | 29 | namespace pb { 30 | 31 | // command: PB.CLEAR key type [path] 32 | // return: Integer reply: If the key exist, return 1. Otherwise, return 0. 33 | // error: If the type doesn't match the protobuf message type of the key, 34 | // or the path doesn't exist, return an error reply. 35 | class ClearCommand { 36 | public: 37 | int run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const; 38 | 39 | private: 40 | struct Args { 41 | RedisModuleString *key_name; 42 | Path path; 43 | }; 44 | 45 | Args _parse_args(RedisModuleString **argv, int argc) const; 46 | 47 | void _clear(gp::Message &msg, const Path &path) const; 48 | }; 49 | 50 | } 51 | 52 | } 53 | 54 | } 55 | 56 | #endif // end SEWENEW_REDISPROTOBUF_CLEAR_COMMANDS_H 57 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/commands.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "commands.h" 18 | #include "set_command.h" 19 | #include "get_command.h" 20 | #include "type_command.h" 21 | #include "clear_command.h" 22 | #include "len_command.h" 23 | #include "append_command.h" 24 | #include "del_command.h" 25 | #include "schema_command.h" 26 | #include "merge_command.h" 27 | #include "import_command.h" 28 | #include "last_import_command.h" 29 | 30 | namespace sw { 31 | 32 | namespace redis { 33 | 34 | namespace pb { 35 | 36 | namespace cmd { 37 | 38 | void create_commands(RedisModuleCtx *ctx) { 39 | if (RedisModule_CreateCommand(ctx, 40 | "PB.TYPE", 41 | [](RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 42 | TypeCommand cmd; 43 | return cmd.run(ctx, argv, argc); 44 | }, 45 | "readonly", 46 | 1, 47 | 1, 48 | 1) == REDISMODULE_ERR) { 49 | throw Error("failed to create PB.TYPE command"); 50 | } 51 | 52 | if (RedisModule_CreateCommand(ctx, 53 | "PB.SET", 54 | [](RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 55 | SetCommand cmd; 56 | return cmd.run(ctx, argv, argc); 57 | }, 58 | "write deny-oom", 59 | 1, 60 | 1, 61 | 1) == REDISMODULE_ERR) { 62 | throw Error("fail to create PB.SET command"); 63 | } 64 | 65 | if (RedisModule_CreateCommand(ctx, 66 | "PB.GET", 67 | [](RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 68 | GetCommand cmd; 69 | return cmd.run(ctx, argv, argc); 70 | }, 71 | "readonly", 72 | 1, 73 | 1, 74 | 1) == REDISMODULE_ERR) { 75 | throw Error("failed to create PB.GET command"); 76 | } 77 | 78 | if (RedisModule_CreateCommand(ctx, 79 | "PB.CLEAR", 80 | [](RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 81 | ClearCommand cmd; 82 | return cmd.run(ctx, argv, argc); 83 | }, 84 | "write deny-oom", 85 | 1, 86 | 1, 87 | 1) == REDISMODULE_ERR) { 88 | throw Error("fail to create PB.CLEAR command"); 89 | } 90 | 91 | if (RedisModule_CreateCommand(ctx, 92 | "PB.LEN", 93 | [](RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 94 | LenCommand cmd; 95 | return cmd.run(ctx, argv, argc); 96 | }, 97 | "readonly", 98 | 1, 99 | 1, 100 | 1) == REDISMODULE_ERR) { 101 | throw Error("failed to create PB.LEN command"); 102 | } 103 | 104 | if (RedisModule_CreateCommand(ctx, 105 | "PB.APPEND", 106 | [](RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 107 | AppendCommand cmd; 108 | return cmd.run(ctx, argv, argc); 109 | }, 110 | "write deny-oom", 111 | 1, 112 | 1, 113 | 1) == REDISMODULE_ERR) { 114 | throw Error("fail to create PB.APPEND command"); 115 | } 116 | 117 | if (RedisModule_CreateCommand(ctx, 118 | "PB.DEL", 119 | [](RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 120 | DelCommand cmd; 121 | return cmd.run(ctx, argv, argc); 122 | }, 123 | "write deny-oom", 124 | 1, 125 | 1, 126 | 1) == REDISMODULE_ERR) { 127 | throw Error("fail to create PB.DEL command"); 128 | } 129 | 130 | if (RedisModule_CreateCommand(ctx, 131 | "PB.SCHEMA", 132 | [](RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 133 | SchemaCommand cmd; 134 | return cmd.run(ctx, argv, argc); 135 | }, 136 | "readonly getkeys-api", 137 | 1, 138 | 1, 139 | 1) == REDISMODULE_ERR) { 140 | throw Error("failed to create PB.SCHEMA command"); 141 | } 142 | 143 | if (RedisModule_CreateCommand(ctx, 144 | "PB.MERGE", 145 | [](RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 146 | MergeCommand cmd; 147 | return cmd.run(ctx, argv, argc); 148 | }, 149 | "write deny-oom", 150 | 1, 151 | 1, 152 | 1) == REDISMODULE_ERR) { 153 | throw Error("fail to create PB.MERGE command"); 154 | } 155 | 156 | if (RedisModule_CreateCommand(ctx, 157 | "PB.IMPORT", 158 | [](RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 159 | ImportCommand cmd; 160 | return cmd.run(ctx, argv, argc); 161 | }, 162 | "write deny-oom", 163 | 1, 164 | 1, 165 | 1) == REDISMODULE_ERR) { 166 | throw Error("fail to create PB.IMPORT command"); 167 | } 168 | 169 | if (RedisModule_CreateCommand(ctx, 170 | "PB.LASTIMPORT", 171 | [](RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 172 | LastImportCommand cmd; 173 | return cmd.run(ctx, argv, argc); 174 | }, 175 | "readonly", 176 | 1, 177 | 1, 178 | 1) == REDISMODULE_ERR) { 179 | throw Error("failed to create PB.LASTIMPORT command"); 180 | } 181 | } 182 | 183 | } 184 | 185 | } 186 | 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/commands.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_COMMANDS_H 18 | #define SEWENEW_REDISPROTOBUF_COMMANDS_H 19 | 20 | #include "module_api.h" 21 | 22 | namespace sw { 23 | 24 | namespace redis { 25 | 26 | namespace pb { 27 | 28 | namespace cmd { 29 | 30 | void create_commands(RedisModuleCtx *ctx); 31 | 32 | } 33 | 34 | } 35 | 36 | } 37 | 38 | } 39 | 40 | #endif // end SEWENEW_REDISPROTOBUF_COMMANDS_H 41 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/del_command.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "del_command.h" 18 | #include "errors.h" 19 | #include "redis_protobuf.h" 20 | 21 | namespace sw { 22 | 23 | namespace redis { 24 | 25 | namespace pb { 26 | 27 | int DelCommand::run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const { 28 | try { 29 | assert(ctx != nullptr); 30 | 31 | auto args = _parse_args(argv, argc); 32 | 33 | auto key = api::open_key(ctx, args.key_name, api::KeyMode::WRITEONLY); 34 | assert(key); 35 | 36 | if (!api::key_exists(key.get(), RedisProtobuf::instance().type())) { 37 | RedisModule_ReplyWithLongLong(ctx, 0); 38 | } else { 39 | auto *msg = api::get_msg_by_key(key.get()); 40 | assert(msg != nullptr); 41 | 42 | const auto &path = args.path; 43 | if (msg->GetTypeName() != path.type()) { 44 | throw Error("type mismatch"); 45 | } 46 | 47 | if (path.empty()) { 48 | // Delete key. 49 | RedisModule_DeleteKey(key.get()); 50 | } else { 51 | // Delete an item from array or map. 52 | _del(*msg, path); 53 | } 54 | 55 | RedisModule_ReplyWithLongLong(ctx, 1); 56 | } 57 | 58 | RedisModule_ReplicateVerbatim(ctx); 59 | 60 | return REDISMODULE_OK; 61 | } catch (const WrongArityError &err) { 62 | return RedisModule_WrongArity(ctx); 63 | } catch (const Error &err) { 64 | return api::reply_with_error(ctx, err); 65 | } 66 | 67 | return REDISMODULE_ERR; 68 | } 69 | 70 | DelCommand::Args DelCommand::_parse_args(RedisModuleString **argv, int argc) const { 71 | assert(argv != nullptr); 72 | 73 | if (argc != 3 && argc != 4) { 74 | throw WrongArityError(); 75 | } 76 | 77 | Path path; 78 | if (argc == 3) { 79 | path = Path(argv[2]); 80 | } else { 81 | path = Path(argv[2], argv[3]); 82 | } 83 | 84 | return {argv[1], std::move(path)}; 85 | } 86 | 87 | void DelCommand::_del(gp::Message &msg, const Path &path) const { 88 | MutableFieldRef field(&msg, path); 89 | 90 | if (!field.is_array_element()) { 91 | // TODO: support map element 92 | throw Error("not an array or map"); 93 | } 94 | 95 | field.del(); 96 | } 97 | 98 | } 99 | 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/del_command.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_DEL_COMMANDS_H 18 | #define SEWENEW_REDISPROTOBUF_DEL_COMMANDS_H 19 | 20 | #include "module_api.h" 21 | #include 22 | #include 23 | #include "utils.h" 24 | #include "field_ref.h" 25 | 26 | namespace sw { 27 | 28 | namespace redis { 29 | 30 | namespace pb { 31 | 32 | // command: PB.DEL key type [path] 33 | // return: Integer reply: return 1, if the key exists. 0, otherwise. 34 | // error: If the path doesn't exist, or the corresponding field is not an array, 35 | // or map or the message itself, return an error reply. 36 | class DelCommand { 37 | public: 38 | int run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const; 39 | 40 | private: 41 | struct Args { 42 | RedisModuleString *key_name; 43 | Path path; 44 | }; 45 | 46 | Args _parse_args(RedisModuleString **argv, int argc) const; 47 | 48 | void _del(gp::Message &msg, const Path &path) const; 49 | }; 50 | 51 | } 52 | 53 | } 54 | 55 | } 56 | 57 | #endif // end SEWENEW_REDISPROTOBUF_DEL_COMMANDS_H 58 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/errors.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_ERRORS_H 18 | #define SEWENEW_REDISPROTOBUF_ERRORS_H 19 | 20 | #include 21 | #include 22 | 23 | namespace sw { 24 | 25 | namespace redis { 26 | 27 | namespace pb { 28 | 29 | class Error : public std::exception { 30 | public: 31 | explicit Error(const std::string &msg) : _msg(msg) {} 32 | 33 | Error(const Error &) = default; 34 | Error& operator=(const Error &) = default; 35 | 36 | Error(Error &&) = default; 37 | Error& operator=(Error &&) = default; 38 | 39 | virtual ~Error() = default; 40 | 41 | virtual const char* what() const noexcept { 42 | return _msg.data(); 43 | } 44 | 45 | private: 46 | std::string _msg; 47 | }; 48 | 49 | class WrongTypeError : public Error { 50 | public: 51 | explicit WrongTypeError(const std::string &msg) : Error(msg) {} 52 | 53 | WrongTypeError(const WrongTypeError &) = default; 54 | WrongTypeError& operator=(const WrongTypeError &) = default; 55 | 56 | WrongTypeError(WrongTypeError &&) = default; 57 | WrongTypeError& operator=(WrongTypeError &&) = default; 58 | 59 | virtual ~WrongTypeError() = default; 60 | }; 61 | 62 | class WrongArityError : public Error { 63 | public: 64 | WrongArityError() : Error("WrongArity") {} 65 | 66 | WrongArityError(const WrongArityError &) = default; 67 | WrongArityError& operator=(const WrongArityError &) = default; 68 | 69 | WrongArityError(WrongArityError &&) = default; 70 | WrongArityError& operator=(WrongArityError &&) = default; 71 | 72 | virtual ~WrongArityError() = default; 73 | }; 74 | 75 | class MapKeyNotFoundError : public Error { 76 | public: 77 | explicit MapKeyNotFoundError(const std::string &key) : Error("key not found: " + key) {} 78 | 79 | MapKeyNotFoundError(const MapKeyNotFoundError &) = default; 80 | MapKeyNotFoundError& operator=(const MapKeyNotFoundError &) = default; 81 | 82 | MapKeyNotFoundError(MapKeyNotFoundError &&) = default; 83 | MapKeyNotFoundError& operator=(MapKeyNotFoundError &&) = default; 84 | 85 | virtual ~MapKeyNotFoundError() = default; 86 | }; 87 | 88 | } 89 | 90 | } 91 | 92 | } 93 | 94 | #endif // end SEWENEW_REDISPROTOBUF_ERRORS_H 95 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/get_command.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_GET_COMMANDS_H 18 | #define SEWENEW_REDISPROTOBUF_GET_COMMANDS_H 19 | 20 | #include "module_api.h" 21 | #include "utils.h" 22 | #include "field_ref.h" 23 | 24 | namespace sw { 25 | 26 | namespace redis { 27 | 28 | namespace pb { 29 | 30 | // command: PB.GET key [--FORMAT BINARY|JSON] type [path] 31 | // return: If no path is specified, return the protobuf message of the key 32 | // as a bulk string reply. If path is specified, return the value 33 | // of the field specified with the path, and the reply type depends 34 | // on the definition of the protobuf. If the key doesn't exist, 35 | // return a nil reply. 36 | // error: If the path doesn't exist, or type mismatch return an error reply. 37 | class GetCommand { 38 | public: 39 | int run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const; 40 | 41 | private: 42 | struct Args { 43 | RedisModuleString *key_name; 44 | 45 | enum class Format { 46 | BINARY = 0, 47 | JSON, 48 | NONE 49 | }; 50 | 51 | Format format = Format::NONE; 52 | 53 | Path path; 54 | }; 55 | 56 | Args _parse_args(RedisModuleString **argv, int argc) const; 57 | 58 | int _parse_opts(RedisModuleString **argv, int argc, Args &args) const; 59 | 60 | Args::Format _parse_format(const StringView &format) const; 61 | 62 | void _reply_with_nil(RedisModuleCtx *ctx) const; 63 | 64 | void _reply_with_msg(RedisModuleCtx *ctx, 65 | gp::Message &msg, 66 | const Args &args) const; 67 | 68 | void _get_scalar_field(RedisModuleCtx *ctx, 69 | const ConstFieldRef &field, 70 | Args::Format format) const; 71 | 72 | void _get_array_element(RedisModuleCtx *ctx, 73 | const ConstFieldRef &field, 74 | Args::Format format) const; 75 | 76 | void _get_array(RedisModuleCtx *ctx, 77 | const ConstFieldRef &field, 78 | Args::Format format) const; 79 | 80 | void _get_map_element(RedisModuleCtx *ctx, 81 | const ConstFieldRef &field, 82 | Args::Format format) const; 83 | 84 | void _get_map(RedisModuleCtx *ctx, 85 | const ConstFieldRef &field, 86 | Args::Format format) const; 87 | 88 | void _get_map_kv(RedisModuleCtx *ctx, 89 | const ConstFieldRef &field, 90 | Args::Format format, 91 | const gp::MapKey &key, 92 | const gp::MapValueRef &value) const; 93 | 94 | void _get_msg(RedisModuleCtx *ctx, 95 | const gp::Message &msg, 96 | Args::Format format) const; 97 | 98 | void _get_field(RedisModuleCtx *ctx, 99 | const ConstFieldRef &field, 100 | Args::Format format) const; 101 | }; 102 | 103 | } 104 | 105 | } 106 | 107 | } 108 | 109 | #endif // end SEWENEW_REDISPROTOBUF_GET_COMMANDS_H 110 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/import_command.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "import_command.h" 18 | #include 19 | #include "utils.h" 20 | #include "errors.h" 21 | #include "redis_protobuf.h" 22 | 23 | namespace sw { 24 | 25 | namespace redis { 26 | 27 | namespace pb { 28 | 29 | int ImportCommand::run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const { 30 | try { 31 | assert(ctx != nullptr); 32 | 33 | auto args = _parse_args(argv, argc); 34 | 35 | auto &m= RedisProtobuf::instance(); 36 | m.proto_factory()->load(args.filename, args.content); 37 | 38 | RedisModule_ReplicateVerbatim(ctx); 39 | 40 | RedisModule_ReplyWithSimpleString(ctx, "OK"); 41 | 42 | return REDISMODULE_OK; 43 | } catch (const WrongArityError &err) { 44 | return RedisModule_WrongArity(ctx); 45 | } catch (const Error &err) { 46 | return api::reply_with_error(ctx, err); 47 | } 48 | 49 | return REDISMODULE_ERR; 50 | } 51 | 52 | auto ImportCommand::_parse_args(RedisModuleString **argv, int argc) const -> Args { 53 | assert(argv != nullptr); 54 | 55 | if (argc != 3) { 56 | throw WrongArityError(); 57 | } 58 | 59 | Args args; 60 | args.filename = util::sv_to_string(argv[1]); 61 | args.content = util::sv_to_string(argv[2]); 62 | 63 | return args; 64 | } 65 | 66 | } 67 | 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/import_command.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_IMPORT_COMMAND_H 18 | #define SEWENEW_REDISPROTOBUF_IMPORT_COMMAND_H 19 | 20 | #include "module_api.h" 21 | #include 22 | #include 23 | #include 24 | #include "utils.h" 25 | #include "field_ref.h" 26 | 27 | namespace sw { 28 | 29 | namespace redis { 30 | 31 | namespace pb { 32 | 33 | // command: PB.IMPORT file-path content 34 | // return: OK status reply. 35 | // error: If failing to import, return an error reply. 36 | class ImportCommand { 37 | public: 38 | int run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const; 39 | 40 | private: 41 | struct Args { 42 | std::string filename; 43 | std::string content; 44 | }; 45 | 46 | Args _parse_args(RedisModuleString **argv, int argc) const; 47 | }; 48 | 49 | } 50 | 51 | } 52 | 53 | } 54 | 55 | #endif // end SEWENEW_REDISPROTOBUF_IMPORT_COMMAND_H 56 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/last_import_command.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "last_import_command.h" 18 | #include 19 | #include "utils.h" 20 | #include "errors.h" 21 | #include "redis_protobuf.h" 22 | 23 | namespace sw { 24 | 25 | namespace redis { 26 | 27 | namespace pb { 28 | 29 | int LastImportCommand::run(RedisModuleCtx *ctx, RedisModuleString ** /*argv*/, int /*argc*/) const { 30 | try { 31 | assert(ctx != nullptr); 32 | 33 | auto &m = RedisProtobuf::instance(); 34 | auto last_loaded_files = m.proto_factory()->last_loaded(); 35 | 36 | RedisModule_ReplyWithArray(ctx, last_loaded_files.size()); 37 | 38 | for (const auto &ele : last_loaded_files) { 39 | const auto &filename = ele.first; 40 | const auto &status = ele.second; 41 | 42 | RedisModule_ReplyWithArray(ctx, 2); 43 | 44 | RedisModule_ReplyWithStringBuffer(ctx, filename.data(), filename.size()); 45 | RedisModule_ReplyWithStringBuffer(ctx, status.data(), status.size()); 46 | } 47 | 48 | return REDISMODULE_OK; 49 | } catch (const WrongArityError &err) { 50 | return RedisModule_WrongArity(ctx); 51 | } catch (const Error &err) { 52 | return api::reply_with_error(ctx, err); 53 | } 54 | 55 | return REDISMODULE_ERR; 56 | } 57 | 58 | } 59 | 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/last_import_command.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_LAST_IMPORT_COMMAND_H 18 | #define SEWENEW_REDISPROTOBUF_LAST_IMPORT_COMMAND_H 19 | 20 | #include "module_api.h" 21 | #include 22 | #include 23 | #include 24 | #include "utils.h" 25 | #include "field_ref.h" 26 | 27 | namespace sw { 28 | 29 | namespace redis { 30 | 31 | namespace pb { 32 | 33 | // command: PB.LASTIMPORT 34 | // return: Array reply of the status of last imported proto files. 35 | // note: This command returns status of all imported proto files since last 36 | // call to this command. Once this command is called, the underlying 37 | // records will be cleared. 38 | class LastImportCommand { 39 | public: 40 | int run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const; 41 | }; 42 | 43 | } 44 | 45 | } 46 | 47 | } 48 | 49 | #endif // end SEWENEW_REDISPROTOBUF_LAST_IMPORT_COMMAND_H 50 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/len_command.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "len_command.h" 18 | #include "errors.h" 19 | #include "redis_protobuf.h" 20 | #include "utils.h" 21 | #include "field_ref.h" 22 | 23 | namespace sw { 24 | 25 | namespace redis { 26 | 27 | namespace pb { 28 | 29 | int LenCommand::run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const { 30 | try { 31 | assert(ctx != nullptr); 32 | 33 | auto args = _parse_args(argv, argc); 34 | 35 | auto key = api::open_key(ctx, args.key_name, api::KeyMode::READONLY); 36 | if (!api::key_exists(key.get(), RedisProtobuf::instance().type())) { 37 | RedisModule_ReplyWithLongLong(ctx, 0); 38 | } else { 39 | auto *msg = api::get_msg_by_key(key.get()); 40 | assert(msg != nullptr); 41 | 42 | auto len = _len(*msg, args.path); 43 | RedisModule_ReplyWithLongLong(ctx, len); 44 | } 45 | 46 | return REDISMODULE_OK; 47 | } catch (const WrongArityError &err) { 48 | return RedisModule_WrongArity(ctx); 49 | } catch (const Error &err) { 50 | return api::reply_with_error(ctx, err); 51 | } 52 | } 53 | 54 | LenCommand::Args LenCommand::_parse_args(RedisModuleString **argv, int argc) const { 55 | assert(argv != nullptr); 56 | 57 | if (argc != 3 && argc != 4) { 58 | throw WrongArityError(); 59 | } 60 | 61 | Path path; 62 | if (argc == 3) { 63 | path = Path(argv[2]); 64 | } else { 65 | path = Path(argv[2], argv[3]); 66 | } 67 | 68 | return {argv[1], std::move(path)}; 69 | } 70 | 71 | long long LenCommand::_len(gp::Message &msg, const Path &path) const { 72 | if (msg.GetTypeName() != path.type()) { 73 | throw Error("type mismatch"); 74 | } 75 | 76 | if (path.empty()) { 77 | // Return the length of the message. 78 | return msg.ByteSizeLong(); 79 | } 80 | 81 | return _len(ConstFieldRef(&msg, path)); 82 | } 83 | 84 | long long LenCommand::_len(const ConstFieldRef &field) const { 85 | if (field.is_map() || field.is_array()) { 86 | return field.size(); 87 | } 88 | 89 | // Scalar type. 90 | switch (field.type()) { 91 | case gp::FieldDescriptor::CPPTYPE_MESSAGE: 92 | return field.get_msg().ByteSizeLong(); 93 | 94 | case gp::FieldDescriptor::CPPTYPE_STRING: 95 | // TODO: use GetStringReference instead. 96 | return field.get_string().size(); 97 | 98 | default: 99 | throw Error("cannot get length of this field"); 100 | } 101 | } 102 | 103 | } 104 | 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/len_command.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_LEN_COMMANDS_H 18 | #define SEWENEW_REDISPROTOBUF_LEN_COMMANDS_H 19 | 20 | #include "module_api.h" 21 | #include "utils.h" 22 | #include "field_ref.h" 23 | 24 | namespace sw { 25 | 26 | namespace redis { 27 | 28 | namespace pb { 29 | 30 | // command: PB.LEN key type [path] 31 | // return: Integer reply: If the specified path is a string, return the 32 | // length of the string in bytes; if the field is an array or a map, 33 | // return the size of the array or map. If the field is a message, 34 | // return the size of the message in bytes, i.e. Message::ByteSizeLong. 35 | // If the key doesn't exist, return 0. 36 | // error: If the path doesn't exist, or the corresponding field is not a 37 | // message or a string or an array or a map, return an error reply. 38 | class LenCommand { 39 | public: 40 | int run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const; 41 | 42 | private: 43 | struct Args { 44 | RedisModuleString *key_name; 45 | Path path; 46 | }; 47 | 48 | Args _parse_args(RedisModuleString **argv, int argc) const; 49 | 50 | long long _len(gp::Message &msg, const Path &path) const; 51 | 52 | long long _len(const ConstFieldRef &field) const; 53 | }; 54 | 55 | } 56 | 57 | } 58 | 59 | } 60 | 61 | #endif // end SEWENEW_REDISPROTOBUF_LEN_COMMANDS_H 62 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/merge_command.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "merge_command.h" 18 | #include "errors.h" 19 | #include "redis_protobuf.h" 20 | #include "utils.h" 21 | #include "field_ref.h" 22 | #include "set_command.h" 23 | 24 | namespace sw { 25 | 26 | namespace redis { 27 | 28 | namespace pb { 29 | 30 | int MergeCommand::run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const { 31 | try { 32 | assert(ctx != nullptr); 33 | 34 | auto args = _parse_args(argv, argc); 35 | 36 | auto key = api::open_key(ctx, args.key_name, api::KeyMode::WRITEONLY); 37 | if (!api::key_exists(key.get(), RedisProtobuf::instance().type())) { 38 | SetCommand set_cmd; 39 | set_cmd._run(ctx, argv, argc); 40 | 41 | return RedisModule_ReplyWithLongLong(ctx, 0); 42 | } 43 | 44 | auto *msg = api::get_msg_by_key(key.get()); 45 | assert(msg != nullptr); 46 | 47 | _merge(args, *msg); 48 | 49 | RedisModule_ReplyWithLongLong(ctx, 1); 50 | 51 | RedisModule_ReplicateVerbatim(ctx); 52 | 53 | return REDISMODULE_OK; 54 | } catch (const WrongArityError &err) { 55 | return RedisModule_WrongArity(ctx); 56 | } catch (const Error &err) { 57 | return api::reply_with_error(ctx, err); 58 | } 59 | 60 | return REDISMODULE_ERR; 61 | } 62 | 63 | MergeCommand::Args MergeCommand::_parse_args(RedisModuleString **argv, int argc) const { 64 | assert(argv != nullptr); 65 | 66 | if (argc != 4 && argc != 5) { 67 | throw WrongArityError(); 68 | } 69 | 70 | Path path; 71 | StringView val; 72 | if (argc == 4) { 73 | path = Path(argv[2]); 74 | val = StringView(argv[3]); 75 | } else { 76 | path = Path(argv[2], argv[3]); 77 | val = StringView(argv[4]); 78 | } 79 | 80 | return {argv[1], std::move(path), std::move(val)}; 81 | } 82 | 83 | void MergeCommand::_merge(const Args &args, gp::Message &msg) const { 84 | const auto &path = args.path; 85 | if (path.empty()) { 86 | _merge_msg(path.type(), args.val, msg); 87 | } else { 88 | _merge_sub_msg(path, args.val, msg); 89 | } 90 | } 91 | 92 | void MergeCommand::_merge_msg(const std::string &type, 93 | const StringView &val, 94 | gp::Message &msg) const { 95 | if (type != msg.GetTypeName()) { 96 | throw Error("type mismatch"); 97 | } 98 | 99 | auto other = RedisProtobuf::instance().proto_factory()->create(type, val); 100 | assert(other); 101 | 102 | msg.MergeFrom(*other); 103 | } 104 | 105 | void MergeCommand::_merge_sub_msg(const Path &path, 106 | const StringView &val, 107 | gp::Message &msg) const { 108 | MutableFieldRef field(&msg, path); 109 | auto sub_msg = RedisProtobuf::instance().proto_factory()->create(field.msg_type(), val); 110 | assert(sub_msg); 111 | 112 | field.merge(*sub_msg); 113 | } 114 | 115 | } 116 | 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/merge_command.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_MERGE_COMMANDS_H 18 | #define SEWENEW_REDISPROTOBUF_MERGE_COMMANDS_H 19 | 20 | #include "module_api.h" 21 | #include 22 | #include "utils.h" 23 | #include "field_ref.h" 24 | 25 | namespace sw { 26 | 27 | namespace redis { 28 | 29 | namespace pb { 30 | 31 | // command: PB.MERGE key type [path] value 32 | // return: Integer reply: If the key exists, return 1. Otherwise, return 0. 33 | // If key doesn't exist, this command behaves as PB.SET. 34 | // error: If the type doesn't match the protobuf message type of the key, 35 | // or path doesn't exist, return an error reply. 36 | class MergeCommand { 37 | public: 38 | int run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const; 39 | 40 | private: 41 | struct Args { 42 | RedisModuleString *key_name; 43 | Path path; 44 | StringView val; 45 | }; 46 | 47 | Args _parse_args(RedisModuleString **argv, int argc) const; 48 | 49 | void _merge(const Args &args, gp::Message &msg) const; 50 | 51 | void _merge_msg(const std::string &type, const StringView &val, gp::Message &msg) const; 52 | 53 | void _merge_sub_msg(const Path &path, const StringView &val, gp::Message &msg) const; 54 | }; 55 | 56 | } 57 | 58 | } 59 | 60 | } 61 | 62 | #endif // end SEWENEW_REDISPROTOBUF_MERGE_COMMANDS_H 63 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/module_api.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "module_api.h" 18 | #include 19 | 20 | namespace sw { 21 | 22 | namespace redis { 23 | 24 | namespace pb { 25 | 26 | namespace api { 27 | 28 | RedisKey open_key(RedisModuleCtx *ctx, RedisModuleString *name, KeyMode key_mode) { 29 | if (name == nullptr) { 30 | throw Error("cannot open key with a null name"); 31 | } 32 | 33 | int mode = 0; 34 | switch (key_mode) { 35 | case KeyMode::READONLY: 36 | mode = REDISMODULE_READ; 37 | break; 38 | 39 | case KeyMode::WRITEONLY: 40 | mode = REDISMODULE_WRITE; 41 | break; 42 | 43 | case KeyMode::READWRITE: 44 | mode = REDISMODULE_READ | REDISMODULE_WRITE; 45 | break; 46 | 47 | default: 48 | assert(false); 49 | } 50 | 51 | return RedisKey(static_cast(RedisModule_OpenKey(ctx, name, mode))); 52 | } 53 | 54 | bool key_exists(RedisModuleKey *key, RedisModuleType *key_type) { 55 | // key can be nullptr. 56 | auto type = RedisModule_KeyType(key); 57 | if (type == REDISMODULE_KEYTYPE_EMPTY) { 58 | return false; 59 | } 60 | 61 | if (RedisModule_ModuleTypeGetType(key) == key_type) { 62 | return true; 63 | } 64 | 65 | throw WrongTypeError(REDISMODULE_ERRORMSG_WRONGTYPE); 66 | } 67 | 68 | int reply_with_error(RedisModuleCtx *ctx, const Error &err) { 69 | auto msg = std::string("ERR ") + err.what(); 70 | 71 | return RedisModule_ReplyWithError(ctx, msg.data()); 72 | } 73 | 74 | google::protobuf::Message* get_msg_by_key(RedisModuleKey *key) { 75 | auto *msg = static_cast(RedisModule_ModuleTypeGetValue(key)); 76 | if (msg == nullptr) { 77 | throw Error("failed to get message by key"); 78 | } 79 | 80 | return msg; 81 | } 82 | 83 | } 84 | 85 | } 86 | 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/module_api.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_MODULE_API_H 18 | #define SEWENEW_REDISPROTOBUF_MODULE_API_H 19 | 20 | #ifdef __cplusplus 21 | 22 | extern "C" { 23 | 24 | #endif 25 | 26 | #include "redismodule.h" 27 | 28 | #ifdef __cplusplus 29 | 30 | } 31 | 32 | #endif 33 | 34 | #include 35 | #include 36 | #include "errors.h" 37 | 38 | namespace sw { 39 | 40 | namespace redis { 41 | 42 | namespace pb { 43 | 44 | namespace api { 45 | 46 | template 47 | void warning(RedisModuleCtx *ctx, const char *fmt, Args &&...args) { 48 | RedisModule_Log(ctx, "warning", fmt, std::forward(args)...); 49 | } 50 | 51 | template 52 | void notice(RedisModuleCtx *ctx, const char *fmt, Args &&...args) { 53 | RedisModule_Log(ctx, "notice", fmt, std::forward(args)...); 54 | } 55 | 56 | template 57 | void debug(RedisModuleCtx *ctx, const char *fmt, Args &&...args) { 58 | RedisModule_Log(ctx, "debug", fmt, std::forward(args)...); 59 | } 60 | 61 | template 62 | void verbose(RedisModuleCtx *ctx, const char *fmt, Args &&...args) { 63 | RedisModule_Log(ctx, "verbose", fmt, std::forward(args)...); 64 | } 65 | 66 | struct RedisKeyCloser { 67 | void operator()(RedisModuleKey *key) const { 68 | RedisModule_CloseKey(key); 69 | } 70 | }; 71 | 72 | using RedisKey = std::unique_ptr; 73 | 74 | enum class KeyMode { 75 | READONLY, 76 | WRITEONLY, 77 | READWRITE 78 | }; 79 | 80 | RedisKey open_key(RedisModuleCtx *ctx, RedisModuleString *name, KeyMode mode); 81 | 82 | // If key doesn't exist return false. 83 | // If key type is NOT *key_type*, throw WrongTypeError. 84 | // Otherwise, return true. 85 | bool key_exists(RedisModuleKey *key, RedisModuleType *key_type); 86 | 87 | int reply_with_error(RedisModuleCtx *ctx, const Error &err); 88 | 89 | google::protobuf::Message* get_msg_by_key(RedisModuleKey *key); 90 | 91 | } 92 | 93 | } 94 | 95 | } 96 | 97 | } 98 | 99 | #endif // end SEWENEW_REDISPROTOBUF_MODULE_API_H 100 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/module_entry.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "module_entry.h" 18 | #include 19 | #include "redis_protobuf.h" 20 | #include "errors.h" 21 | #include "module_api.h" 22 | 23 | int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 24 | assert(ctx != nullptr); 25 | 26 | using namespace sw::redis::pb; 27 | 28 | try { 29 | auto &m = RedisProtobuf::instance(); 30 | 31 | m.load(ctx, argv, argc); 32 | } catch (const Error &e) { 33 | api::warning(ctx, "%s", e.what()); 34 | return REDISMODULE_ERR; 35 | } 36 | 37 | return REDISMODULE_OK; 38 | } 39 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/module_entry.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_MODULE_ENTRY_H 18 | #define SEWENEW_REDISPROTOBUF_MODULE_ENTRY_H 19 | 20 | #include "module_api.h" 21 | 22 | #ifdef __cplusplus 23 | 24 | extern "C" { 25 | 26 | #endif 27 | 28 | int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc); 29 | 30 | #ifdef __cplusplus 31 | 32 | } 33 | 34 | #endif 35 | 36 | #endif // end SEWENEW_REDISPROTOBUF_MODULE_ENTRY_H 37 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/options.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "options.h" 18 | #include 19 | #include "redis_protobuf.h" 20 | #include "errors.h" 21 | #include "utils.h" 22 | 23 | namespace sw { 24 | 25 | namespace redis { 26 | 27 | namespace pb { 28 | 29 | void Options::load(RedisModuleString **argv, int argc) { 30 | Options opts; 31 | 32 | auto idx = 0; 33 | while (idx < argc) { 34 | auto opt = StringView(argv[idx]); 35 | 36 | if (util::str_case_equal(opt, "--DIR")) { 37 | if (!opts.proto_dir.empty()) { 38 | throw Error("duplicate --DIR option"); 39 | } 40 | 41 | ++idx; 42 | 43 | opts.proto_dir = util::sv_to_string(StringView(argv[idx])); 44 | } else { 45 | throw Error("unknown option: " + util::sv_to_string(opt)); 46 | } 47 | 48 | ++idx; 49 | } 50 | 51 | if (opts.proto_dir.empty()) { 52 | throw Error("option '--DIR dir' is required"); 53 | } 54 | 55 | *this = std::move(opts); 56 | } 57 | 58 | } 59 | 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/options.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_OPTIONS_H 18 | #define SEWENEW_REDISPROTOBUF_OPTIONS_H 19 | 20 | #include "module_api.h" 21 | #include 22 | 23 | namespace sw { 24 | 25 | namespace redis { 26 | 27 | namespace pb { 28 | 29 | struct Options { 30 | void load(RedisModuleString **argv, int argc); 31 | 32 | std::string proto_dir; 33 | }; 34 | 35 | } 36 | 37 | } 38 | 39 | } 40 | 41 | #endif // end SEWENEW_REDISPROTOBUF_OPTIONS_H 42 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/path.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "path.h" 18 | #include "errors.h" 19 | 20 | namespace sw { 21 | 22 | namespace redis { 23 | 24 | namespace pb { 25 | 26 | Path::Path(const StringView &type, const StringView &path) : 27 | _type(type.data(), type.size()), _fields(_parse_fields(path)) {} 28 | 29 | std::vector Path::_parse_fields(const StringView &path) const { 30 | if (path.size() <= 1) { 31 | throw Error("empty path"); 32 | } 33 | 34 | if (*(path.data()) != '/') { 35 | throw Error("invalid path: should begin with /"); 36 | } 37 | 38 | std::vector fields; 39 | auto start = 1U; 40 | const auto *ptr = path.data(); 41 | for (auto idx = start; idx != path.size(); ++idx) { 42 | if (ptr[idx] == '/') { 43 | if (idx <= start) { 44 | throw Error("empty field"); 45 | } 46 | 47 | fields.emplace_back(ptr + start, idx - start); 48 | start = idx + 1; 49 | } 50 | } 51 | 52 | if (path.size() <= start) { 53 | throw Error("empty field"); 54 | } 55 | 56 | fields.emplace_back(ptr + start, path.size() - start); 57 | 58 | return fields; 59 | } 60 | 61 | } 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/path.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_PATH_H 18 | #define SEWENEW_REDISPROTOBUF_PATH_H 19 | 20 | #include 21 | #include 22 | #include "utils.h" 23 | 24 | namespace sw { 25 | 26 | namespace redis { 27 | 28 | namespace pb { 29 | 30 | class Path { 31 | public: 32 | Path() = default; 33 | 34 | Path(const StringView &type, const StringView &path); 35 | 36 | explicit Path(const StringView &type) : _type(type.data(), type.size()) {} 37 | 38 | const std::string& type() const { 39 | return _type; 40 | } 41 | 42 | const std::vector& fields() const { 43 | return _fields; 44 | } 45 | 46 | bool empty() const { 47 | return _fields.empty(); 48 | } 49 | 50 | private: 51 | std::vector _parse_fields(const StringView &path) const; 52 | 53 | std::string _type; 54 | 55 | std::vector _fields; 56 | }; 57 | 58 | } 59 | 60 | } 61 | 62 | } 63 | 64 | #endif // end SEWENEW_REDISPROTOBUF_PATH_H 65 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/proto_factory.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "proto_factory.h" 18 | #include 19 | #include 20 | #include "utils.h" 21 | #include "errors.h" 22 | 23 | namespace sw { 24 | 25 | namespace redis { 26 | 27 | namespace pb { 28 | 29 | void FactoryErrorCollector::_add_error(const std::string &type, 30 | const std::string &filename, 31 | int line, 32 | int column, 33 | const std::string &message) { 34 | auto err = type + ":" + filename + ":" 35 | + std::to_string(line) + ":" 36 | + std::to_string(column) + ":" 37 | + message; 38 | 39 | _errors.push_back(std::move(err)); 40 | } 41 | 42 | std::string FactoryErrorCollector::last_errors() const { 43 | std::string err_str; 44 | for (const auto &err : _errors) { 45 | if (!err_str.empty()) { 46 | err_str += "\n"; 47 | } 48 | 49 | err_str += err; 50 | } 51 | 52 | return err_str; 53 | } 54 | 55 | ProtoFactory::ProtoFactory(const std::string &proto_dir) : 56 | _proto_dir(_canonicalize_path(proto_dir)), 57 | _importer(&_source_tree, &_error_collector) { 58 | _source_tree.MapPath("", _proto_dir); 59 | 60 | _load_protos(_proto_dir); 61 | 62 | _async_loader = std::thread([this]() { this->_async_load(); }); 63 | } 64 | 65 | ProtoFactory::~ProtoFactory() { 66 | _stop_loader = true; 67 | _cv.notify_one(); 68 | if (_async_loader.joinable()) { 69 | _async_loader.join(); 70 | } 71 | } 72 | 73 | MsgUPtr ProtoFactory::create(const std::string &type) { 74 | const auto *desc = descriptor(type); 75 | if (desc == nullptr) { 76 | throw Error("unknown protobuf type: " + type); 77 | } 78 | 79 | const auto *prototype = _factory.GetPrototype(desc); 80 | 81 | assert(prototype != nullptr); 82 | 83 | return MsgUPtr(prototype->New()); 84 | } 85 | 86 | MsgUPtr ProtoFactory::create(const std::string &type, const StringView &sv) { 87 | auto msg = create(type); 88 | 89 | const auto *ptr = sv.data(); 90 | auto len = sv.size(); 91 | if (len >= 2 && ptr[0] == '{' && ptr[len - 1] == '}') { 92 | auto status = gp::util::JsonStringToMessage(gp::StringPiece(ptr, len), msg.get()); 93 | if (!status.ok()) { 94 | throw Error("failed to parse json to " + type + ": " + status.ToString()); 95 | } 96 | } else { 97 | if (!msg->ParseFromArray(ptr, len)) { 98 | throw Error("failed to parse binary to " + type); 99 | } 100 | } 101 | 102 | return msg; 103 | } 104 | 105 | const gp::Descriptor* ProtoFactory::descriptor(const std::string &type) { 106 | auto iter = _descriptor_cache.find(type); 107 | if (iter != _descriptor_cache.end()) { 108 | return iter->second; 109 | } 110 | 111 | const auto *desc = _importer.pool()->FindMessageTypeByName(type); 112 | if (desc != nullptr) { 113 | _descriptor_cache.emplace(type, desc); 114 | } 115 | 116 | return desc; 117 | } 118 | 119 | void ProtoFactory::load(const std::string &filename, const std::string &content) { 120 | { 121 | std::lock_guard lock(_mtx); 122 | 123 | _tasks[filename] = content; 124 | } 125 | 126 | _cv.notify_one(); 127 | } 128 | 129 | std::unordered_map ProtoFactory::last_loaded() { 130 | std::unordered_map last_loaded_files; 131 | { 132 | std::lock_guard lock(_mtx); 133 | 134 | _last_loaded_files.swap(last_loaded_files); 135 | } 136 | 137 | return last_loaded_files; 138 | } 139 | 140 | void ProtoFactory::_load_protos(const std::string &proto_dir) { 141 | auto files = io::list_dir(proto_dir); 142 | for (const auto &file : files) { 143 | if (!io::is_regular(file) || io::extension(file) != "proto") { 144 | continue; 145 | } 146 | 147 | auto prefix_size = proto_dir.size() + 1; 148 | if (file.size() < prefix_size) { 149 | continue; 150 | } 151 | 152 | _load(file.substr(prefix_size)); 153 | } 154 | } 155 | 156 | void ProtoFactory::_load(const std::string &file) { 157 | // Clear last errors. 158 | _error_collector.clear(); 159 | 160 | auto *desc = _importer.Import(file); 161 | if (desc == nullptr || _error_collector.has_error()) { 162 | throw Error("failed to load " + file + "\n" + _error_collector.last_errors()); 163 | } 164 | 165 | _loaded_files.insert(file); 166 | } 167 | 168 | std::string ProtoFactory::_canonicalize_path(std::string proto_dir) const { 169 | // Remove trailing '/' 170 | while (!proto_dir.empty() && proto_dir.back() == '/') { 171 | proto_dir.resize(proto_dir.size() - 1); 172 | } 173 | 174 | if (proto_dir.empty()) { 175 | throw Error("invalid proto dir: " + proto_dir); 176 | } 177 | 178 | return proto_dir; 179 | } 180 | 181 | void ProtoFactory::_async_load() { 182 | while (!_stop_loader) { 183 | std::unordered_map tasks; 184 | { 185 | std::unique_lock lock(_mtx); 186 | _cv.wait(lock, [this]() { return this->_stop_loader || !(this->_tasks).empty(); }); 187 | 188 | tasks.swap(_tasks); 189 | } 190 | 191 | std::unordered_map status; 192 | for (const auto &task : tasks) { 193 | const auto &filename = task.first; 194 | const auto &content = task.second; 195 | try { 196 | _load(filename, content); 197 | 198 | status[filename] = "OK"; 199 | } catch (const Error &err) { 200 | status[filename] = std::string("ERR ") + err.what(); 201 | } 202 | } 203 | 204 | if (!status.empty()) { 205 | std::lock_guard lock(_mtx); 206 | 207 | for (auto &&ele : status) { 208 | _last_loaded_files[ele.first] = std::move(ele.second); 209 | } 210 | } 211 | } 212 | } 213 | 214 | void ProtoFactory::_load(const std::string &filename, const std::string &content) { 215 | auto iter = _loaded_files.find(filename); 216 | if (iter != _loaded_files.end()) { 217 | throw Error("already imported"); 218 | } 219 | 220 | _dump_to_disk(filename, content); 221 | 222 | try { 223 | _load(filename); 224 | } catch (const Error &) { 225 | io::remove_file(_absolute_path(filename)); 226 | throw; 227 | } 228 | } 229 | 230 | void ProtoFactory::_dump_to_disk(const std::string &filename, const std::string &content) const { 231 | auto path = _absolute_path(filename); 232 | 233 | std::ofstream file(path); 234 | if (!file) { 235 | throw Error("failed to open proto file for writing: " + path); 236 | } 237 | 238 | file << content; 239 | } 240 | 241 | std::string ProtoFactory::_absolute_path(const std::string &path) const { 242 | return _proto_dir + "/" + path; 243 | } 244 | 245 | } 246 | 247 | } 248 | 249 | } 250 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/proto_factory.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_PROTO_FACTORY_H 18 | #define SEWENEW_REDISPROTOBUF_PROTO_FACTORY_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "utils.h" 32 | 33 | namespace sw { 34 | 35 | namespace redis { 36 | 37 | namespace pb { 38 | 39 | class FactoryErrorCollector : public gp::compiler::MultiFileErrorCollector { 40 | public: 41 | virtual void AddError(const std::string &file_name, 42 | int line, 43 | int column, 44 | const std::string &message) override { 45 | _add_error("error", file_name, line, column, message); 46 | } 47 | 48 | virtual void AddWarning(const std::string &file_name, 49 | int line, 50 | int column, 51 | const std::string &message) override { 52 | _add_error("warning", file_name, line, column, message); 53 | } 54 | 55 | std::string last_errors() const; 56 | 57 | bool has_error() const { 58 | return !_errors.empty(); 59 | } 60 | 61 | void clear() { 62 | _errors.clear(); 63 | } 64 | 65 | private: 66 | void _add_error(const std::string &type, 67 | const std::string &filename, 68 | int line, 69 | int column, 70 | const std::string &message); 71 | 72 | std::vector _errors; 73 | }; 74 | 75 | class ProtoFactory { 76 | public: 77 | explicit ProtoFactory(const std::string &proto_dir); 78 | 79 | ProtoFactory(const ProtoFactory &) = delete; 80 | ProtoFactory& operator=(const ProtoFactory &) = delete; 81 | 82 | ProtoFactory(ProtoFactory &&) = delete; 83 | ProtoFactory& operator=(ProtoFactory &&) = delete; 84 | 85 | ~ProtoFactory(); 86 | 87 | MsgUPtr create(const std::string &type); 88 | 89 | MsgUPtr create(const std::string &type, const StringView &sv); 90 | 91 | const gp::Descriptor* descriptor(const std::string &type); 92 | 93 | void load(const std::string &file, const std::string &content); 94 | 95 | std::unordered_map last_loaded(); 96 | 97 | private: 98 | void _load_protos(const std::string &proto_dir); 99 | 100 | void _load(const std::string &file); 101 | 102 | void _load(const std::string &filename, const std::string &content); 103 | 104 | std::string _canonicalize_path(std::string proto_dir) const; 105 | 106 | void _async_load(); 107 | 108 | void _dump_to_disk(const std::string &filename, const std::string &content) const; 109 | 110 | std::string _absolute_path(const std::string &path) const; 111 | 112 | // Dir where .proto file are saved. 113 | std::string _proto_dir; 114 | 115 | gp::compiler::DiskSourceTree _source_tree; 116 | 117 | FactoryErrorCollector _error_collector; 118 | 119 | gp::compiler::Importer _importer; 120 | 121 | gp::DynamicMessageFactory _factory; 122 | 123 | std::unordered_map _descriptor_cache; 124 | 125 | std::unordered_set _loaded_files; 126 | 127 | std::mutex _mtx; 128 | 129 | std::condition_variable _cv; 130 | 131 | // map 132 | std::unordered_map _tasks; 133 | 134 | // map 135 | std::unordered_map _last_loaded_files; 136 | 137 | std::atomic _stop_loader{false}; 138 | 139 | std::thread _async_loader; 140 | }; 141 | 142 | } 143 | 144 | } 145 | 146 | } 147 | 148 | #endif // end SEWENEW_REDISPROTOBUF_PROTO_FACTORY_H 149 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/redis_protobuf.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "redis_protobuf.h" 18 | #include 19 | #include 20 | #include 21 | #include "errors.h" 22 | #include "commands.h" 23 | 24 | namespace { 25 | 26 | struct StringDeleter { 27 | void operator()(char *str) const { 28 | if (str != nullptr) { 29 | RedisModule_Free(str); 30 | } 31 | } 32 | }; 33 | 34 | using StringUPtr = std::unique_ptr; 35 | 36 | struct RDBString { 37 | StringUPtr str; 38 | std::size_t len; 39 | }; 40 | 41 | RDBString rdb_load_string(RedisModuleIO *rdb); 42 | 43 | std::pair rdb_load_value(RedisModuleIO *rdb); 44 | 45 | std::pair serialize_message(void *value); 46 | 47 | } 48 | 49 | namespace sw { 50 | 51 | namespace redis { 52 | 53 | namespace pb { 54 | 55 | RedisProtobuf& RedisProtobuf::instance() { 56 | static RedisProtobuf redis_proto; 57 | 58 | return redis_proto; 59 | } 60 | 61 | void RedisProtobuf::load(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 62 | assert(ctx != nullptr); 63 | 64 | if (RedisModule_Init(ctx, 65 | module_name().data(), 66 | module_version(), 67 | REDISMODULE_APIVER_1) == REDISMODULE_ERR) { 68 | throw Error("fail to init module of " + module_name() + " type"); 69 | } 70 | 71 | _options.load(argv, argc); 72 | 73 | RedisModuleTypeMethods methods = { 74 | REDISMODULE_TYPE_METHOD_VERSION, 75 | _rdb_load, 76 | _rdb_save, 77 | _aof_rewrite, 78 | nullptr, 79 | nullptr, 80 | _free_msg 81 | }; 82 | 83 | _module_type = RedisModule_CreateDataType(ctx, 84 | type_name().data(), 85 | encoding_version(), 86 | &methods); 87 | if (_module_type == nullptr) { 88 | throw Error(std::string("failed to create ") + type_name() + " type"); 89 | } 90 | 91 | _proto_factory = std::unique_ptr(new ProtoFactory(options().proto_dir)); 92 | 93 | cmd::create_commands(ctx); 94 | } 95 | 96 | void* RedisProtobuf::_rdb_load(RedisModuleIO *rdb, int encver) { 97 | try { 98 | assert(rdb != nullptr); 99 | 100 | auto &m = RedisProtobuf::instance(); 101 | 102 | if (encver != m.encoding_version()) { 103 | throw Error("cannot load data of version: " + std::to_string(encver)); 104 | } 105 | 106 | RDBString type_str; 107 | RDBString data_str; 108 | std::tie(type_str, data_str) = rdb_load_value(rdb); 109 | 110 | auto type = std::string(type_str.str.get(), type_str.len); 111 | 112 | auto *factory = m.proto_factory(); 113 | 114 | assert(factory != nullptr); 115 | 116 | auto msg = factory->create(type); 117 | if (!msg) { 118 | throw Error("unknown protobuf type: " + type); 119 | } 120 | 121 | if (!msg->ParseFromArray(data_str.str.get(), data_str.len)) { 122 | throw Error("failed to parse protobuf of type: " + type); 123 | } 124 | 125 | return msg.release(); 126 | } catch (const Error &e) { 127 | RedisModule_LogIOError(rdb, "warning", e.what()); 128 | return nullptr; 129 | } 130 | } 131 | 132 | void RedisProtobuf::_rdb_save(RedisModuleIO *rdb, void *value) { 133 | try { 134 | assert(rdb != nullptr); 135 | 136 | std::string type; 137 | std::string buf; 138 | std::tie(type, buf) = serialize_message(value); 139 | 140 | RedisModule_SaveStringBuffer(rdb, type.data(), type.size()); 141 | 142 | RedisModule_SaveStringBuffer(rdb, buf.data(), buf.size()); 143 | } catch (const Error &e) { 144 | RedisModule_LogIOError(rdb, "warning", e.what()); 145 | } 146 | } 147 | 148 | void RedisProtobuf::_aof_rewrite(RedisModuleIO *aof, RedisModuleString *key, void *value) { 149 | try { 150 | assert(aof != nullptr); 151 | 152 | if (key == nullptr) { 153 | throw Error("null key to rewrite aof"); 154 | } 155 | 156 | std::string type; 157 | std::string buf; 158 | std::tie(type, buf) = serialize_message(value); 159 | 160 | RedisModule_EmitAOF(aof, 161 | "PB.SET", 162 | "sbb", 163 | key, 164 | type.data(), 165 | type.size(), 166 | buf.data(), 167 | buf.size()); 168 | } catch (const Error &e) { 169 | RedisModule_LogIOError(aof, "warning", e.what()); 170 | } 171 | } 172 | 173 | void RedisProtobuf::_free_msg(void *value) { 174 | if (value != nullptr) { 175 | auto *msg = static_cast(value); 176 | delete msg; 177 | } 178 | } 179 | 180 | } 181 | 182 | } 183 | 184 | } 185 | 186 | namespace { 187 | 188 | using sw::redis::pb::Error; 189 | 190 | RDBString rdb_load_string(RedisModuleIO *rdb) { 191 | std::size_t len = 0; 192 | auto *buf = RedisModule_LoadStringBuffer(rdb, &len); 193 | if (buf == nullptr) { 194 | throw Error("failed to load string buffer from rdb"); 195 | } 196 | 197 | return {StringUPtr(buf), len}; 198 | } 199 | 200 | std::pair rdb_load_value(RedisModuleIO *rdb) { 201 | auto type = rdb_load_string(rdb); 202 | 203 | auto data = rdb_load_string(rdb); 204 | 205 | return {std::move(type), std::move(data)}; 206 | } 207 | 208 | std::pair serialize_message(void *value) { 209 | if (value == nullptr) { 210 | throw Error("Null value to serialize"); 211 | } 212 | 213 | auto *msg = static_cast(value); 214 | 215 | auto type = msg->GetTypeName(); 216 | 217 | std::string buf; 218 | if (!msg->SerializeToString(&buf)) { 219 | throw Error("failed to serialize protobuf message of type " + type); 220 | } 221 | 222 | return {std::move(type), std::move(buf)}; 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/redis_protobuf.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_REDIS_PROTOBUF_H 18 | #define SEWENEW_REDISPROTOBUF_REDIS_PROTOBUF_H 19 | 20 | #include "module_api.h" 21 | #include "proto_factory.h" 22 | #include "options.h" 23 | 24 | namespace sw { 25 | 26 | namespace redis { 27 | 28 | namespace pb { 29 | 30 | class RedisProtobuf { 31 | public: 32 | static RedisProtobuf& instance(); 33 | 34 | void load(RedisModuleCtx *ctx, RedisModuleString **argv, int argc); 35 | 36 | int module_version() const { 37 | return _MODULE_VERSION; 38 | } 39 | 40 | int encoding_version() const { 41 | return _ENCODING_VERSION; 42 | } 43 | 44 | const std::string& module_name() const { 45 | return _MODULE_NAME; 46 | } 47 | 48 | const std::string& type_name() const { 49 | return _TYPE_NAME; 50 | } 51 | 52 | RedisModuleType* type() { 53 | return _module_type; 54 | } 55 | 56 | const Options& options() const { 57 | return _options; 58 | } 59 | 60 | ProtoFactory* proto_factory() { 61 | return _proto_factory.get(); 62 | } 63 | 64 | private: 65 | RedisProtobuf() = default; 66 | 67 | static void* _rdb_load(RedisModuleIO *rdb, int encver); 68 | 69 | static void _rdb_save(RedisModuleIO *rdb, void *value); 70 | 71 | static void _aof_rewrite(RedisModuleIO *aof, RedisModuleString *key, void *value); 72 | 73 | static void _free_msg(void *value); 74 | 75 | const int _MODULE_VERSION = 1; 76 | 77 | const int _ENCODING_VERSION = 0; 78 | 79 | const std::string _MODULE_NAME = "PB"; 80 | 81 | const std::string _TYPE_NAME = "PROTOC-SW"; 82 | 83 | RedisModuleType *_module_type = nullptr; 84 | 85 | std::unique_ptr _proto_factory; 86 | 87 | Options _options; 88 | }; 89 | 90 | } 91 | 92 | } 93 | 94 | } 95 | 96 | #endif // end SEWENEW_REDISPROTOBUF_REDIS_PROTOBUF_H 97 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/redismodule.cpp: -------------------------------------------------------------------------------- 1 | // This file is copied from Redis 4.0, and I splited the original header into 2 | // a header file and a cpp file, i.e. redismodule.h and redismodule.cpp. 3 | // So that it can be compiled with C++ compiler. 4 | 5 | #include "redismodule.h" 6 | 7 | #ifndef REDISMODULE_CORE 8 | 9 | void *REDISMODULE_API_FUNC(RedisModule_Alloc)(size_t bytes); 10 | void *REDISMODULE_API_FUNC(RedisModule_Realloc)(void *ptr, size_t bytes); 11 | void REDISMODULE_API_FUNC(RedisModule_Free)(void *ptr); 12 | void *REDISMODULE_API_FUNC(RedisModule_Calloc)(size_t nmemb, size_t size); 13 | char *REDISMODULE_API_FUNC(RedisModule_Strdup)(const char *str); 14 | int REDISMODULE_API_FUNC(RedisModule_GetApi)(const char *, void *); 15 | int REDISMODULE_API_FUNC(RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep); 16 | void REDISMODULE_API_FUNC(RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver); 17 | int REDISMODULE_API_FUNC(RedisModule_IsModuleNameBusy)(const char *name); 18 | int REDISMODULE_API_FUNC(RedisModule_WrongArity)(RedisModuleCtx *ctx); 19 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll); 20 | int REDISMODULE_API_FUNC(RedisModule_GetSelectedDb)(RedisModuleCtx *ctx); 21 | int REDISMODULE_API_FUNC(RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid); 22 | void *REDISMODULE_API_FUNC(RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode); 23 | void REDISMODULE_API_FUNC(RedisModule_CloseKey)(RedisModuleKey *kp); 24 | int REDISMODULE_API_FUNC(RedisModule_KeyType)(RedisModuleKey *kp); 25 | size_t REDISMODULE_API_FUNC(RedisModule_ValueLength)(RedisModuleKey *kp); 26 | int REDISMODULE_API_FUNC(RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele); 27 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ListPop)(RedisModuleKey *key, int where); 28 | RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); 29 | const char *REDISMODULE_API_FUNC(RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len); 30 | void REDISMODULE_API_FUNC(RedisModule_FreeCallReply)(RedisModuleCallReply *reply); 31 | int REDISMODULE_API_FUNC(RedisModule_CallReplyType)(RedisModuleCallReply *reply); 32 | long long REDISMODULE_API_FUNC(RedisModule_CallReplyInteger)(RedisModuleCallReply *reply); 33 | size_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *reply); 34 | RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx); 35 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len); 36 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll); 37 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str); 38 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...); 39 | void REDISMODULE_API_FUNC(RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str); 40 | const char *REDISMODULE_API_FUNC(RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len); 41 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err); 42 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg); 43 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len); 44 | void REDISMODULE_API_FUNC(RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len); 45 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len); 46 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str); 47 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithNull)(RedisModuleCtx *ctx); 48 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d); 49 | int REDISMODULE_API_FUNC(RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply); 50 | int REDISMODULE_API_FUNC(RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll); 51 | int REDISMODULE_API_FUNC(RedisModule_StringToDouble)(const RedisModuleString *str, double *d); 52 | void REDISMODULE_API_FUNC(RedisModule_AutoMemory)(RedisModuleCtx *ctx); 53 | int REDISMODULE_API_FUNC(RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); 54 | int REDISMODULE_API_FUNC(RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx); 55 | const char *REDISMODULE_API_FUNC(RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len); 56 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply); 57 | int REDISMODULE_API_FUNC(RedisModule_DeleteKey)(RedisModuleKey *key); 58 | int REDISMODULE_API_FUNC(RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str); 59 | char *REDISMODULE_API_FUNC(RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode); 60 | int REDISMODULE_API_FUNC(RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen); 61 | mstime_t REDISMODULE_API_FUNC(RedisModule_GetExpire)(RedisModuleKey *key); 62 | int REDISMODULE_API_FUNC(RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire); 63 | int REDISMODULE_API_FUNC(RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr); 64 | int REDISMODULE_API_FUNC(RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore); 65 | int REDISMODULE_API_FUNC(RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score); 66 | int REDISMODULE_API_FUNC(RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted); 67 | void REDISMODULE_API_FUNC(RedisModule_ZsetRangeStop)(RedisModuleKey *key); 68 | int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); 69 | int REDISMODULE_API_FUNC(RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); 70 | int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); 71 | int REDISMODULE_API_FUNC(RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); 72 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score); 73 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangeNext)(RedisModuleKey *key); 74 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangePrev)(RedisModuleKey *key); 75 | int REDISMODULE_API_FUNC(RedisModule_ZsetRangeEndReached)(RedisModuleKey *key); 76 | int REDISMODULE_API_FUNC(RedisModule_HashSet)(RedisModuleKey *key, int flags, ...); 77 | int REDISMODULE_API_FUNC(RedisModule_HashGet)(RedisModuleKey *key, int flags, ...); 78 | int REDISMODULE_API_FUNC(RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx); 79 | void REDISMODULE_API_FUNC(RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos); 80 | unsigned long long REDISMODULE_API_FUNC(RedisModule_GetClientId)(RedisModuleCtx *ctx); 81 | int REDISMODULE_API_FUNC(RedisModule_GetContextFlags)(RedisModuleCtx *ctx); 82 | void *REDISMODULE_API_FUNC(RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes); 83 | RedisModuleType *REDISMODULE_API_FUNC(RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods); 84 | int REDISMODULE_API_FUNC(RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value); 85 | RedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModuleKey *key); 86 | void *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key); 87 | void REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value); 88 | uint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io); 89 | void REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value); 90 | int64_t REDISMODULE_API_FUNC(RedisModule_LoadSigned)(RedisModuleIO *io); 91 | void REDISMODULE_API_FUNC(RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...); 92 | void REDISMODULE_API_FUNC(RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s); 93 | void REDISMODULE_API_FUNC(RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len); 94 | RedisModuleString *REDISMODULE_API_FUNC(RedisModule_LoadString)(RedisModuleIO *io); 95 | char *REDISMODULE_API_FUNC(RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr); 96 | void REDISMODULE_API_FUNC(RedisModule_SaveDouble)(RedisModuleIO *io, double value); 97 | double REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io); 98 | void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value); 99 | float REDISMODULE_API_FUNC(RedisModule_LoadFloat)(RedisModuleIO *io); 100 | void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...); 101 | void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...); 102 | int REDISMODULE_API_FUNC(RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len); 103 | void REDISMODULE_API_FUNC(RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str); 104 | int REDISMODULE_API_FUNC(RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b); 105 | RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetContextFromIO)(RedisModuleIO *io); 106 | long long REDISMODULE_API_FUNC(RedisModule_Milliseconds)(void); 107 | void REDISMODULE_API_FUNC(RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len); 108 | void REDISMODULE_API_FUNC(RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele); 109 | void REDISMODULE_API_FUNC(RedisModule_DigestEndSequence)(RedisModuleDigest *md); 110 | 111 | #ifdef REDISMODULE_EXPERIMENTAL_API 112 | 113 | RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(void*), long long timeout_ms); 114 | int REDISMODULE_API_FUNC(RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata); 115 | int REDISMODULE_API_FUNC(RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx); 116 | int REDISMODULE_API_FUNC(RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx); 117 | void *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx); 118 | int REDISMODULE_API_FUNC(RedisModule_AbortBlock)(RedisModuleBlockedClient *bc); 119 | RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc); 120 | void REDISMODULE_API_FUNC(RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx); 121 | void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx); 122 | void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx); 123 | 124 | #endif 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/schema_command.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "schema_command.h" 18 | #include "errors.h" 19 | #include "redis_protobuf.h" 20 | #include "field_ref.h" 21 | 22 | namespace sw { 23 | 24 | namespace redis { 25 | 26 | namespace pb { 27 | 28 | int SchemaCommand::run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const { 29 | try { 30 | assert(ctx != nullptr); 31 | 32 | auto args = _parse_args(argv, argc); 33 | const auto &type = args.type; 34 | 35 | auto *desc = RedisProtobuf::instance().proto_factory()->descriptor(type); 36 | if (desc == nullptr) { 37 | RedisModule_ReplyWithNull(ctx); 38 | } else { 39 | //auto schema = _format(desc->DebugString()); 40 | auto schema = desc->DebugString(); 41 | 42 | RedisModule_ReplyWithStringBuffer(ctx, schema.data(), schema.size()); 43 | } 44 | 45 | return REDISMODULE_OK; 46 | } catch (const WrongArityError &err) { 47 | return RedisModule_WrongArity(ctx); 48 | } catch (const Error &err) { 49 | return api::reply_with_error(ctx, err); 50 | } 51 | 52 | return REDISMODULE_ERR; 53 | } 54 | 55 | SchemaCommand::Args SchemaCommand::_parse_args(RedisModuleString **argv, int argc) const { 56 | assert(argv != nullptr); 57 | 58 | if (argc != 2) { 59 | throw WrongArityError(); 60 | } 61 | 62 | return {Path(argv[1]).type()}; 63 | } 64 | 65 | std::string SchemaCommand::_format(const std::string &schema) const { 66 | std::string formated_schema; 67 | formated_schema.reserve(schema.size() * 2); 68 | 69 | for (auto ch : schema) { 70 | if (ch != '.') { 71 | formated_schema.push_back(ch); 72 | } else { 73 | if (formated_schema.empty()) { 74 | // This should not happen. 75 | throw Error("invalid schema"); 76 | } 77 | 78 | if (formated_schema.back() != ' ') { 79 | // '.' => '::' 80 | formated_schema.append("::"); 81 | } // else discard leading '.' 82 | } 83 | } 84 | 85 | return formated_schema; 86 | } 87 | 88 | } 89 | 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/schema_command.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_SCHEMA_COMMANDS_H 18 | #define SEWENEW_REDISPROTOBUF_SCHEMA_COMMANDS_H 19 | 20 | #include 21 | #include "module_api.h" 22 | #include "utils.h" 23 | 24 | namespace sw { 25 | 26 | namespace redis { 27 | 28 | namespace pb { 29 | 30 | // command: PB.SCHEMA type 31 | // return: Bulk string reply: return the schema of the specified type. If 32 | // the type doesn't exist, return a nil reply. 33 | class SchemaCommand { 34 | public: 35 | int run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const; 36 | 37 | private: 38 | struct Args { 39 | std::string type; 40 | }; 41 | 42 | Args _parse_args(RedisModuleString **argv, int argc) const; 43 | 44 | std::string _format(const std::string &schema) const; 45 | }; 46 | 47 | } 48 | 49 | } 50 | 51 | } 52 | 53 | #endif // end SEWENEW_REDISPROTOBUF_SCHEMA_COMMANDS_H 54 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/set_command.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_SET_COMMANDS_H 18 | #define SEWENEW_REDISPROTOBUF_SET_COMMANDS_H 19 | 20 | #include "module_api.h" 21 | #include 22 | #include 23 | #include 24 | #include "utils.h" 25 | #include "field_ref.h" 26 | 27 | namespace sw { 28 | 29 | namespace redis { 30 | 31 | namespace pb { 32 | 33 | // command: PB.SET key [--NX|--XX] [--EX seconds | --PX milliseconds] type [path] value 34 | // return: Integer reply: 1 if set successfully. 0, otherwise, e.g. option --NX has 35 | // been set, while key already exists. 36 | // error: If the type doesn't match the protobuf message type of the 37 | // key, or the path doesn't exist, return an error reply. 38 | class SetCommand { 39 | public: 40 | int run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const; 41 | 42 | private: 43 | struct Args { 44 | RedisModuleString *key_name; 45 | 46 | enum class Opt { 47 | NX = 0, 48 | XX, 49 | NONE 50 | }; 51 | 52 | Opt opt = Opt::NONE; 53 | 54 | std::chrono::milliseconds expire{0}; 55 | 56 | Path path; 57 | StringView val; 58 | }; 59 | 60 | int _run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const; 61 | 62 | friend class MergeCommand; 63 | 64 | Args _parse_args(RedisModuleString **argv, int argc) const; 65 | 66 | // Return the position of the first non-option argument. 67 | int _parse_opts(RedisModuleString **argv, int argc, Args &args) const; 68 | 69 | int64_t _parse_expire(const StringView &sv) const; 70 | 71 | void _create_msg(RedisModuleKey &key, 72 | const Path &path, 73 | const StringView &val) const; 74 | 75 | void _set_msg(RedisModuleKey &key, 76 | const Path &path, 77 | const StringView &val) const; 78 | 79 | void _set_scalar_field(MutableFieldRef &field, const StringView &val) const; 80 | 81 | void _set_map_element(MutableFieldRef &field, const StringView &val) const; 82 | 83 | void _set_array_element(MutableFieldRef &field, const StringView &val) const; 84 | 85 | void _set_field(MutableFieldRef &field, const StringView &sv) const; 86 | 87 | void _set_int32(MutableFieldRef &field, const StringView &sv) const; 88 | 89 | void _set_int64(MutableFieldRef &field, const StringView &sv) const; 90 | 91 | void _set_uint32(MutableFieldRef &field, const StringView &sv) const; 92 | 93 | void _set_uint64(MutableFieldRef &field, const StringView &sv) const; 94 | 95 | void _set_double(MutableFieldRef &field, const StringView &sv) const; 96 | 97 | void _set_float(MutableFieldRef &field, const StringView &sv) const; 98 | 99 | void _set_bool(MutableFieldRef &field, const StringView &sv) const; 100 | 101 | void _set_enum(MutableFieldRef &field, const StringView &sv) const; 102 | 103 | void _set_string(MutableFieldRef &field, const StringView &sv) const; 104 | 105 | void _set_msg(MutableFieldRef &field, const StringView &sv) const; 106 | 107 | void _set_repeated_int32(MutableFieldRef &field, const StringView &sv) const; 108 | 109 | void _set_repeated_int64(MutableFieldRef &field, const StringView &sv) const; 110 | 111 | void _set_repeated_uint32(MutableFieldRef &field, const StringView &sv) const; 112 | 113 | void _set_repeated_uint64(MutableFieldRef &field, const StringView &sv) const; 114 | 115 | void _set_repeated_double(MutableFieldRef &field, const StringView &sv) const; 116 | 117 | void _set_repeated_float(MutableFieldRef &field, const StringView &sv) const; 118 | 119 | void _set_repeated_bool(MutableFieldRef &field, const StringView &sv) const; 120 | 121 | void _set_repeated_enum(MutableFieldRef &field, const StringView &sv) const; 122 | 123 | void _set_repeated_string(MutableFieldRef &field, const StringView &sv) const; 124 | 125 | void _set_repeated_msg(MutableFieldRef &field, const StringView &sv) const; 126 | 127 | void _set_mapped_int32(MutableFieldRef &field, const StringView &sv) const; 128 | 129 | void _set_mapped_int64(MutableFieldRef &field, const StringView &sv) const; 130 | 131 | void _set_mapped_uint32(MutableFieldRef &field, const StringView &sv) const; 132 | 133 | void _set_mapped_uint64(MutableFieldRef &field, const StringView &sv) const; 134 | 135 | void _set_mapped_double(MutableFieldRef &field, const StringView &sv) const; 136 | 137 | void _set_mapped_float(MutableFieldRef &field, const StringView &sv) const; 138 | 139 | void _set_mapped_bool(MutableFieldRef &field, const StringView &sv) const; 140 | 141 | void _set_mapped_enum(MutableFieldRef &field, const StringView &sv) const; 142 | 143 | void _set_mapped_string(MutableFieldRef &field, const StringView &sv) const; 144 | 145 | void _set_mapped_msg(MutableFieldRef &field, const StringView &sv) const; 146 | }; 147 | 148 | } 149 | 150 | } 151 | 152 | } 153 | 154 | #endif // end SEWENEW_REDISPROTOBUF_SET_COMMANDS_H 155 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/type_command.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "type_command.h" 18 | #include "errors.h" 19 | #include "redis_protobuf.h" 20 | #include "utils.h" 21 | 22 | namespace sw { 23 | 24 | namespace redis { 25 | 26 | namespace pb { 27 | 28 | int TypeCommand::run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const { 29 | try { 30 | assert(ctx != nullptr); 31 | 32 | auto args = _parse_args(argv, argc); 33 | 34 | auto key = api::open_key(ctx, args.key_name, api::KeyMode::READONLY); 35 | if (!api::key_exists(key.get(), RedisProtobuf::instance().type())) { 36 | return RedisModule_ReplyWithNull(ctx); 37 | } 38 | 39 | auto *msg = api::get_msg_by_key(key.get()); 40 | assert(msg != nullptr); 41 | 42 | auto type = _format_type(msg->GetTypeName()); 43 | 44 | return RedisModule_ReplyWithSimpleString(ctx, type.data()); 45 | } catch (const WrongArityError &err) { 46 | return RedisModule_WrongArity(ctx); 47 | } catch (const Error &err) { 48 | return api::reply_with_error(ctx, err); 49 | } 50 | } 51 | 52 | TypeCommand::Args TypeCommand::_parse_args(RedisModuleString **argv, int argc) const { 53 | assert(argv != nullptr); 54 | 55 | if (argc != 2) { 56 | throw WrongArityError(); 57 | } 58 | 59 | return Args{argv[1]}; 60 | } 61 | 62 | std::string TypeCommand::_format_type(std::string type) const { 63 | auto pos = type.find('.'); 64 | if (pos == std::string::npos) { 65 | // No namespace. 66 | return type; 67 | } 68 | 69 | std::string type_str; 70 | type_str.reserve(type.size() * 2); 71 | 72 | type_str = type.substr(0, pos); 73 | 74 | for (std::size_t idx = pos; idx != type.size(); ++idx) { 75 | auto ch = type[idx]; 76 | if (ch != '.') { 77 | type_str.push_back(ch); 78 | } else { 79 | type_str.append("::"); 80 | } 81 | } 82 | 83 | return type_str; 84 | } 85 | 86 | } 87 | 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/type_command.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_TYPE_COMMANDS_H 18 | #define SEWENEW_REDISPROTOBUF_TYPE_COMMANDS_H 19 | 20 | #include "module_api.h" 21 | #include 22 | #include 23 | #include "utils.h" 24 | 25 | namespace sw { 26 | 27 | namespace redis { 28 | 29 | namespace pb { 30 | 31 | // command: PB.TYPE key 32 | // return: Simple string reply: the protobuf type of the key. 33 | // If key doesn't exist, return a nil reply. 34 | class TypeCommand { 35 | public: 36 | int run(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) const; 37 | 38 | private: 39 | struct Args { 40 | RedisModuleString *key_name; 41 | }; 42 | 43 | Args _parse_args(RedisModuleString **argv, int argc) const; 44 | 45 | std::string _format_type(std::string type) const; 46 | }; 47 | 48 | } 49 | 50 | } 51 | 52 | } 53 | 54 | #endif // end SEWENEW_REDISPROTOBUF_TYPE_COMMANDS_H 55 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/utils.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "utils.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "errors.h" 25 | 26 | namespace { 27 | 28 | mode_t file_type(const std::string &file); 29 | 30 | } 31 | 32 | namespace sw { 33 | 34 | namespace redis { 35 | 36 | namespace pb { 37 | 38 | StringView::StringView(RedisModuleString *str) { 39 | if (str == nullptr) { 40 | throw Error("null string"); 41 | } 42 | 43 | _data = RedisModule_StringPtrLen(str, &_size); 44 | } 45 | 46 | namespace util { 47 | 48 | std::string msg_to_json(const gp::Message &msg) { 49 | std::string json; 50 | auto status = gp::util::MessageToJsonString(msg, &json); 51 | if (!status.ok()) { 52 | throw Error("failed to parse message to json"); 53 | } 54 | 55 | return json; 56 | } 57 | 58 | int32_t sv_to_int32(const StringView &sv) { 59 | try { 60 | return std::stoi(std::string(sv.data(), sv.size())); 61 | } catch (const std::exception &e) { 62 | throw Error("not int32"); 63 | } 64 | } 65 | 66 | int64_t sv_to_int64(const StringView &sv) { 67 | try { 68 | return std::stoll(std::string(sv.data(), sv.size())); 69 | } catch (const std::exception &e) { 70 | throw Error("not int64"); 71 | } 72 | } 73 | 74 | uint32_t sv_to_uint32(const StringView &sv) { 75 | try { 76 | // TODO: check if it's overflow 77 | return std::stoul(std::string(sv.data(), sv.size())); 78 | } catch (const std::exception &e) { 79 | throw Error("not uint32"); 80 | } 81 | } 82 | 83 | uint64_t sv_to_uint64(const StringView &sv) { 84 | try { 85 | return std::stoull(std::string(sv.data(), sv.size())); 86 | } catch (const std::exception &e) { 87 | throw Error("not uint64"); 88 | } 89 | } 90 | 91 | double sv_to_double(const StringView &sv) { 92 | try { 93 | return std::stod(std::string(sv.data(), sv.size())); 94 | } catch (const std::exception &e) { 95 | throw Error("not double"); 96 | } 97 | } 98 | 99 | float sv_to_float(const StringView &sv) { 100 | try { 101 | return std::stof(std::string(sv.data(), sv.size())); 102 | } catch (const std::exception &e) { 103 | throw Error("not float"); 104 | } 105 | } 106 | 107 | bool sv_to_bool(const StringView &sv) { 108 | bool b = false; 109 | auto s = std::string(sv.data(), sv.size()); 110 | // TODO: make it case insensitive 111 | if (s == "true") { 112 | b = true; 113 | } else if (s == "false") { 114 | b = false; 115 | } else { 116 | try { 117 | auto val = std::stoi(s); 118 | if (val == 0) { 119 | b = false; 120 | } else { 121 | b = true; 122 | } 123 | } catch (const std::exception &e) { 124 | throw Error("not bool"); 125 | } 126 | } 127 | 128 | return b; 129 | } 130 | 131 | std::string sv_to_string(const StringView &sv) { 132 | return std::string(sv.data(), sv.size()); 133 | } 134 | 135 | bool str_case_equal(const StringView &s1, const StringView &s2) { 136 | if (s1.size() != s2.size()) { 137 | return false; 138 | } 139 | 140 | const auto *p1 = s1.data(); 141 | const auto *p2 = s2.data(); 142 | for (std::size_t idx = 0; idx != s1.size(); ++idx) { 143 | if (static_cast(std::toupper(static_cast(p1[idx]))) 144 | != static_cast(std::toupper(static_cast(p2[idx])))) { 145 | return false; 146 | } 147 | } 148 | 149 | return true; 150 | } 151 | 152 | } 153 | 154 | namespace io { 155 | 156 | bool is_regular(const std::string &file) { 157 | return S_ISREG(file_type(file)); 158 | } 159 | 160 | bool is_directory(const std::string &file) { 161 | return S_ISDIR(file_type(file)); 162 | } 163 | 164 | std::vector list_dir(const std::string &path) { 165 | if (!is_directory(path)) { 166 | throw Error(path + " is not a directory"); 167 | } 168 | 169 | auto *dir = opendir(path.c_str()); 170 | if (dir == nullptr) { 171 | throw Error("failed to open directory: " + path); 172 | } 173 | 174 | std::vector files; 175 | dirent *entry = nullptr; 176 | while ((entry = readdir(dir)) != nullptr) { 177 | std::string name = entry->d_name; 178 | 179 | // Skip "." and ".." 180 | if (name == "." || name == "..") { 181 | continue; 182 | } 183 | 184 | auto file_path = path + "/" + name; 185 | if (is_directory(file_path)) { 186 | auto sub_files = list_dir(file_path); 187 | files.insert(files.end(), sub_files.begin(), sub_files.end()); 188 | } else { 189 | files.push_back(file_path); 190 | } 191 | } 192 | 193 | closedir(dir); 194 | 195 | return files; 196 | } 197 | 198 | std::string extension(const std::string &file) { 199 | auto pos = file.rfind("."); 200 | if (pos == std::string::npos) { 201 | return {}; 202 | } 203 | 204 | return file.substr(pos + 1); 205 | } 206 | 207 | void remove_file(const std::string &path) { 208 | std::remove(path.data()); 209 | } 210 | 211 | } 212 | 213 | } 214 | 215 | } 216 | 217 | } 218 | 219 | namespace { 220 | 221 | mode_t file_type(const std::string &file) { 222 | struct stat buf; 223 | if (stat(file.c_str(), &buf) < 0) { 224 | throw sw::redis::pb::Error("failed to get file status: " + file); 225 | } 226 | 227 | return buf.st_mode; 228 | } 229 | 230 | } 231 | -------------------------------------------------------------------------------- /src/sw/redis-protobuf/utils.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2019 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_UTILS_H 18 | #define SEWENEW_REDISPROTOBUF_UTILS_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "module_api.h" 26 | 27 | namespace sw { 28 | 29 | namespace redis { 30 | 31 | namespace pb { 32 | 33 | namespace gp = google::protobuf; 34 | 35 | using MsgUPtr = std::unique_ptr; 36 | 37 | // By now, not all compilers support std::string_view, 38 | // so we make our own implementation. 39 | class StringView { 40 | public: 41 | constexpr StringView() noexcept = default; 42 | 43 | constexpr StringView(const char *data, std::size_t size) : _data(data), _size(size) {} 44 | 45 | StringView(const char *data) : _data(data), _size(std::strlen(data)) {} 46 | 47 | StringView(const std::string &str) : _data(str.data()), _size(str.size()) {} 48 | 49 | StringView(RedisModuleString *str); 50 | 51 | constexpr StringView(const StringView &) noexcept = default; 52 | 53 | StringView& operator=(const StringView &) noexcept = default; 54 | 55 | constexpr const char* data() const noexcept { 56 | return _data; 57 | } 58 | 59 | constexpr std::size_t size() const noexcept { 60 | return _size; 61 | } 62 | 63 | constexpr bool empty() const noexcept { 64 | return _size == 0; 65 | } 66 | 67 | private: 68 | const char *_data = nullptr; 69 | std::size_t _size = 0; 70 | }; 71 | 72 | template 73 | class Optional { 74 | public: 75 | Optional() = default; 76 | 77 | Optional(const Optional &) = default; 78 | Optional& operator=(const Optional &) = default; 79 | 80 | Optional(Optional &&) = default; 81 | Optional& operator=(Optional &&) = default; 82 | 83 | ~Optional() = default; 84 | 85 | template 86 | explicit Optional(Args &&...args) : _value(true, T(std::forward(args)...)) {} 87 | 88 | explicit operator bool() const { 89 | return _value.first; 90 | } 91 | 92 | T& value() { 93 | return _value.second; 94 | } 95 | 96 | const T& value() const { 97 | return _value.second; 98 | } 99 | 100 | T* operator->() { 101 | return &(_value.second); 102 | } 103 | 104 | const T* operator->() const { 105 | return &(_value.second); 106 | } 107 | 108 | T& operator*() { 109 | return _value.second; 110 | } 111 | 112 | const T& operator*() const { 113 | return _value.second; 114 | } 115 | 116 | void reset() noexcept { 117 | _value.first = false; 118 | } 119 | 120 | private: 121 | std::pair _value; 122 | }; 123 | 124 | namespace util { 125 | 126 | std::string msg_to_json(const gp::Message &msg); 127 | 128 | int32_t sv_to_int32(const StringView &sv); 129 | 130 | int64_t sv_to_int64(const StringView &sv); 131 | 132 | uint32_t sv_to_uint32(const StringView &sv); 133 | 134 | uint64_t sv_to_uint64(const StringView &sv); 135 | 136 | double sv_to_double(const StringView &sv); 137 | 138 | float sv_to_float(const StringView &sv); 139 | 140 | bool sv_to_bool(const StringView &sv); 141 | 142 | std::string sv_to_string(const StringView &sv); 143 | 144 | bool str_case_equal(const StringView &s1, const StringView &s2); 145 | 146 | } 147 | 148 | namespace io { 149 | 150 | bool is_regular(const std::string &file); 151 | 152 | bool is_directory(const std::string &file); 153 | 154 | std::vector list_dir(const std::string &path); 155 | 156 | std::string extension(const std::string &file); 157 | 158 | void remove_file(const std::string &path); 159 | 160 | } 161 | 162 | } 163 | 164 | } 165 | 166 | } 167 | 168 | #endif // end SEWENEW_REDISPROTOBUF_UTILS_H 169 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/append_test.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "append_test.h" 18 | #include "utils.h" 19 | 20 | namespace sw { 21 | 22 | namespace redis { 23 | 24 | namespace pb { 25 | 26 | namespace test { 27 | 28 | void AppendTest::_run(sw::redis::Redis &r) { 29 | auto key = test_key("append"); 30 | 31 | KeyDeleter deleter(r, key); 32 | 33 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 34 | R"({"i" : 1})") == 1, 35 | "failed to test pb.append command"); 36 | 37 | REDIS_ASSERT(r.command("PB.APPEND", key, "Msg", 38 | "/sub/s", "abc") == 3, 39 | "failed to test appending string"); 40 | 41 | REDIS_ASSERT(r.command("PB.APPEND", key, "Msg", 42 | "/sub/s", "123") == 6, 43 | "failed to test appending string"); 44 | 45 | REDIS_ASSERT(r.command("PB.GET", key, "Msg", 46 | "/sub/s") == "abc123", 47 | "failed to test pb.append"); 48 | 49 | REDIS_ASSERT(r.command("PB.APPEND", key, "Msg", 50 | "/arr", 1, 2) == 2, 51 | "failed to test appending array"); 52 | 53 | REDIS_ASSERT(r.command("PB.APPEND", key, "Msg", 54 | "/arr", 3, 4) == 4, 55 | "failed to test appending array"); 56 | 57 | REDIS_ASSERT(r.command("PB.GET", key, "Msg", 58 | "/arr/0") == 1, 59 | "failed to test pb.append"); 60 | try { 61 | r.command("PB.APPEND", key, "Msg", 62 | "/i", 2); 63 | REDIS_ASSERT(false, "failed to test pb.append"); 64 | } catch (const sw::redis::Error &) { 65 | } 66 | } 67 | 68 | } 69 | 70 | } 71 | 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/append_test.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_TEST_APPEND_TEST_H 18 | #define SEWENEW_REDISPROTOBUF_TEST_APPEND_TEST_H 19 | 20 | #include "proto_test.h" 21 | 22 | namespace sw { 23 | 24 | namespace redis { 25 | 26 | namespace pb { 27 | 28 | namespace test { 29 | 30 | class AppendTest : public ProtoTest { 31 | public: 32 | explicit AppendTest(sw::redis::Redis &r) : ProtoTest("PB.APPEND", r) {} 33 | 34 | private: 35 | virtual void _run(sw::redis::Redis &r) override; 36 | }; 37 | 38 | } 39 | 40 | } 41 | 42 | } 43 | 44 | } 45 | 46 | #endif // end SEWENEW_REDISPROTOBUF_TEST_APPEND_TEST_H 47 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/clear_test.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "clear_test.h" 18 | #include "utils.h" 19 | 20 | namespace sw { 21 | 22 | namespace redis { 23 | 24 | namespace pb { 25 | 26 | namespace test { 27 | 28 | void ClearTest::_run(sw::redis::Redis &r) { 29 | auto key = test_key("clear"); 30 | 31 | KeyDeleter deleter(r, key); 32 | 33 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 34 | R"({"i" : 1, "sub" : {"s" : "hello"}, "arr" : [1, 2]})") == 1, 35 | "failed to test pb.append command"); 36 | 37 | REDIS_ASSERT(r.command("PB.CLEAR", key, "Msg") == 1 && 38 | r.command("PB.GET", key, "Msg", "/i") == 0, 39 | "failed to test clear whole message"); 40 | 41 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 42 | R"({"i" : 1, "sub" : {"s" : "hello", "i" : 23}, "arr" : [1, 2]})") == 1, 43 | "failed to test pb.clear command"); 44 | 45 | REDIS_ASSERT(r.command("PB.CLEAR", key, "Msg", "/i") == 1 && 46 | r.command("PB.GET", key, "Msg", "/i") == 0, 47 | "failed to test clear int"); 48 | 49 | REDIS_ASSERT(r.command("PB.CLEAR", key, "Msg", "/sub/s") == 1 && 50 | r.command("PB.GET", key, "Msg", "/sub/s").empty(), 51 | "failed to test clear string"); 52 | 53 | REDIS_ASSERT(r.command("PB.CLEAR", key, "Msg", "/arr") == 1 && 54 | r.command("PB.LEN", key, "Msg", "/arr") == 0, 55 | "failed to test clear array"); 56 | 57 | REDIS_ASSERT(r.command("PB.CLEAR", key, "Msg", "/sub") == 1 && 58 | r.command("PB.GET", key, "Msg", "/sub/i") == 0, 59 | "failed to test clear sub message"); 60 | } 61 | 62 | } 63 | 64 | } 65 | 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/clear_test.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_TEST_CLEAR_TEST_H 18 | #define SEWENEW_REDISPROTOBUF_TEST_CLEAR_TEST_H 19 | 20 | #include "proto_test.h" 21 | 22 | namespace sw { 23 | 24 | namespace redis { 25 | 26 | namespace pb { 27 | 28 | namespace test { 29 | 30 | class ClearTest : public ProtoTest { 31 | public: 32 | explicit ClearTest(sw::redis::Redis &r) : ProtoTest("PB.CLEAR", r) {} 33 | 34 | private: 35 | virtual void _run(sw::redis::Redis &r) override; 36 | }; 37 | 38 | } 39 | 40 | } 41 | 42 | } 43 | 44 | } 45 | 46 | #endif // end SEWENEW_REDISPROTOBUF_TEST_CLEAR_TEST_H 47 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/del_test.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "del_test.h" 18 | #include "utils.h" 19 | 20 | namespace sw { 21 | 22 | namespace redis { 23 | 24 | namespace pb { 25 | 26 | namespace test { 27 | 28 | void DelTest::_run(sw::redis::Redis &r) { 29 | auto key = test_key("del"); 30 | 31 | KeyDeleter deleter(r, key); 32 | 33 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 34 | R"({"i" : 1, "arr" : [1, 2], "m" : {"key" : "val"}})") == 1, 35 | "failed to test pb.del command"); 36 | 37 | REDIS_ASSERT(r.command("PB.DEL", key, "Msg", "/arr/0") == 1 && 38 | r.command("PB.LEN", key, "Msg", "/arr") == 1, 39 | "failed to test del array element"); 40 | /* 41 | TODO: support delete whole array and whole map, and map element 42 | REDIS_ASSERT(r.command("PB.DEL", key, "Msg", "/arr") == 1 && 43 | r.command("PB.LEN", key, "Msg", "/arr") == 0, 44 | "failed to test del array"); 45 | 46 | REDIS_ASSERT(r.command("PB.DEL", key, "Msg", "/m") == 1 && 47 | r.command("PB.LEN", key, "Msg", "/m") == 0, 48 | "failed to test del map"); 49 | */ 50 | 51 | REDIS_ASSERT(r.command("PB.DEL", key, "Msg") == 1 && 52 | r.exists(key) == 0, 53 | "failed to test del message"); 54 | } 55 | 56 | } 57 | 58 | } 59 | 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/del_test.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_TEST_DEL_TEST_H 18 | #define SEWENEW_REDISPROTOBUF_TEST_DEL_TEST_H 19 | 20 | #include "proto_test.h" 21 | 22 | namespace sw { 23 | 24 | namespace redis { 25 | 26 | namespace pb { 27 | 28 | namespace test { 29 | 30 | class DelTest : public ProtoTest { 31 | public: 32 | explicit DelTest(sw::redis::Redis &r) : ProtoTest("PB.DEL", r) {} 33 | 34 | private: 35 | virtual void _run(sw::redis::Redis &r) override; 36 | }; 37 | 38 | } 39 | 40 | } 41 | 42 | } 43 | 44 | } 45 | 46 | #endif // end SEWENEW_REDISPROTOBUF_TEST_DEL_TEST_H 47 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/import_test.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "import_test.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "utils.h" 23 | 24 | namespace sw { 25 | 26 | namespace redis { 27 | 28 | namespace pb { 29 | 30 | namespace test { 31 | 32 | void ImportTest::_run(sw::redis::Redis &r) { 33 | auto key = test_key("import"); 34 | 35 | KeyDeleter deleter(r, key); 36 | 37 | std::string name{"test_import.proto"}; 38 | auto proto = R"( 39 | syntax = "proto3"; 40 | package sw.redis.pb; 41 | message Msg { 42 | int32 i = 1; 43 | string s = 2; 44 | } 45 | )"; 46 | r.command("PB.IMPORT", name, proto); 47 | 48 | // Ensure proto has been loaded 49 | std::this_thread::sleep_for(std::chrono::seconds(1)); 50 | 51 | auto res = r.command>("PB.LASTIMPORT"); 52 | REDIS_ASSERT(res.size() == 1 && res[name] == "OK", 53 | "failed to test pb.import command"); 54 | 55 | REDIS_ASSERT(r.command("PB.SET", key, "sw.redis.pb.Msg", "/i", 123) && 56 | r.command("PB.GET", key, "sw.redis.pb.Msg", "/i") == 123, 57 | "failed to test pb.import command"); 58 | } 59 | 60 | } 61 | 62 | } 63 | 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/import_test.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_TEST_IMPORT_TEST_H 18 | #define SEWENEW_REDISPROTOBUF_TEST_IMPORT_TEST_H 19 | 20 | #include "proto_test.h" 21 | 22 | namespace sw { 23 | 24 | namespace redis { 25 | 26 | namespace pb { 27 | 28 | namespace test { 29 | 30 | class ImportTest : public ProtoTest { 31 | public: 32 | explicit ImportTest(sw::redis::Redis &r) : ProtoTest("PB.IMPORT", r) {} 33 | 34 | private: 35 | virtual void _run(sw::redis::Redis &r) override; 36 | }; 37 | 38 | } 39 | 40 | } 41 | 42 | } 43 | 44 | } 45 | 46 | #endif // end SEWENEW_REDISPROTOBUF_TEST_IMPORT_TEST_H 47 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/len_test.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "len_test.h" 18 | #include "utils.h" 19 | 20 | namespace sw { 21 | 22 | namespace redis { 23 | 24 | namespace pb { 25 | 26 | namespace test { 27 | 28 | void LenTest::_run(sw::redis::Redis &r) { 29 | auto key = test_key("len"); 30 | 31 | KeyDeleter deleter(r, key); 32 | 33 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 34 | R"({"sub" : {"s" : "hello"}, "arr" : [1, 2, 3], "m" : {"k1" : "v1", "k2" : "v2"}})") == 1, 35 | "failed to test pb.len command"); 36 | 37 | REDIS_ASSERT(r.command("PB.LEN", key, "Msg", 38 | "/sub/s") == 5, 39 | "failed to test pb.len with string"); 40 | 41 | REDIS_ASSERT(r.command("PB.LEN", key, "Msg", 42 | "/arr") == 3, 43 | "failed to test pb.len with array"); 44 | 45 | REDIS_ASSERT(r.command("PB.LEN", key, "Msg", 46 | "/m") == 2, 47 | "failed to test pb.len with map"); 48 | } 49 | 50 | } 51 | 52 | } 53 | 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/len_test.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_TEST_LEN_TEST_H 18 | #define SEWENEW_REDISPROTOBUF_TEST_LEN_TEST_H 19 | 20 | #include "proto_test.h" 21 | 22 | namespace sw { 23 | 24 | namespace redis { 25 | 26 | namespace pb { 27 | 28 | namespace test { 29 | 30 | class LenTest : public ProtoTest { 31 | public: 32 | explicit LenTest(sw::redis::Redis &r) : ProtoTest("PB.LEN", r) {} 33 | 34 | private: 35 | virtual void _run(sw::redis::Redis &r) override; 36 | }; 37 | 38 | } 39 | 40 | } 41 | 42 | } 43 | 44 | } 45 | 46 | #endif // end SEWENEW_REDISPROTOBUF_TEST_LEN_TEST_H 47 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/merge_test.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "merge_test.h" 18 | #include "utils.h" 19 | 20 | namespace sw { 21 | 22 | namespace redis { 23 | 24 | namespace pb { 25 | 26 | namespace test { 27 | 28 | void MergeTest::_run(sw::redis::Redis &r) { 29 | auto key = test_key("merge"); 30 | 31 | KeyDeleter deleter(r, key); 32 | 33 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 34 | R"({"i" : 1, "m" : {"k1" : "v1"}})") == 1, 35 | "failed to test pb.merge command"); 36 | 37 | REDIS_ASSERT(r.command("PB.MERGE", key, "Msg", 38 | R"({"arr" : [1, 2]})") == 1, 39 | "failed to test pb.merge command"); 40 | 41 | REDIS_ASSERT(r.command("PB.GET", key, "Msg", 42 | "/arr/0") == 1, 43 | "failed to test pb.merge command"); 44 | 45 | REDIS_ASSERT(r.command("PB.LEN", key, "Msg", 46 | "/arr") == 2, 47 | "failed to test pb.merge command"); 48 | 49 | REDIS_ASSERT(r.command("PB.MERGE", key, "Msg", 50 | R"({"m" : {"k2" : "v2"}})") == 1, 51 | "failed to test pb.merge command"); 52 | 53 | REDIS_ASSERT(r.command("PB.GET", key, "Msg", 54 | "/m/k2") == "v2", 55 | "failed to test pb.merge command"); 56 | } 57 | 58 | } 59 | 60 | } 61 | 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/merge_test.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_TEST_MERGE_TEST_H 18 | #define SEWENEW_REDISPROTOBUF_TEST_MERGE_TEST_H 19 | 20 | #include "proto_test.h" 21 | 22 | namespace sw { 23 | 24 | namespace redis { 25 | 26 | namespace pb { 27 | 28 | namespace test { 29 | 30 | class MergeTest : public ProtoTest { 31 | public: 32 | explicit MergeTest(sw::redis::Redis &r) : ProtoTest("PB.MERGE", r) {} 33 | 34 | private: 35 | virtual void _run(sw::redis::Redis &r) override; 36 | }; 37 | 38 | } 39 | 40 | } 41 | 42 | } 43 | 44 | } 45 | 46 | #endif // end SEWENEW_REDISPROTOBUF_TEST_MERGE_TEST_H 47 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/proto_test.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "proto_test.h" 18 | #include 19 | 20 | namespace sw { 21 | 22 | namespace redis { 23 | 24 | namespace pb { 25 | 26 | namespace test { 27 | 28 | void ProtoTest::run() { 29 | std::cout << "Test " << _name << ": "; 30 | 31 | _run(_redis); 32 | 33 | std::cout << "pass" << std::endl; 34 | } 35 | 36 | } 37 | 38 | } 39 | 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/proto_test.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_TEST_PROTO_TEST_H 18 | #define SEWENEW_REDISPROTOBUF_TEST_PROTO_TEST_H 19 | 20 | #include 21 | 22 | namespace sw { 23 | 24 | namespace redis { 25 | 26 | namespace pb { 27 | 28 | namespace test { 29 | 30 | class ProtoTest { 31 | public: 32 | ProtoTest(const std::string &name, sw::redis::Redis &r) : 33 | _name(name), _redis(r) {} 34 | 35 | virtual ~ProtoTest() = default; 36 | 37 | void run(); 38 | 39 | const std::string& name() const { 40 | return _name; 41 | } 42 | 43 | private: 44 | virtual void _run(sw::redis::Redis &r) = 0; 45 | 46 | std::string _name; 47 | 48 | sw::redis::Redis &_redis; 49 | }; 50 | 51 | } 52 | 53 | } 54 | 55 | } 56 | 57 | } 58 | 59 | #endif // endif SEWENEW_REDISPROTOBUF_TEST_PROTO_TEST_H 60 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/schema_test.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "schema_test.h" 18 | #include "utils.h" 19 | 20 | namespace sw { 21 | 22 | namespace redis { 23 | 24 | namespace pb { 25 | 26 | namespace test { 27 | 28 | void SchemaTest::_run(sw::redis::Redis &r) { 29 | auto schema = r.command("PB.SCHEMA", "Msg"); 30 | REDIS_ASSERT(schema && !schema->empty(), "failed to test pb.schema command"); 31 | 32 | schema = r.command("PB.SCHEMA", 33 | "sw.redis.pb.not-exist-Msg-type"); 34 | REDIS_ASSERT(!schema, "failed to test pb.schema command"); 35 | } 36 | 37 | } 38 | 39 | } 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/schema_test.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_TEST_SCHEMA_TEST_H 18 | #define SEWENEW_REDISPROTOBUF_TEST_SCHEMA_TEST_H 19 | 20 | #include "proto_test.h" 21 | 22 | namespace sw { 23 | 24 | namespace redis { 25 | 26 | namespace pb { 27 | 28 | namespace test { 29 | 30 | class SchemaTest : public ProtoTest { 31 | public: 32 | explicit SchemaTest(sw::redis::Redis &r) : ProtoTest("PB.SCHEMA", r) {} 33 | 34 | private: 35 | virtual void _run(sw::redis::Redis &r) override; 36 | }; 37 | 38 | } 39 | 40 | } 41 | 42 | } 43 | 44 | } 45 | 46 | #endif // end SEWENEW_REDISPROTOBUF_TEST_SCHEMA_TEST_H 47 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/set_get_test.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "set_get_test.h" 18 | #include "utils.h" 19 | #include 20 | 21 | namespace sw { 22 | 23 | namespace redis { 24 | 25 | namespace pb { 26 | 27 | namespace test { 28 | 29 | void SetGetTest::_run(sw::redis::Redis &r) { 30 | auto key = test_key("set-get"); 31 | 32 | KeyDeleter deleter(r, key); 33 | 34 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 35 | R"({"i" : 1})") == 1 && 36 | r.command("PB.GET", key, "Msg", "/i") == 1, 37 | "failed to test pb.set and pb.get command"); 38 | 39 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 40 | "/i", 2) == 1 && 41 | r.command("PB.GET", key, "Msg", "/i") == 2, 42 | "failed to test pb.set and pb.get command"); 43 | 44 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 45 | R"({"sub" : {"s" : "hello"}})") == 1 && 46 | r.command("PB.GET", key, "Msg", "/sub/s") == "hello", 47 | "failed to test pb.set and pb.get command"); 48 | 49 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 50 | "/sub/s", "world") == 1 && 51 | r.command("PB.GET", key, "Msg", "/sub/s") == "world", 52 | "failed to test pb.set and pb.get command"); 53 | 54 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 55 | R"({"i" : 123, "sub" : {"s" : "hello", "i" : 123}, "arr" : [1, 2], "m" : {"k" : "v"}})") == 1 && 56 | r.command("PB.GET", key, "Msg", "/sub/s") == "hello" && 57 | r.command("PB.GET", key, "Msg", "/arr/0") == 1, 58 | "failed to test pb.set and pb.get command"); 59 | 60 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 61 | "/m/key", "world") == 1 && 62 | r.command("PB.GET", key, "Msg", "/m/key") == "world", 63 | "failed to test pb.set and pb.get command"); 64 | 65 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 66 | R"({"arr" : [4, 5, 6]})") == 1, 67 | "failed to test pb.set command"); 68 | 69 | auto arr = r.command>("PB.GET", key, "Msg", "/arr"); 70 | auto tmp = std::vector{4, 5, 6}; 71 | REDIS_ASSERT(arr == tmp, "failed to test pb.get command"); 72 | 73 | REDIS_ASSERT(r.command("PB.SET", key, "--NX", "Msg", 74 | "/sub/s", "world") == 0, 75 | "failed to test pb.set and pb.get command"); 76 | } 77 | 78 | } 79 | 80 | } 81 | 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/set_get_test.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_TEST_SET_TEST_TEST_H 18 | #define SEWENEW_REDISPROTOBUF_TEST_SET_TEST_TEST_H 19 | 20 | #include "proto_test.h" 21 | 22 | namespace sw { 23 | 24 | namespace redis { 25 | 26 | namespace pb { 27 | 28 | namespace test { 29 | 30 | class SetGetTest : public ProtoTest { 31 | public: 32 | explicit SetGetTest(sw::redis::Redis &r) : ProtoTest("PB.GET PB.SET", r) {} 33 | 34 | private: 35 | virtual void _run(sw::redis::Redis &r) override; 36 | }; 37 | 38 | } 39 | 40 | } 41 | 42 | } 43 | 44 | } 45 | 46 | #endif // end SEWENEW_REDISPROTOBUF_TEST_SET_TEST_TEST_H 47 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/test_main.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include 18 | #include 19 | #include "append_test.h" 20 | #include "clear_test.h" 21 | #include "del_test.h" 22 | #include "type_test.h" 23 | #include "schema_test.h" 24 | #include "set_get_test.h" 25 | #include "len_test.h" 26 | #include "merge_test.h" 27 | #include "import_test.h" 28 | 29 | int main() { 30 | try { 31 | auto r = sw::redis::Redis("tcp://127.0.0.1"); 32 | 33 | sw::redis::pb::test::AppendTest append_test(r); 34 | append_test.run(); 35 | 36 | sw::redis::pb::test::ClearTest clear_test(r); 37 | clear_test.run(); 38 | 39 | sw::redis::pb::test::DelTest del_test(r); 40 | del_test.run(); 41 | 42 | sw::redis::pb::test::TypeTest type_test(r); 43 | type_test.run(); 44 | 45 | sw::redis::pb::test::SchemaTest schema_test(r); 46 | schema_test.run(); 47 | 48 | sw::redis::pb::test::SetGetTest set_get_test(r); 49 | set_get_test.run(); 50 | 51 | sw::redis::pb::test::LenTest len_test(r); 52 | len_test.run(); 53 | 54 | sw::redis::pb::test::MergeTest merge_test(r); 55 | merge_test.run(); 56 | 57 | sw::redis::pb::test::ImportTest import_test(r); 58 | import_test.run(); 59 | 60 | std::cout << "pass all tests" << std::endl; 61 | } catch (const sw::redis::Error &e) { 62 | std::cerr << "failed to do test: " << e.what() << std::endl; 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/type_test.cpp: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #include "type_test.h" 18 | #include "utils.h" 19 | 20 | namespace sw { 21 | 22 | namespace redis { 23 | 24 | namespace pb { 25 | 26 | namespace test { 27 | 28 | void TypeTest::_run(sw::redis::Redis &r) { 29 | auto key = test_key("type"); 30 | 31 | KeyDeleter deleter(r, key); 32 | 33 | REDIS_ASSERT(r.command("PB.SET", key, "Msg", 34 | R"({"i" : 1})") == 1, 35 | "failed to test pb.type command"); 36 | 37 | auto type = r.command("PB.TYPE", key); 38 | REDIS_ASSERT(type && *type == "Msg", 39 | "failed to test pb.type command"); 40 | 41 | type = r.command("PB.TYPE", "sw.redis.pb.not-exist-Msg-type"); 42 | REDIS_ASSERT(!type, "failed to test pb.type command"); 43 | } 44 | 45 | } 46 | 47 | } 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/type_test.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2022 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_TEST_TYPE_TEST_H 18 | #define SEWENEW_REDISPROTOBUF_TEST_TYPE_TEST_H 19 | 20 | #include "proto_test.h" 21 | 22 | namespace sw { 23 | 24 | namespace redis { 25 | 26 | namespace pb { 27 | 28 | namespace test { 29 | 30 | class TypeTest : public ProtoTest { 31 | public: 32 | explicit TypeTest(sw::redis::Redis &r) : ProtoTest("PB.TYPE", r) {} 33 | 34 | private: 35 | virtual void _run(sw::redis::Redis &r) override; 36 | }; 37 | 38 | } 39 | 40 | } 41 | 42 | } 43 | 44 | } 45 | 46 | #endif // end SEWENEW_REDISPROTOBUF_TEST_TYPE_TEST_H 47 | -------------------------------------------------------------------------------- /test/src/sw/redis-protobuf/utils.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | Copyright (c) 2017 sewenew 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | *************************************************************************/ 16 | 17 | #ifndef SEWENEW_REDISPROTOBUF_TEST_UTILS_H 18 | #define SEWENEW_REDISPROTOBUF_TEST_UTILS_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #define REDIS_ASSERT(condition, msg) \ 25 | sw::redis::pb::test::redis_assert((condition), (msg), __FILE__, __LINE__) 26 | 27 | namespace sw { 28 | 29 | namespace redis { 30 | 31 | namespace pb { 32 | 33 | namespace test { 34 | 35 | inline void redis_assert(bool condition, 36 | const std::string &msg, 37 | const std::string &file, 38 | int line) { 39 | if (!condition) { 40 | auto err_msg = "ASSERT: " + msg + ". " + file + ":" + std::to_string(line); 41 | throw Error(err_msg); 42 | } 43 | } 44 | 45 | inline std::string key_prefix(const std::string &key = "") { 46 | static std::string KEY_PREFIX = "sw::redis::test"; 47 | if (!key.empty()) { 48 | KEY_PREFIX = key; 49 | } 50 | 51 | return KEY_PREFIX; 52 | } 53 | 54 | inline std::string test_key(const std::string &k) { 55 | // Key prefix with hash tag, 56 | // so that we can call multiple-key commands on RedisCluster. 57 | return "{" + key_prefix() + "}::" + k; 58 | } 59 | 60 | class KeyDeleter { 61 | public: 62 | template 63 | KeyDeleter(sw::redis::Redis &redis, Input first, Input last) : _redis(redis), _keys(first, last) { 64 | _delete(); 65 | } 66 | 67 | KeyDeleter(sw::redis::Redis &redis, std::initializer_list il) : 68 | KeyDeleter(redis, il.begin(), il.end()) {} 69 | 70 | KeyDeleter(sw::redis::Redis &redis, const std::string &key) : KeyDeleter(redis, {key}) {} 71 | 72 | ~KeyDeleter() { 73 | _delete(); 74 | } 75 | 76 | private: 77 | void _delete() { 78 | if (!_keys.empty()) { 79 | _redis.del(_keys.begin(), _keys.end()); 80 | } 81 | } 82 | 83 | sw::redis::Redis &_redis; 84 | std::vector _keys; 85 | }; 86 | 87 | } 88 | 89 | } 90 | 91 | } 92 | 93 | } 94 | 95 | #endif // end SEWENEW_REDISPROTOBUF_TEST_UTILS_H 96 | --------------------------------------------------------------------------------