├── .clang-format ├── .clang-tidy ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .gitmodules ├── 1C_include ├── AddInDefBase.h ├── ComponentBase.h ├── IAndroidComponentHelper.h ├── IMemoryManager.h ├── MANIFEST.xsd ├── addin.idl ├── addinlib.h ├── com.h ├── mobile.h └── types.h ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── include └── core_as │ └── testable_static_assert.h ├── readme.md ├── sqlite3 ├── CMakeLists.txt ├── sqlite3.c └── sqlite3.h └── src ├── CMakeLists.txt ├── addin_base.cpp ├── addin_base.h ├── dllmain.cpp ├── sqlite.cpp ├── sqlite.h ├── sqlite3_unicode.c ├── stdafx.cpp ├── stdafx.h ├── tests ├── CMakeLists.txt ├── test_addin_base.cpp ├── test_sqlite.cpp └── v8sqlite.epf ├── v8sqlite.def ├── v8sqlite.rc ├── v8sqlite_addin.cpp ├── v8sqlite_addin.h ├── version.h ├── version.script └── version_str.h /.clang-tidy: -------------------------------------------------------------------------------- 1 | CheckOptions: 2 | - key: readability-identifier-naming.ClassCase 3 | value: CamelCase 4 | - key: readability-identifier-naming.ParameterCase 5 | value: camelBack 6 | - key: readability-identifier-naming.ClassMethodCase 7 | value: camelBack 8 | - key: readability-identifier-naming.FunctionCase 9 | value: camelBack 10 | - key: readability-identifier-naming.EnumConstantCase 11 | value: CamelCase 12 | - key: readability-identifier-naming.LocalVariableCase 13 | value: camelBack 14 | - key: readability-identifier-naming.PrivateMemberSuffix 15 | value: _ 16 | - key: readability-identifier-naming.EnumCase 17 | value: CamelCase 18 | - key: readability-identifier-naming.GlobalVariableCase 19 | value: camelBack 20 | - key: readability-identifier-naming.LocalConstantCase 21 | value: aNy_CasE 22 | - key: readability-identifier-naming.ProtectedMemberSuffix 23 | value: _ 24 | - key: readability-identifier-naming.ClassMemberCase 25 | value: camelBack 26 | - key: readability-identifier-naming.MacroDefinitionCase 27 | value: UPPER_CASE 28 | - key: readability-identifier-naming.GlobalVariablePrefix 29 | value: g_ 30 | - key: readability-identifier-naming.GlobalConstantCase 31 | value: UPPER_CASE 32 | - key: performance-move-const-arg.CheckTriviallyCopyableMove 33 | value: false 34 | Checks: '-*, 35 | -bugprone-argument-comment, 36 | -bugprone-branch-clone, 37 | -bugprone-exception-escape, 38 | -bugprone-infinite-loop, 39 | -bugprone-lambda-function-name, 40 | -bugprone-macro-parentheses, 41 | -bugprone-narrowing-conversions, 42 | -bugprone-reserved-identifier, 43 | -bugprone-signed-char-misuse, 44 | -bugprone-string-integer-assignment, 45 | -bugprone-suspicious-include, 46 | -bugprone-use-after-move, 47 | -cppcoreguidelines-avoid-c-arrays, 48 | -cppcoreguidelines-avoid-magic-numbers, 49 | -cppcoreguidelines-avoid-non-const-global-variables, 50 | -cppcoreguidelines-explicit-virtual-functions, 51 | -cppcoreguidelines-init-variables, 52 | -cppcoreguidelines-interfaces-global-init, 53 | -cppcoreguidelines-macro-usage, 54 | -cppcoreguidelines-narrowing-conversions, 55 | -cppcoreguidelines-no-malloc, 56 | -cppcoreguidelines-non-private-member-variables-in-classes, 57 | -cppcoreguidelines-owning-memory, 58 | -cppcoreguidelines-pro-bounds-array-to-pointer-decay, 59 | -cppcoreguidelines-pro-bounds-constant-array-index, 60 | -cppcoreguidelines-pro-bounds-pointer-arithmetic, 61 | -cppcoreguidelines-pro-type-const-cast, 62 | -cppcoreguidelines-pro-type-cstyle-cast, 63 | -cppcoreguidelines-pro-type-member-init, 64 | -cppcoreguidelines-pro-type-reinterpret-cast, 65 | -cppcoreguidelines-pro-type-static-cast-downcast, 66 | -cppcoreguidelines-pro-type-union-access, 67 | -cppcoreguidelines-pro-type-vararg, 68 | -cppcoreguidelines-special-member-functions, 69 | -fuchsia-statically-constructed-objects, 70 | -llvm-namespace-comment, 71 | -misc-definitions-in-headers, 72 | -modernize-avoid-bind, 73 | -modernize-concat-nested-namespaces, 74 | -modernize-deprecated-headers, 75 | -modernize-loop-convert, 76 | -modernize-make-shared, 77 | -modernize-make-unique, 78 | -modernize-pass-by-value, 79 | -modernize-raw-string-literal, 80 | -modernize-use-auto, 81 | -modernize-use-default-member-init, 82 | -modernize-use-emplace, 83 | -modernize-use-equals-default, 84 | -modernize-use-equals-delete, 85 | -modernize-use-nodiscard, 86 | -modernize-use-nullptr, 87 | -modernize-use-using, 88 | -performance-faster-string-find, 89 | -performance-for-range-copy, 90 | -performance-implicit-conversion-in-loop, 91 | -performance-inefficient-algorithm, 92 | -performance-inefficient-string-concatenation, 93 | -performance-inefficient-vector-operation, 94 | -performance-move-const-arg, 95 | -performance-move-constructor-init, 96 | -performance-no-automatic-move, 97 | -performance-no-int-to-ptr, 98 | -performance-noexcept-move-constructor, 99 | -performance-trivially-destructible, 100 | -performance-type-promotion-in-math-fn, 101 | -performance-unnecessary-copy-initialization, 102 | -performance-unnecessary-value-param, 103 | -portability-restrict-system-includes, 104 | -portability-simd-intrinsics, 105 | -readability-avoid-const-params-in-decls, 106 | -readability-braces-around-statements, 107 | -readability-const-return-type, 108 | -readability-container-size-empty, 109 | -readability-convert-member-functions-to-static, 110 | -readability-deleted-default, 111 | -readability-else-after-return, 112 | -readability-function-cognitive-complexity, 113 | -readability-function-size, 114 | -readability-identifier-naming, 115 | -readability-implicit-bool-conversion, 116 | -readability-inconsistent-declaration-parameter-name, 117 | -readability-isolate-declaration, 118 | -readability-magic-numbers, 119 | -readability-make-member-function-const, 120 | -readability-named-parameter, 121 | -readability-non-const-parameter, 122 | -readability-qualified-auto, 123 | -readability-redundant-access-specifiers, 124 | -readability-redundant-control-flow, 125 | -readability-redundant-declaration, 126 | -readability-redundant-member-init, 127 | -readability-redundant-smartptr-get, 128 | -readability-redundant-string-cstr, 129 | -readability-redundant-string-init, 130 | -readability-simplify-boolean-expr, 131 | -readability-static-accessed-through-instance, 132 | -readability-static-definition-in-anonymous-namespace, 133 | -readability-uppercase-literal-suffix, 134 | -readability-use-anyofallof, 135 | bugprone-branch-clone, 136 | bugprone-infinite-loop, 137 | bugprone-lambda-function-name, 138 | bugprone-macro-parentheses, 139 | bugprone-reserved-identifier, 140 | bugprone-string-integer-assignment, 141 | cppcoreguidelines-init-variables, 142 | cppcoreguidelines-no-malloc, 143 | cppcoreguidelines-pro-type-member-init, 144 | misc-definitions-in-headers, 145 | modernize-concat-nested-namespaces, 146 | modernize-make-shared, 147 | modernize-make-unique, 148 | modernize-pass-by-value, 149 | modernize-use-default-member-init, 150 | modernize-use-emplace, 151 | modernize-use-equals-default, 152 | modernize-use-equals-delete, 153 | modernize-use-using, 154 | llvm-namespace-comment, 155 | performance-faster-string-find, 156 | performance-for-range-copy, 157 | performance-inefficient-algorithm, 158 | performance-move-const-arg, 159 | performance-move-constructor-init, 160 | performance-no-automatic-move, 161 | performance-noexcept-move-constructor, 162 | performance-unnecessary-copy-initialization, 163 | performance-unnecessary-value-param, 164 | readability-avoid-const-params-in-decls, 165 | readability-convert-member-functions-to-static, 166 | readability-const-return-type, 167 | readability-identifier-naming, 168 | readability-inconsistent-declaration-parameter-name, 169 | readability-make-member-function-const, 170 | readability-redundant-declaration, 171 | readability-static-definition-in-anonymous-namespace, 172 | arcadia-typeid-name-restriction, 173 | bugprone-use-after-move, 174 | performance-implicit-conversion-in-loop, 175 | readability-identifier-naming' 176 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{as,cpp,h,hpp,inl,txt}] 4 | charset = utf-8-bom 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.htm text eol=crlf 3 | *.html text eol=crlf 4 | renames.txt text eol=crlf 5 | 6 | *.v text eol=lf 7 | *.as text eol=lf 8 | *.cpp text eol=lf 9 | *.h text eol=lf 10 | *.js text eol=lf 11 | 12 | *.feature text 13 | *.md text 14 | *.json text eol=lf 15 | 16 | *.bat text eol=crlf 17 | *.cmd text eol=crlf 18 | *.ps1 text eol=crlf 19 | 20 | *.sh text eol=lf 21 | *.groovy text eol=lf 22 | 23 | # Archives 24 | # 1C 25 | *.xml text eol=crlf 26 | 27 | *.bsl text eol=crlf 28 | *.os text eol=crlf 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _build/ 2 | 1C/ 3 | out/ 4 | !.gitignore 5 | *.ipch 6 | *.VC.* 7 | .vs/ 8 | .vscode/ 9 | CMakeUserPresets.json 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "simstr"] 2 | path = libs/simstr 3 | url = git@github.com:orefkov/simstr.git 4 | -------------------------------------------------------------------------------- /1C_include/AddInDefBase.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Warning!!! 3 | * DO NOT ALTER THIS FILE! 4 | */ 5 | 6 | #ifndef __ADAPTER_DEF_H__ 7 | #define __ADAPTER_DEF_H__ 8 | #include "types.h" 9 | 10 | struct IInterface 11 | { 12 | }; 13 | 14 | 15 | enum Interfaces 16 | { 17 | eIMsgBox = 0, 18 | eIPlatformInfo, 19 | #if defined(__ANDROID__) 20 | eIAndroidComponentHelper, 21 | #endif 22 | eIAttachedInfo, 23 | }; 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | /** 27 | * This class serves as representation of a platform for external 28 | * components External components use it to communicate with a platform. 29 | * 30 | */ 31 | /// Base interface for object components. 32 | class IAddInDefBase 33 | { 34 | public: 35 | virtual ~IAddInDefBase() {} 36 | /// Adds the error message 37 | /** 38 | * @param wcode - error code 39 | * @param source - source of error 40 | * @param descr - description of error 41 | * @param scode - error code (HRESULT) 42 | * @return the result of 43 | */ 44 | virtual bool ADDIN_API AddError(unsigned short wcode, const WCHAR_T* source, 45 | const WCHAR_T* descr, long scode) = 0; 46 | 47 | /// Reads a property value 48 | /** 49 | * @param wszPropName -property name 50 | * @param pVal - value being returned 51 | * @param pErrCode - error code (if any error occured) 52 | * @param errDescriptor - error description (if any error occured) 53 | * @return the result of read. 54 | */ 55 | virtual bool ADDIN_API Read(WCHAR_T* wszPropName, 56 | tVariant* pVal, 57 | long *pErrCode, 58 | WCHAR_T** errDescriptor) = 0; 59 | /// Writes a property value 60 | /** 61 | * @param wszPropName - property name 62 | * @param pVar - new property value 63 | * @return the result of write. 64 | */ 65 | virtual bool ADDIN_API Write(WCHAR_T* wszPropName, 66 | tVariant *pVar) = 0; 67 | 68 | ///Registers profile components 69 | /** 70 | * @param wszProfileName - profile name 71 | * @return the result of 72 | */ 73 | virtual bool ADDIN_API RegisterProfileAs(WCHAR_T* wszProfileName) = 0; 74 | 75 | /// Changes the depth of event buffer 76 | /** 77 | * @param lDepth - new depth of event buffer 78 | * @return the result of 79 | */ 80 | virtual bool ADDIN_API SetEventBufferDepth(long lDepth) = 0; 81 | /// Returns the depth of event buffer 82 | /** 83 | * @return the depth of event buffer 84 | */ 85 | virtual long ADDIN_API GetEventBufferDepth() = 0; 86 | /// Registers external event 87 | /** 88 | * @param wszSource - source of event 89 | * @param wszMessage - event message 90 | * @param wszData - message parameters 91 | * @return the result of 92 | */ 93 | virtual bool ADDIN_API ExternalEvent(WCHAR_T* wszSource, 94 | WCHAR_T* wszMessage, 95 | WCHAR_T* wszData) = 0; 96 | /// Clears event buffer 97 | /** 98 | */ 99 | virtual void ADDIN_API CleanEventBuffer() = 0; 100 | 101 | /// Sets status line contents 102 | /** 103 | * @param wszStatusLine - new status line contents 104 | * @return the result of 105 | */ 106 | virtual bool ADDIN_API SetStatusLine(WCHAR_T* wszStatusLine) = 0; 107 | /// Resets the status line contents 108 | /** 109 | * @return the result of 110 | */ 111 | virtual void ADDIN_API ResetStatusLine() = 0; 112 | }; 113 | 114 | class IAddInDefBaseEx : 115 | public IAddInDefBase 116 | { 117 | public: 118 | virtual ~IAddInDefBaseEx() {} 119 | 120 | virtual IInterface* ADDIN_API GetInterface(Interfaces iface) = 0; 121 | }; 122 | 123 | struct IMsgBox : 124 | public IInterface 125 | { 126 | virtual bool ADDIN_API Confirm(const WCHAR_T* queryText, tVariant* retVal) = 0; 127 | 128 | virtual bool ADDIN_API Alert(const WCHAR_T* text) = 0; 129 | }; 130 | 131 | struct IPlatformInfo : 132 | public IInterface 133 | { 134 | enum AppType 135 | { 136 | eAppUnknown = -1, 137 | eAppThinClient = 0, 138 | eAppThickClient, 139 | eAppWebClient, 140 | eAppServer, 141 | eAppExtConn, 142 | eAppMobileClient, 143 | eAppMobileServer, 144 | }; 145 | 146 | struct AppInfo 147 | { 148 | const WCHAR_T* AppVersion; 149 | const WCHAR_T* UserAgentInformation; 150 | AppType Application; 151 | }; 152 | 153 | virtual const AppInfo* ADDIN_API GetPlatformInfo() = 0; 154 | }; 155 | struct IAttachedInfo : 156 | public IInterface 157 | { 158 | enum AttachedType 159 | { 160 | eAttachedIsolated = 0, 161 | eAttachedNotIsolated, 162 | }; 163 | virtual const AttachedType ADDIN_API GetAttachedInfo() = 0; 164 | }; 165 | #endif //__ADAPTER_DEF_H__ 166 | -------------------------------------------------------------------------------- /1C_include/ComponentBase.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Warning!!! 3 | * DO NOT ALTER THIS FILE! 4 | */ 5 | 6 | 7 | #ifndef __COMPONENT_BASE_H__ 8 | #define __COMPONENT_BASE_H__ 9 | 10 | #include "types.h" 11 | //////////////////////////////////////////////////////////////////////////////// 12 | /** 13 | * The given interface is intended for initialization and 14 | * uninitialization of component and its adjustments 15 | */ 16 | /// Interface of component initialization. 17 | class IInitDoneBase 18 | { 19 | public: 20 | virtual ~IInitDoneBase() {} 21 | /// Initializes component 22 | /** 23 | * @param disp - 1C:Enterpise interface 24 | * @return the result of 25 | */ 26 | virtual bool ADDIN_API Init(void* disp) = 0; 27 | /// Sets the memory manager 28 | /* 29 | * @param mem - pointer to memory manager interface. 30 | * @return the result of 31 | */ 32 | virtual bool ADDIN_API setMemManager(void* mem) = 0; 33 | 34 | /// Returns component version 35 | /** 36 | * @return - component version (2000 - version 2) 37 | */ 38 | virtual long ADDIN_API GetInfo() = 0; 39 | 40 | /// Uninitializes component 41 | /** 42 | * Component here should release all consumed resources. 43 | */ 44 | virtual void ADDIN_API Done() = 0; 45 | 46 | }; 47 | /////////////////////////////////////////////////////////////////////////// 48 | /** 49 | * The given interface defines methods that are intented to be used by the Platform 50 | */ 51 | /// Interface describing extension of language. 52 | class ILanguageExtenderBase 53 | { 54 | public: 55 | virtual ~ILanguageExtenderBase(){} 56 | /// Registers language extension 57 | /** 58 | * @param wsExtensionName - extension name 59 | * @return the result of 60 | */ 61 | virtual bool ADDIN_API RegisterExtensionAs(WCHAR_T** wsExtensionName) = 0; 62 | 63 | /// Returns number of component properties 64 | /** 65 | * @return number of properties 66 | */ 67 | virtual long ADDIN_API GetNProps() = 0; 68 | 69 | /// Finds property by name 70 | /** 71 | * @param wsPropName - property name 72 | * @return property index or -1, if it is not found 73 | */ 74 | virtual long ADDIN_API FindProp(const WCHAR_T* wsPropName) = 0; 75 | 76 | /// Returns property name 77 | /** 78 | * @param lPropNum - property index (starting with 0) 79 | * @param lPropAlias - 0 - international alias, 80 | * 1 - russian alias. (International alias is required) 81 | * @return proeprty name or 0 if it is not found 82 | */ 83 | virtual const WCHAR_T* ADDIN_API GetPropName(long lPropNum, 84 | long lPropAlias) = 0; 85 | 86 | /// Returns property value 87 | /** 88 | * @param lPropNum - property index (starting with 0) 89 | * @param pvarPropVal - the pointer to a variable for property value 90 | * @return the result of 91 | */ 92 | virtual bool ADDIN_API GetPropVal(const long lPropNum, 93 | tVariant* pvarPropVal) = 0; 94 | 95 | /// Sets the property value 96 | /** 97 | * @param lPropNum - property index (starting with 0) 98 | * @param varPropVal - the pointer to a variable for property value 99 | * @return the result of 100 | */ 101 | virtual bool ADDIN_API SetPropVal(const long lPropNum, 102 | tVariant* varPropVal) = 0; 103 | 104 | /// Is property readable? 105 | /** 106 | * @param lPropNum - property index (starting with 0) 107 | * @return true if property is readable 108 | */ 109 | virtual bool ADDIN_API IsPropReadable(const long lPropNum) = 0; 110 | 111 | /// Is property writable? 112 | /** 113 | * @param lPropNum - property index (starting with 0) 114 | * @return true if property is writable 115 | */ 116 | virtual bool ADDIN_API IsPropWritable(const long lPropNum) = 0; 117 | 118 | /// Returns number of component methods 119 | /** 120 | * @return number of component methods 121 | */ 122 | virtual long ADDIN_API GetNMethods() = 0; 123 | 124 | /// Finds a method by name 125 | /** 126 | * @param wsMethodName - method name 127 | * @return - method index 128 | */ 129 | virtual long ADDIN_API FindMethod(const WCHAR_T* wsMethodName) = 0; 130 | 131 | /// Returns method name 132 | /** 133 | * @param lMethodNum - method index(starting with 0) 134 | * @param lMethodAlias - 0 - international alias, 135 | * 1 - russian alias. (International alias is required) 136 | * @return method name or 0 if method is not found 137 | */ 138 | virtual const WCHAR_T* ADDIN_API GetMethodName(const long lMethodNum, 139 | const long lMethodAlias) = 0; 140 | 141 | /// Returns number of method parameters 142 | /** 143 | * @param lMethodNum - method index (starting with 0) 144 | * @return number of parameters 145 | */ 146 | virtual long ADDIN_API GetNParams(const long lMethodNum) = 0; 147 | 148 | /// Returns default value of method parameter 149 | /** 150 | * @param lMethodNum - method index(starting with 0) 151 | * @param lParamNum - parameter index (starting with 0) 152 | * @param pvarParamDefValue - the pointer to a variable for default value 153 | * @return the result of 154 | */ 155 | virtual bool ADDIN_API GetParamDefValue(const long lMethodNum, 156 | const long lParamNum, 157 | tVariant *pvarParamDefValue) = 0; 158 | 159 | /// Does the method have a return value? 160 | /** 161 | * @param lMethodNum - method index (starting with 0) 162 | * @return true if the method has a return value 163 | */ 164 | virtual bool ADDIN_API HasRetVal(const long lMethodNum) = 0; 165 | 166 | /// Calls the method as a procedure 167 | /** 168 | * @param lMethodNum - method index (starting with 0) 169 | * @param paParams - the pointer to array of method parameters 170 | * @param lSizeArray - the size of array 171 | * @return the result of 172 | */ 173 | virtual bool ADDIN_API CallAsProc(const long lMethodNum, 174 | tVariant* paParams, 175 | const long lSizeArray) = 0; 176 | 177 | /// Calls the method as a function 178 | /** 179 | * @param lMethodNum - method index (starting with 0) 180 | * @param pvarRetValue - the pointer to returned value 181 | * @param paParams - the pointer to array of method parameters 182 | * @param lSizeArray - the size of array 183 | * @return the result of 184 | */ 185 | virtual bool ADDIN_API CallAsFunc(const long lMethodNum, 186 | tVariant* pvarRetValue, 187 | tVariant* paParams, 188 | const long lSizeArray) = 0; 189 | }; 190 | /////////////////////////////////////////////////////////////////////////// 191 | /** 192 | * This interface is used to change component locale 193 | */ 194 | /// Base interface for component localization. 195 | class LocaleBase 196 | { 197 | public: 198 | virtual ~LocaleBase(){} 199 | /// Changes component locale 200 | /** 201 | * @param loc - new locale (for Windows - rus_RUS, 202 | * for Linux - ru_RU, etc...) 203 | */ 204 | virtual void ADDIN_API SetLocale(const WCHAR_T* loc) = 0; 205 | }; 206 | 207 | /////////////////////////////////////////////////////////////////////// 208 | /// class UserLanguageBase- интерфейс изменения языка компоненты 209 | /** 210 | * Этот интерфейс предназначен для изменения локализации компоненты 211 | */ 212 | class UserLanguageBase 213 | { 214 | public: 215 | virtual ~UserLanguageBase() {} 216 | /// Изменение локали компоненты 217 | /** 218 | * @param const char16_t* lang - устанавливаемый язык (ru, etc...) 219 | */ 220 | virtual void ADDIN_API SetUserInterfaceLanguageCode(const WCHAR_T* lang) = 0; 221 | }; 222 | 223 | /////////////////////////////////////////////////////////////////////////// 224 | /** 225 | * The given interface is generalized, for its obligatory inheritance 226 | * in implementing components. 227 | */ 228 | /// Base interface describing object as a set of properties and methods. 229 | class IComponentBase : 230 | public IInitDoneBase, 231 | public ILanguageExtenderBase, 232 | public LocaleBase, 233 | public UserLanguageBase 234 | { 235 | public: 236 | virtual ~IComponentBase(){} 237 | }; 238 | 239 | enum AppCapabilities 240 | { 241 | eAppCapabilitiesInvalid = -1, 242 | eAppCapabilities1 = 1, 243 | eAppCapabilities2 = 2, 244 | eAppCapabilities3 = 3, 245 | eAppCapabilitiesLast = eAppCapabilities3, 246 | }; 247 | 248 | enum AttachType 249 | { 250 | eCanAttachNotIsolated = 1, 251 | eCanAttachIsolated, 252 | eCanAttachAny, 253 | }; 254 | 255 | /// Announcements of exported functions 256 | /** 257 | * These functions should be implemented that component can be loaded and created. 258 | */ 259 | extern "C" long GetClassObject(const WCHAR_T*, IComponentBase** pIntf); 260 | extern "C" long DestroyObject(IComponentBase** pIntf); 261 | extern "C" const WCHAR_T* GetClassNames(); 262 | extern "C" AppCapabilities SetPlatformCapabilities(const AppCapabilities capabilities); 263 | extern "C" AttachType GetAttachType(); 264 | 265 | typedef long (*GetClassObjectPtr)(const WCHAR_T* wsName, IComponentBase** pIntf); 266 | typedef long (*DestroyObjectPtr)(IComponentBase** pIntf); 267 | typedef const WCHAR_T* (*GetClassNamesPtr)(); 268 | typedef AppCapabilities (*SetPlatformCapabilitiesPtr)(const AppCapabilities capabilities); 269 | typedef AttachType (*GetAttachTypePtr)(); 270 | 271 | #endif //__COMPONENT_BASE_H__ 272 | -------------------------------------------------------------------------------- /1C_include/IAndroidComponentHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Warning!!! 3 | * DO NOT ALTER THIS FILE! 4 | */ 5 | 6 | #ifndef __IANDROIDCOMPONENTHELPER_H__ 7 | #define __IANDROIDCOMPONENTHELPER_H__ 8 | 9 | #include "types.h" 10 | 11 | #if defined(__ANDROID__) 12 | #include 13 | 14 | struct IAndroidComponentHelper : 15 | public IInterface 16 | { 17 | virtual jobject ADDIN_API GetActivity() = 0; 18 | 19 | virtual jclass ADDIN_API FindClass(const WCHAR_T* className) = 0; 20 | }; 21 | 22 | #endif //__ANDROID__ 23 | 24 | #endif //__IANDROIDCOMPONENTHELPER_H__ 25 | -------------------------------------------------------------------------------- /1C_include/IMemoryManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Warning!!! 3 | * DO NOT ALTER THIS FILE! 4 | */ 5 | 6 | 7 | #ifndef __IMEMORY_MANAGER_H__ 8 | #define __IMEMORY_MANAGER_H__ 9 | 10 | /////////////////////////////////////////////////////////////////////////////// 11 | /** 12 | * The given class allocates and releases memory for a component 13 | */ 14 | /// Interface representing memory manager. 15 | class IMemoryManager 16 | { 17 | public: 18 | virtual ~IMemoryManager() {} 19 | /// Allocates memory of specified size 20 | /** 21 | * @param pMemory - the double pointer to variable, that will hold newly 22 | * allocated block of memory of NULL if allocation fails. 23 | * @param ulCountByte - memory size 24 | * @return the result of 25 | */ 26 | virtual bool ADDIN_API AllocMemory (void** pMemory, unsigned long ulCountByte) = 0; 27 | /// Releases memory 28 | /** 29 | * @param pMemory - The double pointer to the memory block being released 30 | */ 31 | virtual void ADDIN_API FreeMemory (void** pMemory) = 0; 32 | }; 33 | 34 | #endif //__IMEMORY_MANAGER_H__ 35 | -------------------------------------------------------------------------------- /1C_include/MANIFEST.xsd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Bundle description 6 | 7 | 8 | 9 | 10 | 11 | 12 | Name component 13 | 14 | 15 | 16 | 17 | 18 | 19 | Platform type 20 | 21 | 22 | 23 | 24 | File name 25 | 26 | 27 | 28 | 29 | Component type 30 | 31 | 32 | 33 | 34 | Object name 35 | 36 | 37 | 38 | 39 | Architecture 40 | 41 | 42 | 43 | 44 | Client type 45 | 46 | 47 | 48 | 49 | Client version 50 | 51 | 52 | 53 | 54 | Build type 55 | 56 | 57 | 58 | 59 | Code type 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /1C_include/addin.idl: -------------------------------------------------------------------------------- 1 | import "oaidl.idl"; 2 | import "ocidl.idl"; 3 | 4 | [ 5 | object, 6 | uuid(AB634001-F13D-11d0-A459-004095E1DAEA), 7 | helpstring("IInitDone Interface"), 8 | pointer_default(unique) 9 | ] 10 | interface IInitDone : IUnknown 11 | { 12 | [helpstring("method Init")] HRESULT Init([in] IDispatch *pConnection); 13 | [helpstring("method Done")] HRESULT Done(); 14 | [helpstring("method GetInfo")] HRESULT GetInfo([in,out] SAFEARRAY (VARIANT) *pInfo); 15 | }; 16 | 17 | [ 18 | object, 19 | uuid(AB634002-F13D-11d0-A459-004095E1DAEA), 20 | helpstring("IPropertyProfile Interface"), 21 | pointer_default(unique) 22 | ] 23 | interface IPropertyProfile : IPropertyBag 24 | { 25 | [helpstring("method RegisterProfileAs")] HRESULT RegisterProfileAs(BSTR bstrProfileName); 26 | }; 27 | 28 | [ 29 | object, 30 | uuid(ab634004-f13d-11d0-a459-004095e1daea), 31 | helpstring("IAsyncEvent Interface"), 32 | pointer_default(unique) 33 | ] 34 | interface IAsyncEvent : IUnknown 35 | { 36 | [helpstring("method SetEventBufferDepth")] HRESULT SetEventBufferDepth(long lDepth); 37 | [helpstring("method GetEventBufferDepth")] HRESULT GetEventBufferDepth(long *plDepth); 38 | [helpstring("method ExternalEvent")] HRESULT ExternalEvent(BSTR bstrSource, BSTR bstrMessage, BSTR bstrData); 39 | [helpstring("method CleanBuffer")] HRESULT CleanBuffer(); 40 | }; 41 | [ 42 | object, 43 | uuid(E88A191E-8F52-4261-9FAE-FF7AA84F5D7E), 44 | helpstring("ILocale Interface"), 45 | pointer_default(unique) 46 | ] 47 | interface ILocale : IUnknown 48 | { 49 | [helpstring("method SetLocale")] HRESULT SetLocale(BSTR bstrLocale); 50 | 51 | }; 52 | [ 53 | object, 54 | uuid(AB634003-F13D-11d0-A459-004095E1DAEA), 55 | helpstring("ILanguageExtender Interface"), 56 | pointer_default(unique) 57 | ] 58 | interface ILanguageExtender : IUnknown 59 | { 60 | [helpstring("method RegisterExtensionAs")] HRESULT RegisterExtensionAs([in,out]BSTR *bstrExtensionName); 61 | [helpstring("method GetNProps")] HRESULT GetNProps([in,out]long *plProps); 62 | [helpstring("method FindProp")] HRESULT FindProp([in]BSTR bstrPropName,[in,out]long *plPropNum); 63 | [helpstring("method GetPropName")] HRESULT GetPropName([in]long lPropNum,[in]long lPropAlias,[in,out]BSTR *pbstrPropName); 64 | [helpstring("method GetPropVal")] HRESULT GetPropVal([in]long lPropNum,[in,out]VARIANT *pvarPropVal); 65 | [helpstring("method SetPropVal")] HRESULT SetPropVal([in]long lPropNum,[in]VARIANT *varPropVal); 66 | [helpstring("method IsPropReadable")] HRESULT IsPropReadable([in]long lPropNum,[in,out]BOOL *pboolPropRead); 67 | [helpstring("method IsPropWritable")] HRESULT IsPropWritable([in]long lPropNum,[in,out]BOOL *pboolPropWrite); 68 | 69 | [helpstring("method GetNMethods")] HRESULT GetNMethods([in,out]long *plMethods); 70 | [helpstring("method FindMethod")] HRESULT FindMethod(BSTR bstrMethodName,[in,out]long *plMethodNum); 71 | [helpstring("method GetMethodName")] HRESULT GetMethodName([in]long lMethodNum,[in]long lMethodAlias,[in,out]BSTR *pbstrMethodName); 72 | [helpstring("method GetNParams")] HRESULT GetNParams([in]long lMethodNum,[in,out]long *plParams); 73 | [helpstring("method GetParamDefValue")] HRESULT GetParamDefValue([in]long lMethodNum,[in]long lParamNum,[in,out]VARIANT *pvarParamDefValue); 74 | [helpstring("method HasRetVal")] HRESULT HasRetVal([in]long lMethodNum,[in,out]BOOL *pboolRetValue); 75 | [helpstring("method CallAsProc")] HRESULT CallAsProc([in]long lMethodNum,[in,out] SAFEARRAY (VARIANT) *paParams); 76 | [helpstring("method CallAsFunc")] HRESULT CallAsFunc([in]long lMethodNum,[in,out] VARIANT *pvarRetValue,[in,out] SAFEARRAY (VARIANT) *paParams); 77 | }; 78 | 79 | [ 80 | object, 81 | uuid(ab634005-f13d-11d0-a459-004095e1daea), 82 | helpstring("IStatusLine Interface"), 83 | pointer_default(unique) 84 | ] 85 | interface IStatusLine : IUnknown 86 | { 87 | [helpstring("method SetStatusLine")] HRESULT SetStatusLine(BSTR bstrStatusLine); 88 | [helpstring("method ResetStatusLine")] HRESULT ResetStatusLine(); 89 | }; 90 | 91 | [ 92 | object, 93 | uuid(efe19ea0-09e4-11d2-a601-008048da00de), 94 | helpstring("IExtWndsSupport Interface"), 95 | pointer_default(unique) 96 | ] 97 | interface IExtWndsSupport : IUnknown 98 | { 99 | [helpstring("method GetAppMainFrame")] HRESULT GetAppMainFrame([in,out]HWND *hwnd); 100 | [helpstring("method GetAppMDIFrame")] HRESULT GetAppMDIFrame([in,out]HWND *hwnd); 101 | [helpstring("method CreateAddInWindow")] HRESULT CreateAddInWindow([in]BSTR bstrProgID, [in]BSTR bstrWindowName, [in]long dwStyles, [in]long dwExStyles, [in]RECT *rctl, [in]long Flags, [in,out]HWND *pHwnd, [in,out]IDispatch **pDisp); 102 | }; 103 | 104 | [ 105 | object, 106 | uuid(3C2136B5-B35A-4FAC-9AC3-F77F361E9467), 107 | helpstring("IMsgBox Interface"), 108 | pointer_default(unique) 109 | ] 110 | interface IMsgBox : IUnknown 111 | { 112 | [helpstring("method Confirm")] HRESULT Confirm([in]BSTR queryText, [out,retval]VARIANT *retVal); 113 | [helpstring("method Alert")] HRESULT Alert([in] BSTR text); 114 | 115 | }; 116 | typedef [v1_enum] enum _AppType { 117 | eAppUnknown = -1, 118 | eAppThinClient = 0, 119 | eAppThickClient, 120 | eAppWebClient, 121 | eAppServer, 122 | eAppExtConn, 123 | eAppMobileClient, 124 | eAppMobileServer, 125 | } AppType; 126 | typedef struct _AppInfo { 127 | BSTR AppVersion; 128 | BSTR UserAgentInformation; 129 | const AppType Application; 130 | } AppInfo; 131 | [ 132 | object, 133 | uuid(AAABE126-2230-4a7d-9DDA-8987FD2A62BA), 134 | helpstring("IPlatformInfo Interface"), 135 | pointer_default(unique) 136 | ] 137 | interface IPlatformInfo : IUnknown 138 | { 139 | [helpstring("method GetPlatformInfo")] HRESULT GetPlatformInfo([out]AppInfo** info); 140 | 141 | }; 142 | typedef [v1_enum] enum _AttachedType { 143 | eAttachedIsolated = 0, 144 | eAttachedNotIsolated, 145 | } AttachedType; 146 | [ 147 | object, 148 | uuid(6C269247-815E-42D9-9B17-5F17DF11B98F), 149 | helpstring("IAttachedInfo Interface"), 150 | pointer_default(unique) 151 | ] 152 | interface IAttachedInfo : IUnknown 153 | { 154 | [helpstring("method GetAttachedInfo")] HRESULT GetAttachedInfo([out]AttachedType* con_type); 155 | }; -------------------------------------------------------------------------------- /1C_include/addinlib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum AddInComponentType 4 | { 5 | eAddInCom = 1, 6 | eAddInNative, 7 | eAddInJava, 8 | eAddInvalid = -1 9 | }; 10 | -------------------------------------------------------------------------------- /1C_include/com.h: -------------------------------------------------------------------------------- 1 |  2 | #ifndef __COM_H__ 3 | #define __COM_H__ 4 | 5 | #if defined(__linux__) || defined(__APPLE__) || defined(__ANDROID__) 6 | 7 | #ifdef __ANDROID__ 8 | 9 | typedef struct { 10 | unsigned int Data1; 11 | unsigned short Data2; 12 | unsigned short Data3; 13 | unsigned char Data4[ 8 ]; 14 | } uuid_t; 15 | 16 | #else 17 | #include 18 | #endif //__ANDROID__ 19 | 20 | #ifndef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ // iOS 21 | #include 22 | #endif //!__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 23 | 24 | #pragma GCC system_header 25 | 26 | typedef long HRESULT; 27 | 28 | #ifdef __GNUC__ 29 | #define STDMETHODCALLTYPE __attribute__ ((__stdcall__)) 30 | #define DECLSPEC_NOTHROW __attribute__ ((nothrow)) 31 | #define STDMETHOD(method) virtual DECLSPEC_NOTHROW HRESULT STDMETHODCALLTYPE method 32 | #else 33 | #define STDMETHODCALLTYPE 34 | #endif 35 | 36 | #define __stdcall STDMETHODCALLTYPE 37 | #define near 38 | #define far 39 | #define CONST const 40 | #define FAR far 41 | 42 | typedef unsigned long DWORD; 43 | #ifndef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ // iOS 44 | typedef int BOOL; 45 | #elif defined(__LP64__) 46 | typedef bool BOOL; 47 | #else 48 | typedef signed char BOOL; 49 | #endif //!__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ 50 | 51 | typedef void VOID; 52 | typedef short SHORT; 53 | typedef unsigned char BYTE; 54 | typedef unsigned short WORD; 55 | typedef float FLOAT; 56 | typedef FLOAT *PFLOAT; 57 | typedef BOOL near *PBOOL; 58 | typedef BOOL far *LPBOOL; 59 | typedef BYTE near *PBYTE; 60 | typedef BYTE far *LPBYTE; 61 | typedef int near *PINT; 62 | typedef int far *LPINT; 63 | typedef WORD near *PWORD; 64 | typedef WORD far *LPWORD; 65 | typedef long far *LPLONG; 66 | typedef DWORD near *PDWORD; 67 | typedef DWORD far *LPDWORD; 68 | typedef void far *LPVOID; 69 | typedef CONST void far *LPCVOID; 70 | typedef wchar_t *BSTR; 71 | typedef long SCODE; 72 | typedef int INT; 73 | typedef unsigned int UINT; 74 | typedef unsigned int *PUINT; 75 | typedef wchar_t WCHAR; 76 | typedef wchar_t OLECHAR; 77 | typedef wchar_t *LPOLESTR; 78 | typedef const wchar_t *LPCOLESTR; 79 | typedef DWORD LCID; 80 | typedef PDWORD PLCID; 81 | typedef long LONG; 82 | typedef unsigned long ULONG; 83 | typedef long long LONGLONG; 84 | typedef unsigned long long ULONGLONG; 85 | typedef LONG DISPID; 86 | typedef double DOUBLE; 87 | typedef double DATE; 88 | typedef short VARIANT_BOOL; 89 | typedef void *PVOID; 90 | typedef char CHAR; 91 | typedef CONST CHAR *LPCSTR; 92 | typedef unsigned short USHORT; 93 | typedef void *HMODULE; 94 | #define OLESTR(str) L##str 95 | 96 | typedef guid_t uuid_t; 97 | typedef uuid_t GUID; 98 | typedef uuid_t IID; 99 | typedef uuid_t UUID; 100 | #define REFIID const IID & 101 | #define MAX_PATH 260 102 | 103 | #define IsEqualIID(x,y) uuid_compare((x),(y)) 104 | #ifdef __GNUC__ 105 | #define LoadLibraryA(x) dlopen((x), RTLD_LAZY) 106 | #define FreeLibrary(x) dlclose((x)) 107 | #define GetProcAddress(x, y) dlsym((x), (y)) 108 | #endif //__GNUC__ 109 | 110 | #define E_FAIL 0x80004005L 111 | #define S_OK 0L 112 | #define S_FALSE 1L 113 | #define E_NOINTERFACE 0x80004002L 114 | #define E_NOTIMPL 0x80004001L 115 | #define E_INVALIDARG 0x80070057L 116 | #define E_UNEXPECTED 0x8000FFFFL 117 | #define E_OUTOFMEMORY 0x8007000EL 118 | #define DISP_E_UNKNOWNNAME 0x80020006L 119 | #define DISPID_UNKNOWN ( -1 ) 120 | #define TRUE 1 121 | #define FALSE 0 122 | 123 | typedef long ITypeInfo; 124 | 125 | #if defined (__GNUC__) && !defined (NONAMELESSUNION) 126 | __extension__ /* no named members */ 127 | #endif 128 | union tCY { 129 | __extension__ struct 130 | { 131 | unsigned long Lo; 132 | long Hi; 133 | }; 134 | long long int64; 135 | }; 136 | typedef union tagCY CY; 137 | #define CLSIDFromString(x,y) uuid_parse((x),(unsigned char*)(y)) 138 | 139 | #endif //defined(__linux__) || defined(__APPLE__) 140 | 141 | #endif //__COM_H__ 142 | -------------------------------------------------------------------------------- /1C_include/mobile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__ANDROID__) 4 | 5 | #define MOBILE_PLATFORM_ANDROID 1 6 | 7 | #elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) 8 | 9 | #define MOBILE_PLATFORM_IOS 1 10 | 11 | #elif defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) 12 | 13 | #define MOBILE_PLATFORM_WINRT 1 14 | 15 | #endif 16 | 17 | #include "types.h" 18 | 19 | #if defined(MOBILE_PLATFORM_IOS) 20 | 21 | extern "C" VOID RegisterLibrary(LPCSTR, LPCVOID, LPCVOID); 22 | 23 | #define DECLARE_DLL(name, fnTable) \ 24 | namespace { static struct s { s() { RegisterLibrary(name, NULL, fnTable); }} s; } 25 | 26 | #endif //MOBILE_PLATFORM_IOS 27 | 28 | -------------------------------------------------------------------------------- /1C_include/types.h: -------------------------------------------------------------------------------- 1 |  2 | #ifndef __CON_TYPES_H__ 3 | #define __CON_TYPES_H__ 4 | 5 | #ifdef _MSC_VER 6 | // warning C4201 : nonstandard extension used : nameless struct / union 7 | #pragma warning(disable : 4201) 8 | #endif 9 | 10 | #ifdef _WINDOWS 11 | #define WIN32_LEAN_AND_MEAN 12 | #define NOMINMAX 13 | #include 14 | #define WCHAR_T char16_t 15 | #else 16 | #define WCHAR_T char16_t 17 | #endif //_WINDOWS 18 | 19 | #if defined(WINAPI_FAMILY) 20 | #include 21 | #endif 22 | 23 | #if __GNUC__ >=3 24 | #pragma GCC system_header 25 | #endif 26 | 27 | #include "com.h" 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #define EXTERN_C extern "C" 34 | 35 | #ifdef __GNUC__ 36 | #define _ANONYMOUS_UNION __extension__ 37 | #define _ANONYMOUS_STRUCT __extension__ 38 | #else 39 | #define _ANONYMOUS_UNION 40 | #define _ANONYMOUS_STRUCT 41 | #endif //__GNUC__ 42 | 43 | #ifdef NONAMELESSUNION 44 | #define __VARIANT_NAME_1 u 45 | #define __VARIANT_NAME_2 iface 46 | #define __VARIANT_NAME_3 str 47 | #define __VARIANT_NAME_4 wstr 48 | #else 49 | #define __VARIANT_NAME_1 50 | #define __VARIANT_NAME_2 51 | #define __VARIANT_NAME_3 52 | #define __VARIANT_NAME_4 53 | #endif //NONAMELESSUNION 54 | 55 | #define RESULT_FROM_ERRNO(x) ((long)(x) <= 0 ? ((long)(x)) \ 56 | : ((long) (((x) & 0x0000FFFF) | (BASE_ERRNO << 16) | 0x80000000))) 57 | 58 | #define ADDIN_E_NONE 1000 59 | #define ADDIN_E_ORDINARY 1001 60 | #define ADDIN_E_ATTENTION 1002 61 | #define ADDIN_E_IMPORTANT 1003 62 | #define ADDIN_E_VERY_IMPORTANT 1004 63 | #define ADDIN_E_INFO 1005 64 | #define ADDIN_E_FAIL 1006 65 | #define ADDIN_E_MSGBOX_ATTENTION 1007 66 | #define ADDIN_E_MSGBOX_INFO 1008 67 | #define ADDIN_E_MSGBOX_FAIL 1009 68 | 69 | #ifndef ADDIN_API 70 | #ifdef _WINDOWS 71 | #define ADDIN_API __stdcall 72 | #else 73 | //#define ADDIN_API __attribute__ ((__stdcall__)) 74 | #define ADDIN_API 75 | #endif //_WINDOWS 76 | #endif //ADDIN_API 77 | 78 | #include 79 | 80 | typedef unsigned short TYPEVAR; 81 | enum ENUMVAR 82 | { 83 | VTYPE_EMPTY = 0, 84 | VTYPE_NULL, 85 | VTYPE_I2, //int16_t 86 | VTYPE_I4, //int32_t 87 | VTYPE_R4, //float 88 | VTYPE_R8, //double 89 | VTYPE_DATE, //DATE (double) 90 | VTYPE_TM, //struct tm 91 | VTYPE_PSTR, //struct str string 92 | VTYPE_INTERFACE, //struct iface 93 | VTYPE_ERROR, //int32_t errCode 94 | VTYPE_BOOL, //bool 95 | VTYPE_VARIANT, //struct _tVariant * 96 | VTYPE_I1, //int8_t 97 | VTYPE_UI1, //uint8_t 98 | VTYPE_UI2, //uint16_t 99 | VTYPE_UI4, //uint32_t 100 | VTYPE_I8, //int64_t 101 | VTYPE_UI8, //uint64_t 102 | VTYPE_INT, //int Depends on architecture 103 | VTYPE_UINT, //unsigned int Depends on architecture 104 | VTYPE_HRESULT, //long hRes 105 | VTYPE_PWSTR, //struct wstr 106 | VTYPE_BLOB, //means in struct str binary data contain 107 | VTYPE_CLSID, //UUID 108 | VTYPE_STR_BLOB = 0xfff, 109 | VTYPE_VECTOR = 0x1000, 110 | VTYPE_ARRAY = 0x2000, 111 | VTYPE_BYREF = 0x4000, //Only with struct _tVariant * 112 | VTYPE_RESERVED = 0x8000, 113 | VTYPE_ILLEGAL = 0xffff, 114 | VTYPE_ILLEGALMASKED = 0xfff, 115 | VTYPE_TYPEMASK = 0xfff 116 | } ; 117 | #if defined (__GNUC__) && !defined (NONAMELESSUNION) 118 | __extension__ /* no named members */ 119 | #endif 120 | struct _tVariant 121 | { 122 | _ANONYMOUS_UNION union 123 | { 124 | int8_t i8Val; 125 | int16_t shortVal; 126 | int32_t lVal; 127 | int intVal; 128 | unsigned int uintVal; 129 | int64_t llVal; 130 | uint8_t ui8Val; 131 | uint16_t ushortVal; 132 | uint32_t ulVal; 133 | uint64_t ullVal; 134 | int32_t errCode; 135 | long hRes; 136 | float fltVal; 137 | double dblVal; 138 | bool bVal; 139 | char chVal; 140 | wchar_t wchVal; 141 | DATE date; 142 | IID IDVal; 143 | struct _tVariant *pvarVal; 144 | struct tm tmVal; 145 | _ANONYMOUS_STRUCT struct 146 | { 147 | void* pInterfaceVal; 148 | IID InterfaceID; 149 | } __VARIANT_NAME_2/*iface*/; 150 | _ANONYMOUS_STRUCT struct 151 | { 152 | char* pstrVal; 153 | uint32_t strLen; //count of bytes 154 | } __VARIANT_NAME_3/*str*/; 155 | _ANONYMOUS_STRUCT struct 156 | { 157 | WCHAR_T* pwstrVal; 158 | uint32_t wstrLen; //count of symbol 159 | } __VARIANT_NAME_4/*wstr*/; 160 | } __VARIANT_NAME_1; 161 | uint32_t cbElements; //Dimension for an one-dimensional array in pvarVal 162 | TYPEVAR vt; 163 | }; 164 | typedef struct _tVariant tVariant; 165 | typedef tVariant tVariantArg; 166 | 167 | 168 | #if defined(NONAMELESSUNION) 169 | #define TV_JOIN(X, Y) ((X)->u.Y) 170 | #else 171 | #define TV_JOIN(X, Y) ((X)->Y) 172 | #endif 173 | 174 | #define TV_VT(X) ((X)->vt) 175 | #define TV_ISBYREF(X) (TV_VT(X)&VT_BYREF) 176 | #define TV_ISARRAY(X) (TV_VT(X)&VT_ARRAY) 177 | #define TV_ISVECTOR(X) (TV_VT(X)&VT_VECTOR) 178 | #define TV_NONE(X) TV_I2(X) 179 | 180 | #define TV_UI1(X) TV_JOIN(X, ui8Val) 181 | #define TV_I2(X) TV_JOIN(X, shortVal) 182 | #define TV_I4(X) TV_JOIN(X, lVal) 183 | #define TV_I8(X) TV_JOIN(X, llVal) 184 | #define TV_R4(X) TV_JOIN(X, fltVal) 185 | #define TV_R8(X) TV_JOIN(X, dblVal) 186 | #define TV_I1(X) TV_JOIN(X, i8Val) 187 | #define TV_UI2(X) TV_JOIN(X, ushortVal) 188 | #define TV_UI4(X) TV_JOIN(X, ulVal) 189 | #define TV_UI8(X) TV_JOIN(X, ullVal) 190 | #define TV_INT(X) TV_JOIN(X, intVal) 191 | #define TV_UINT(X) TV_JOIN(X, uintVal) 192 | 193 | #ifdef _WIN64 194 | #define TV_INT_PTR(X) TV_JOIN(X, llVal) 195 | #define TV_UINT_PTR(X) TV_JOIN(X, ullVal) 196 | #else 197 | #define TV_INT_PTR(X) TV_JOIN(X, lVal) 198 | #define TV_UINT_PTR(X) TV_JOIN(X, ulVal) 199 | #endif 200 | 201 | 202 | #define TV_DATE(X) TV_JOIN(X, date) 203 | #define TV_STR(X) TV_JOIN(X, pstrVal) 204 | #define TV_WSTR(X) TV_JOIN(X, pwstrVal) 205 | #define TV_BOOL(X) TV_JOIN(X, bVal) 206 | #define TV_UNKNOWN(X) TV_JOIN(X, pInterfaceVal) 207 | #define TV_VARIANTREF(X) TV_JOIN(X, pvarVal) 208 | 209 | void tVarInit(tVariant* tvar); 210 | 211 | inline 212 | void tVarInit(tVariant* tvar) 213 | { 214 | assert(tvar != NULL); 215 | memset(tvar, 0, sizeof(tVariant)); 216 | TV_VT(tvar) = VTYPE_EMPTY; 217 | } 218 | //----------------------------------------------------------------------------// 219 | // static setter functions... 220 | 221 | #define DATA_SET_BEGIN(data_) \ 222 | tVarInit(data_); 223 | 224 | #define DATA_SET_END(data_, type_) \ 225 | TV_VT(data_) = type_; 226 | 227 | 228 | #define DATA_SET(data_, type_, member_, value_) \ 229 | DATA_SET_BEGIN(data_) \ 230 | TV_JOIN(data_, member_) = value_; \ 231 | DATA_SET_END(data_, type_) 232 | 233 | #define DATA_SET_WITH_CAST(data_, type_, member_, cast_, value_) \ 234 | DATA_SET_BEGIN(data_) \ 235 | TV_JOIN(data_, member_) = cast_ value_; \ 236 | DATA_SET_END(data_, type_) 237 | 238 | #endif //__CON_TYPES_H__ 239 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeList.txt : Top-level CMake project file, do global configuration 2 | # and include sub-projects here. 3 | # 4 | cmake_minimum_required (VERSION 3.15) 5 | project ("v8sqlite") 6 | 7 | if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC) 8 | set (MSVC_COMPILER ON) 9 | elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL Clang) 10 | set (CLANG_COMPILER ON) 11 | endif () 12 | 13 | option(V8SQLITE_STATIC_RUNTIME "Подключать рантайм-библиотеки статически" ON) 14 | option(V8SQLITE_BUILD_TESTS "Построить тесты" ON) 15 | option(V8SQLITE_BENCHMARKS "Построить замеры производительности" OFF) 16 | 17 | # Установим дефолтные значения 18 | if (WIN32) 19 | 20 | # Одинаковые флаги для компиляторов MSVC и clang-cl во всех конфигурациях 21 | set (V8SQLITE_CXX_FLAGS "/std:c++20 /EHs /utf-8 /Zc:strictStrings /W3" CACHE STRING "") 22 | # Одинаковые флаги для компиляторов MSVC и clang-cl в релизной конфигурации 23 | set (V8SQLITE_CXX_FLAGS_RELEASE "/O3 /GS- /DNDEBUG" CACHE STRING "") 24 | # Одинаковые флаги для компиляторов MSVC и clang-cl в отладочной конфигурации 25 | set (V8SQLITE_CXX_FLAGS_DEBUG "/Zi /Ob0 /Od /D_DEBUG /RTC1" CACHE STRING "") 26 | # Флаги только для компилятора MSVC в релизной конфигурации 27 | set (V8SQLITE_CXX_MSVC_RELEASE "/GL" CACHE STRING "") 28 | # Флаги только для компилятора MSVC в отладочной конфигурации 29 | set (V8SQLITE_CXX_MSVC_DEBUG "" CACHE STRING "") 30 | # Флаги только для компилятора clang-cl в релизной конфигурации 31 | set (V8SQLITE_CXX_CLANG_RELEASE "-Wno-error=unused-command-line-argument -flto" CACHE STRING "") 32 | # Флаги только для компилятора clang-cl в отладочной конфигурации 33 | set (V8SQLITE_CXX_CLANG_DEBUG "-Wno-error=unused-command-line-argument" CACHE STRING "") 34 | 35 | # Флаги для MSVC линкера в релизе 36 | set (V8SQLITE_LINK_MSVC_RELEASE "/DEBUG:NONE /LTGC" CACHE STRING "") 37 | # Флаги для MSVC линкера в отладке 38 | set (V8SQLITE_LINK_MSVC_DEBUG "" CACHE STRING "") 39 | # Флаги для clang-cl линкера в релизе 40 | set (V8SQLITE_LINK_CLANG_RELEASE "LINKER:-flto" CACHE STRING "") 41 | # Флаги для clang-cl линкера в отладке 42 | set (V8SQLITE_LINK_CLANG_DEBUG "" CACHE STRING "") 43 | 44 | elseif (UNIX) 45 | # Флаги для компилятора clang++ во всех конфигурациях 46 | set (V8SQLITE_CXX_CLANG_FLAGS "-fPIC -std=c++20 -fexperimental-library -DLIBCXX_ENABLE_INCOMPLETE_FEATURES=ON -stdlib=libstdc++" CACHE STRING "") 47 | # Флаги для компилятора clang во всех конфигурациях 48 | set (V8SQLITE_C_CLANG_FLAGS "-fPIC" CACHE STRING "") 49 | # Флаги для компилятора clang в релизной конфигурации 50 | set (V8SQLITE_CXX_CLANG_RELEASE "-O2 -DNDEBUG -flto" CACHE STRING "") 51 | # Флаги для компилятора clang в отладочной конфигурации 52 | set (V8SQLITE_CXX_CLANG_DEBUG "-O0 -D_DEBUG" CACHE STRING "") 53 | 54 | elseif() 55 | 56 | message(FATAL_ERROR "Сборка поддерживается только в Windows и Linux") 57 | 58 | endif() 59 | 60 | # Установим основные флаги компилятора 61 | if (WIN32) 62 | set (CMAKE_CXX_FLAGS "${V8SQLITE_CXX_FLAGS}") 63 | set (CMAKE_C_FLAGS "${V8SQLITE_CXX_FLAGS}") 64 | set (CMAKE_CXX_FLAGS_RELEASE "${V8SQLITE_CXX_FLAGS_RELEASE}") 65 | set (CMAKE_CXX_FLAGS_DEBUG "${V8SQLITE_CXX_FLAGS_DEBUG}") 66 | set (CMAKE_C_FLAGS_RELEASE "${V8SQLITE_CXX_FLAGS_RELEASE}") 67 | set (CMAKE_C_FLAGS_DEBUG "${V8SQLITE_CXX_FLAGS_DEBUG}") 68 | 69 | if (MSVC_COMPILER) 70 | set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${V8SQLITE_CXX_MSVC_RELEASE}") 71 | set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${V8SQLITE_CXX_MSVC_DEBUG}") 72 | set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${V8SQLITE_CXX_MSVC_RELEASE}") 73 | set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${V8SQLITE_CXX_MSVC_DEBUG}") 74 | elseif (CLANG_COMPILER) 75 | set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${V8SQLITE_CXX_CLANG_RELEASE}") 76 | set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${V8SQLITE_CXX_CLANG_DEBUG}") 77 | set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${V8SQLITE_CXX_CLANG_RELEASE}") 78 | set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${V8SQLITE_CXX_CLANG_DEBUG}") 79 | else () 80 | message(FATAL_ERROR "Неизвестный компилятор") 81 | endif() 82 | 83 | elseif(UNIX) 84 | 85 | set (CMAKE_CXX_FLAGS "${V8SQLITE_CXX_CLANG_FLAGS}") 86 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${V8SQLITE_C_CLANG_FLAGS}") 87 | set (CMAKE_CXX_FLAGS_RELEASE "${V8SQLITE_CXX_CLANG_RELEASE}") 88 | set (CMAKE_CXX_FLAGS_DEBUG "${V8SQLITE_CXX_CLANG_DEBUG} -g") 89 | set (CMAKE_C_FLAGS_RELEASE "${V8SQLITE_CXX_CLANG_RELEASE}") 90 | set (CMAKE_C_FLAGS_DEBUG "${V8SQLITE_CXX_FLAGS_DEBUG}") 91 | 92 | if (CMAKE_BUILD_TYPE STREQUAL Debug) 93 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${V8SQLITE_CXX_CLANG_DEBUG}") 94 | else () 95 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${V8SQLITE_CXX_CLANG_RELEASE}") 96 | endif () 97 | 98 | endif() 99 | 100 | # Set static runtime library 101 | if (WIN32) 102 | # Allow MSVC_RUNTIME_LIBRARY property 103 | set(CMAKE_POLICY_DEFAULT_CMP0091 NEW) 104 | if (V8SQLITE_STATIC_RUNTIME) 105 | set (CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 106 | else () 107 | set (CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDll$<$:Debug>") 108 | endif () 109 | 110 | else() 111 | 112 | if (V8SQLITE_STATIC_RUNTIME) 113 | set (LINK_OPTIONS "${LINK_OPTIONS} -static-libgcc -static-libstdc++") 114 | endif () 115 | 116 | endif(WIN32) 117 | 118 | if (V8SQLITE_BUILD_TESTS) 119 | enable_testing() 120 | set (BUILD_TESTS ON) 121 | # Load and build GTest 122 | include(FetchContent) 123 | FetchContent_Declare( 124 | googletest 125 | # Specify the commit you depend on and update it regularly. 126 | URL https://github.com/google/googletest/archive/refs/tags/release-1.12.1.zip 127 | ) 128 | 129 | # For Windows: Prevent overriding the parent project's compiler/linker settings 130 | set(gtest_force_shared_crt FALSE CACHE BOOL "" FORCE) 131 | FetchContent_MakeAvailable(googletest) 132 | endif() 133 | 134 | function (GBencmark) 135 | # Load and build Google benchmarks 136 | include(FetchContent) 137 | FetchContent_Declare( 138 | googlebench 139 | # Specify the commit you depend on and update it regularly. 140 | URL https://github.com/google/benchmark/archive/refs/tags/v1.7.0.zip 141 | ) 142 | 143 | set (CMAKE_CXX_STANDARD 20) 144 | set (BENCHMARK_ENABLE_TESTING OFF) 145 | set (BENCHMARK_ENABLE_LTO OFF) 146 | set (BENCHMARK_ENABLE_INSTALL OFF) 147 | set (BENCHMARK_INSTALL_DOCS OFF) 148 | set (BENCHMARK_DOWNLOAD_DEPENDENCIES ON) 149 | set (BENCHMARK_ENABLE_GTEST_TESTS OFF) 150 | add_compile_definitions(BENCHMARK_STATIC_DEFINE) 151 | if (UNIX OR CLANG_COMPILER) 152 | set (BENCHMARK_USE_LIBCXX ON) 153 | endif () 154 | 155 | FetchContent_MakeAvailable(googlebench) 156 | endfunction() 157 | 158 | if (V8SQLITE_BENCHMARKS)# AND CMAKE_BUILD_TYPE STREQUAL Release) 159 | set (BUILD_BENCHMARKS ON) 160 | GBencmark() 161 | endif () 162 | 163 | include_directories("${CMAKE_SOURCE_DIR}/1C_include") 164 | include_directories("${CMAKE_SOURCE_DIR}/include") 165 | include_directories("${CMAKE_SOURCE_DIR}/libs/simstr/include") 166 | include_directories("${CMAKE_SOURCE_DIR}/sqlite3") 167 | 168 | # Include sub-projects. 169 | set (SIMSTR_BUILD_TESTS OFF) 170 | set (SIMSTR_BENCHMARKS OFF) 171 | add_subdirectory (libs/simstr) 172 | add_subdirectory (sqlite3) 173 | add_subdirectory (src) 174 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "configurePresets": [ 4 | { 5 | "name": "base", 6 | "hidden": true, 7 | "generator": "Ninja", 8 | "binaryDir": "${sourceDir}/_build/objs/${presetName}", 9 | "installDir": "${sourceDir}/_build/install/${presetName}", 10 | "cacheVariables": { 11 | "CMAKE_EXPORT_COMPILE_COMMANDS": true 12 | } 13 | }, 14 | { 15 | "name": "windows-base", 16 | "hidden": true, 17 | "inherits": "base", 18 | "condition": { 19 | "type": "equals", 20 | "lhs": "${hostSystemName}", 21 | "rhs": "Windows" 22 | } 23 | }, 24 | { 25 | "name": "compiler-cl", 26 | "hidden": true, 27 | "cacheVariables": { 28 | "CMAKE_C_COMPILER": "cl", 29 | "CMAKE_CXX_COMPILER": "cl" 30 | } 31 | }, 32 | { 33 | "name": "compiler-clang-cl", 34 | "hidden": true, 35 | "cacheVariables": { 36 | "CMAKE_C_COMPILER": "clang-cl", 37 | "CMAKE_CXX_COMPILER": "clang-cl" 38 | }, 39 | "vendor": { 40 | "microsoft.com/VisualStudioSettings/CMake/1.0": { 41 | "intelliSenseMode": "windows-clang-x64" 42 | } 43 | } 44 | }, 45 | { 46 | "name": "x64", 47 | "hidden": true, 48 | "architecture": { 49 | "value": "x64", 50 | "strategy": "external" 51 | } 52 | }, 53 | { 54 | "name": "x86", 55 | "hidden": true, 56 | "architecture": { 57 | "value": "x86", 58 | "strategy": "external" 59 | } 60 | }, 61 | { 62 | "name": "windows-base-debug", 63 | "hidden": true, 64 | "inherits": "windows-base", 65 | "cacheVariables": { 66 | "CMAKE_BUILD_TYPE": "Debug" 67 | } 68 | }, 69 | { 70 | "name": "windows-base-release", 71 | "hidden": true, 72 | "inherits": "windows-base", 73 | "cacheVariables": { 74 | "CMAKE_BUILD_TYPE": "Release" 75 | } 76 | }, 77 | { 78 | "name": "x64-debug", 79 | "displayName": "x64 Debug", 80 | "inherits": [ 81 | "windows-base-debug", 82 | "compiler-cl", 83 | "x64" 84 | ] 85 | }, 86 | { 87 | "name": "x64-release", 88 | "displayName": "x64 Release", 89 | "inherits": [ 90 | "windows-base-release", 91 | "compiler-cl", 92 | "x64" 93 | ] 94 | }, 95 | { 96 | "name": "x64-debug-clang", 97 | "displayName": "clang x64 Debug", 98 | "inherits": [ 99 | "windows-base-debug", 100 | "compiler-clang-cl", 101 | "x64" 102 | ] 103 | }, 104 | { 105 | "name": "x64-release-clang", 106 | "displayName": "clang x64 Release", 107 | "inherits": [ 108 | "windows-base-release", 109 | "compiler-clang-cl", 110 | "x64" 111 | ] 112 | }, 113 | { 114 | "name": "x86-debug", 115 | "displayName": "x86 Debug", 116 | "inherits": [ 117 | "windows-base-debug", 118 | "compiler-cl", 119 | "x86" 120 | ] 121 | }, 122 | { 123 | "name": "x86-release", 124 | "displayName": "x86 Release", 125 | "inherits": [ 126 | "windows-base-release", 127 | "compiler-cl", 128 | "x86" 129 | ] 130 | }, 131 | { 132 | "name": "linux-base", 133 | "hidden": true, 134 | "inherits": "base", 135 | "displayName": "Linux Debug", 136 | "condition": { 137 | "type": "equals", 138 | "lhs": "${hostSystemName}", 139 | "rhs": "Linux" 140 | }, 141 | "vendor": { 142 | "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { 143 | "sourceDir": "$env{HOME}/projects/$ms{projectDirName}", 144 | "copySourcesOptions": { 145 | "exclusionList": [ 146 | ".vs", 147 | ".git", 148 | "out", 149 | "_build", 150 | "1C" 151 | ] 152 | } 153 | } 154 | } 155 | }, 156 | { 157 | "name": "linux-debug", 158 | "displayName": "Linux Debug", 159 | "inherits": "linux-base", 160 | "hidden": true, 161 | "cacheVariables": { 162 | "CMAKE_BUILD_TYPE": "Debug" 163 | } 164 | }, 165 | { 166 | "name": "linux-release", 167 | "displayName": "Linux Release", 168 | "inherits": "linux-base", 169 | "hidden": true, 170 | "cacheVariables": { 171 | "CMAKE_BUILD_TYPE": "Release" 172 | } 173 | }, 174 | { 175 | "name": "linux-clang-16-debug", 176 | "displayName": "Linux Clang 16 Debug", 177 | "inherits": "linux-debug", 178 | "cacheVariables": { 179 | "CMAKE_C_COMPILER": "/usr/bin/clang-16", 180 | "CMAKE_CXX_COMPILER": "/usr/bin/clang++-16" 181 | } 182 | }, 183 | { 184 | "name": "linux-clang-16-release", 185 | "displayName": "Linux Clang 16 Release", 186 | "inherits": "linux-release", 187 | "cacheVariables": { 188 | "CMAKE_C_COMPILER": "/usr/bin/clang-16", 189 | "CMAKE_CXX_COMPILER": "/usr/bin/clang++-16" 190 | } 191 | }, 192 | { 193 | "name": "linux-clang-21-debug", 194 | "displayName": "Linux Clang 21 Debug", 195 | "inherits": "linux-debug", 196 | "cacheVariables": { 197 | "CMAKE_C_COMPILER": "/usr/bin/clang-21", 198 | "CMAKE_CXX_COMPILER": "/usr/bin/clang++-21" 199 | } 200 | }, 201 | { 202 | "name": "linux-clang-21-release", 203 | "displayName": "Linux Clang 21 Release", 204 | "inherits": "linux-release", 205 | "cacheVariables": { 206 | "CMAKE_C_COMPILER": "/usr/bin/clang-21", 207 | "CMAKE_CXX_COMPILER": "/usr/bin/clang++-21" 208 | } 209 | } 210 | ], 211 | "buildPresets": [ 212 | { 213 | "name": "buildPresets", 214 | "displayName": "Custom build preset", 215 | "description": "Custom build preset description", 216 | "configurePreset": "x64-release" 217 | } 218 | ] 219 | } 220 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 orefkov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/core_as/testable_static_assert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | /* 4 | * Чтобы тестировать static_assert'ы - в тестовом билде заменяем их на рантайм проверки с выбрасыванием исключения 5 | */ 6 | 7 | struct _testable_static_assert { 8 | _testable_static_assert(bool condition, const char* text) { 9 | if (!condition) { 10 | throw std::logic_error{text}; 11 | } 12 | } 13 | }; 14 | 15 | #ifdef BUILD_TESTS 16 | #define testable_static_assert(p1, p2) \ 17 | _testable_static_assert __tsa##__COUNTER__ { \ 18 | p1, p2 \ 19 | } 20 | #else 21 | #define testable_static_assert static_assert 22 | #endif 23 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # V8Sqlite 2 | 3 | Нативная внешняя компонента для 1С 8 для работы с базами данных sqlite. 4 | Позволяет выполнять запросы к базам данных sqlite и получать их результаты в виде ТаблицыЗначений или массива массивов. 5 | 6 | Поддерживает работу в: 7 | - Windows x86 8 | - Windows x64 9 | - Linux x64 10 | 11 | Сама библиотека sqlite подключена статически, то есть внедрена в ВК. 12 | Используется версия 3.49.2. Версию sqlite можно узнать запросом 13 | 14 | ``` 15 | select sqlite_version() 16 | ``` 17 | 18 | ## Кодировка текста 19 | Базы данных sqlite хранят текст в одной кодировке, выбираемой при создании базы данных - UTF-8 либо UTF-16. 20 | Несмотря на то, что работать возможно с базами с любой кодировкой, стоит учитывать, что внутри 1С для данных 21 | типа "Строка" используется кодировка UTF-16, поэтому при чтении текста из баз с этой кодировкой движку sqlite 22 | не нужно выполнять каждый раз перекодировку текста из UTF-8 в UTF-16 и наоборот. 23 | Поэтому из соображений производительности, если вы планируете пользоваться базой данных в-основном в 1С, 24 | создавайте её в кодировке UTF-16. Для этого после первого открытия базы данных выполните `pragma encoding`: 25 | 26 | ``` 27 | // Создать пустую базу в файле 28 | ИмяФайла = ПолучитьИмяВременногоФайла("db"); 29 | база.ОткрытьБазуДанных(ИмяФайла); 30 | база.Выполнить("pragma encoding='UTF-16'"); 31 | ``` 32 | 33 | ## Поддержка Unicode 34 | В компоненте к движку sqlite добавлена минимальная поддержка регистронезависимости Unicode-символов, для работы функций `upper`, `lower`, 35 | `fold`, `title`, `unaccent`, `like` и `collate nocase`. Правильно обрабатываются символы только из первой плоскости Unicode (до 0xFFFF). 36 | Кроме того, преобразование регистра выполняется только "один символ - в один символ", т.е. некоторые случаи, при которых один символ 37 | может в другом регистре стать двумя символами и наоборот - не учитываются. Для регистронезависимого сравнения используется `fold`. 38 | 39 | ## Сборка 40 | Собирается как cmake проект в Visual Studio Community Edition 2022. Под windows проверял в MSVC и Clang. 41 | Под linux собирается подключением к удалённой машине с установленным Clang, собирал на clang-16. 42 | В бинарной сборке: 43 | - win x86 собрана MSVC 44 | - win x64 - clang-cl 19 45 | - Linux x64 - clang-16 под Ubuntu 18.04 с GLIBC 2.25, clang-18 под Ubuntu 22 - с GLIBC 2.34 46 | 47 | ## Использование 48 | 49 | Подключается как обычная нативная ВК, например так 50 | 51 | ``` 52 | Функция ПодключитьВК() 53 | 54 | ПутьКВК = КаталогПрограммы(); 55 | 56 | СистемнаяИнформация = Новый СистемнаяИнформация; 57 | 58 | Если СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86 или СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86_64 Тогда 59 | ПутьКВК = ПутьКВК + "v8sqlite.dll"; 60 | ИначеЕсли СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Linux_x86_64 Тогда 61 | ПутьКВК = ПутьКВК + "libv8sqlite.so"; 62 | КонецЕсли; 63 | 64 | Если не ПодключитьВнешнююКомпоненту(ПутьКВК, "v8sqlite", ТипВнешнейКомпоненты.Native) Тогда 65 | Предупреждение("Не удалось подключить внешнюю компоненту " + ПутьКВК); 66 | Возврат Ложь; 67 | КонецЕсли; 68 | Возврат Истина; 69 | 70 | КонецФункции // ПодключитьВК() 71 | 72 | ``` 73 | 74 | После подключения доступен к созданию объект внешней компоненты, через который и можно будет работать с базой даных sqlite. 75 | 76 | ``` 77 | база = Новый("AddIn.v8sqlite.v8sqlite"); 78 | ``` 79 | --- 80 | 81 | ## Свойства объекта 82 | 83 | ### Версия / Version 84 | Строка, только чтение. Версия компоненты. 85 | 86 | --- 87 | 88 | ### `БросатьОписаниеОшибки / ThrowErrorDescription` 89 | Булево, чтение/запись. По умолчанию Ложь. 90 | 91 | Флаг, нужно ли при возникновении исключения добавлять текстовое описание ошибки штатным для ВК методом `AddError`. 92 | Дело в том, что вывод такого сообщения в окно сообщений невозможно подавить. Даже если вы отловили исключение с помощью 93 | `Попытка Исключение` и программа продолжает работу - в окно сообщений всё-равно выдаст текст ошибки. 94 | Если значение свойства `Ложь`, то текст ошибки не будет выдавать в исключении (будет просто штатное "Ошибка при вызове метода объекта"), 95 | которое не выдает сообщение в случае отлова попыткой. Описание ошибки в этом случае можно прочитать в свойстве `ОписаниеОшибки`. 96 | 97 | --- 98 | 99 | ### `ОписаниеОшибки / ErrorDescription` 100 | Строка. Только чтение. 101 | Содержит текстовое описание последней ошибки. Ошибка может быть как возвращенная sqlite базой, так и самой компоненты. 102 | 103 | ``` 104 | Попытка 105 | база.Выполнить("create table test(a,b,c"); 106 | Исключение 107 | Сообщить(база.ОписаниеОшибки); 108 | КонецПопытки; 109 | 110 | ``` 111 | 112 | --- 113 | 114 | ### `БазаДанныхОткрыта / isDataBaseOpen` 115 | Булево. Только чтение. 116 | Показывает, открыта ли в данный момент база данных. 117 | 118 | --- 119 | 120 | ### `ПоследнийИд / LastId` 121 | Число. Только чтение. 122 | После выполнения операций вставки выдает ID последней вставленной строки. 123 | 124 | --- 125 | 126 | ### `СтрокИзменено / Changes` 127 | Число. Только чтение. 128 | Показывает, сколько строк было вставлено/изменено во время выполнения последнего запроса. 129 | 130 | --- 131 | 132 | ## Методы объекта 133 | 134 | ### `ОткрытьБазуДанных(ИмяФайла) / OpenDataBase(FileName)` 135 | Открывает базу данных. Если была открыта база данных, она закрывается. Если файла с заданным именем не существует, 136 | будет создана новая пустая база данных. 137 | 138 | Параметры: 139 | - ИмяФайла: Строка, обязательный. Имя открываемого файла. Специальное имя `":memory:"` открывает базу данных в памяти. 140 | Поддерживается имена в [URI формате](https://www.sqlite.org/uri.html), позволяющие задавать дополнительные параметры отрытия 141 | файла. 142 | 143 | Возвращаемое значение - нет. При ошибке выбрасывает исключение. 144 | 145 | Пример: 146 | ``` 147 | база = Новый("AddIn.v8sqlite.v8sqlite"); 148 | // Открыть базу в памяти 149 | база.ОткрытьБазуДанных(":memory:"); 150 | ... 151 | // Создать пустую базу в файле 152 | ИмяФайла = ПолучитьИмяВременногоФайла("db"); 153 | база.ОткрытьБазуДанных(ИмяФайла); 154 | 155 | ``` 156 | --- 157 | 158 | ### `ЗакрытьБазуДанных() / CloseDataBase()` 159 | Закрывает открытую базу данных, если она была ранее открыта. 160 | Параметров нет. Возвращаемого значения нет. 161 | 162 | --- 163 | 164 | ### `Выполнить(ТекстЗапроса) / Exec(QueryText)` 165 | Выполняет переданный текст запроса с помощью `sqlite3_exec`. 166 | Служит в основном для выполнения запросов insert/update/delete, то есть не возвращающих результата. 167 | Можно указывать текст нескольких запросов, разделяя их `;`. 168 | 169 | Пример: 170 | ``` 171 | база = Новый("AddIn.v8sqlite.v8sqlite"); 172 | база.ОткрытьБазуДанных(":memory:"); 173 | база.Выполнить(" 174 | |create table test(a,b,c); 175 | |insert into test values 176 | |(1, 2.3, null), 177 | |('text', x'414243', datetime('now')) 178 | |"); 179 | ``` 180 | Возвращаемого значения нет. 181 | 182 | --- 183 | 184 | ### `ПодготовитьЗапрос(ИмяЗапроса, ТекстЗапроса) / PrepareQuery(QueryName, QueryText)` 185 | Подготавливает запрос для дальнейшего неоднократного выполнения. 186 | 187 | Параметры: 188 | - ИмяЗапроса - Строка, обязательный. Любое уникальное в пределах этой базы имя для дальнейшего обращения к запросу. 189 | Если запрос с таким именем уже был подготовлен ранее, он будет заменён. 190 | - Текст запроса - Строка, обязательный. Текст sql-запроса. 191 | 192 | Возвращаемого значения нет. При ошибке в тексте запроса - выкинет исключение. 193 | 194 | Пример: 195 | ``` 196 | база = Новый("AddIn.v8sqlite.v8sqlite"); 197 | база.ОткрытьБазуДанных(":memory:"); 198 | база.ПодготовитьЗапрос("Запрос1", " 199 | |select * from test where a = @p1 200 | |"); 201 | ``` 202 | --- 203 | 204 | ### `УстановитьПараметр(ИмяЗапроса, ИмяПараметра, ЗначениеПараметра) / BindParam(QueryName, ParamName, ParamValue)` 205 | Устанавливает значение sql-параметра для ранее подготовленного запроса. 206 | 207 | Параметры: 208 | - ИмяЗапроса - Строка, обязательный. Имя, которое использовалоь в методе `ПодготовитьЗапрос`. Задаёт, для какого запроса 209 | устанавливается параметр. 210 | - ИмяПараметра - Строка или Число. Задает, какой параметр устанавливается. Передаётся либо имя параметра, либо его номер. 211 | Нумерация параметров с `1`. 212 | - ЗначениеПараметра - Число, Строка, ДвоичныеДанные, Дата, Неопределено, Null, Булево. Необязательный. 213 | При отсутствии, Неопределено или Null - устанавливает null. 214 | Дата конвертируется в формат `YYYY-MM-DD HH:MM:SS` - так, как возвращает функция sqlite `datetime`. 215 | Булево преобразуется в 0 или 1. 216 | 217 | Возвращаемого значения нет. 218 | 219 | Пример: 220 | ``` 221 | база = Новый("AddIn.v8sqlite.v8sqlite"); 222 | база.ОткрытьБазуДанных(":memory:"); 223 | база.ПодготовитьЗапрос("Запрос1", " 224 | |select * from test where a = @p1 225 | |"); 226 | 227 | ... 228 | 229 | база.УстановитьПараметр("Запрос1", "@p1", 1); 230 | ``` 231 | --- 232 | 233 | ### `ВыполнитьЗапрос(Запрос, ФорматОтвета, [КолонкиСДатой]) / ExecQuery(Query, AnswerFormat, [DateColumns])` 234 | Выполняет запрос с выдачей результата. 235 | 236 | Параметры: 237 | - Запрос - Строка, обязательный. Здесь можно либо указать имя ранее подготовленного запроса, либо просто 238 | текст запроса. В этом случае запрос подготавливается, выполняется и закрывается. В случае подготовленного 239 | запроса он выполняется с учётом установленных параметров, после все установленные параметры сбрасываются. 240 | - ФорматОтвета - Строка, обязательный. Задаёт, в каком формате будет выдаваться результат выполнения запроса. 241 | Может принимать значения "JSON" или "ТаблицаЗначений" / "ValueTable". Регистронезависимый. 242 | - КолонкиСДатой - Строка, необязательный. Перечисленные через запятую названия или номера колонок, значения 243 | в которых нужно преобразовать в Дату. Нумерация колонок с нуля. Чтобы преобразование сработало, реальное 244 | значение должно быть строкой длиной 19 символов, т.е. датой в формате "YYYY-MM-DD HH:MM:SS", так, как выдаёт 245 | функция sqlite `datetime`. 246 | Компонента не парсит дату, а просто берёт куски из этой строки и расставляет в нужном 1С формате. 247 | 248 | Возвращаемое значение: строка, если выполняется запрос `select`. 249 | Для остальных видов запросов - пустое значение. 250 | При возникновении sql-ошибки - выкидывает исключение. 251 | 252 | Возвращаемая строка зависит от указанного при вызове метода формате. 253 | Для формата "ТаблицаЗначений" - выдаётся строка, которая методом `ЗначениеИзСтрокиВнутр` преобразуется 254 | в Таблицу значений. Названия колонок запроса становятся именем и заголовком колонок таблицы значений. 255 | 256 | Для формата "JSON" возвращается строка, которая через XDTO преобразуется в массив. 257 | Элементы массива - тоже массивы, строки результата запроса. 258 | Самой первой строкой идёт массив с названиями колонок. 259 | 260 | Преобразование типов sqlite: 261 | - `integer`, `real` - преобразуются в тип 1С `Число`. 262 | - `text` - в тип `Строка`. 263 | - `null` - в `Null`. 264 | - `blob` - в `ДвоичныеДанные`. 265 | 266 | Если для колонки задано преобразование в Дату, компонента попытается это сделать, 267 | см. описание параметра `КолонкиСДатой`. 268 | 269 | Пример: 270 | ``` 271 | // Получение как ТЗ 272 | тз = ЗначениеИзСтрокиВнутр(база.ВыполнитьЗапрос(" 273 | |select a, b, c as dt 274 | |from test where a = 100 275 | |", "ТаблицаЗначений", "dt")); 276 | 277 | // Получение как массива массивов 278 | Функция JSONВМассив(текст) 279 | ч = Новый ЧтениеJSON; 280 | ч.УстановитьСтроку(текст); 281 | Возврат СериализаторXDTO.ПрочитатьJSON(ч); 282 | КонецФункции 283 | 284 | ... 285 | 286 | результат = JSONВМассив(база.ВыполнитьЗапрос(" 287 | |select a [col a], b, c as dt 288 | |from test 289 | |", "json", "dt")); 290 | 291 | ``` 292 | --- 293 | 294 | ### `УдалитьЗапрос(ИмяЗапроса) / RemoveQuery(QueryName)` 295 | Закрывает ранее подготовленный запрос и удаляет его из списка запросов. 296 | При закрытии базы все подготовленные запросы удаляются автоматически. 297 | 298 | Параметры: 299 | - ИмяЗапроса - строка, обязательный. Имя запроса, использованное ранее в методе `ПодготовитьЗапрос`. 300 | 301 | Возвращаемого значения нет. 302 | 303 | Пример: 304 | 305 | ``` 306 | база.УдалитьЗапрос("Запрос1"); 307 | ``` 308 | -------------------------------------------------------------------------------- /sqlite3/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.15 FATAL_ERROR) 2 | 3 | add_library(sqlite3 sqlite3.c) 4 | 5 | target_compile_definitions(sqlite3 PRIVATE 6 | SQLITE_DQS=0 7 | SQLITE_THREADSAFE=2 8 | SQLITE_TEMP_STORE=2 9 | SQLITE_DEFAULT_FOREIGN_KEYS=1 10 | SQLITE_ENABLE_COLUMN_METADATA 11 | SQLITE_ENABLE_DBSTAT_VTAB 12 | SQLITE_ENABLE_EXPLAIN_COMMENTS 13 | SQLITE_ENABLE_FTS5 14 | SQLITE_ENABLE_MEMORY_MANAGEMENT 15 | SQLITE_ENABLE_RTREE 16 | SQLITE_ENABLE_STAT4 17 | SQLITE_ENABLE_STMTVTAB 18 | SQLITE_ENABLE_UPDATE_DELETE_LIMIT 19 | SQLITE_ENABLE_UNLOCK_NOTIFY 20 | SQLITE_ENABLE_BYTECODE_VTAB 21 | SQLITE_ENABLE_DBPAGE_VTAB 22 | SQLITE_ENABLE_DBSTAT_VTAB 23 | SQLITE_ENABLE_NORMALIZE 24 | SQLITE_ENABLE_NULL_TRIM 25 | SQLITE_ENABLE_SNAPSHOT 26 | SQLITE_USE_URI 27 | SQLITE_USE_ALLOCA 28 | SQLITE_OMIT_LOAD_EXTENSION 29 | ) 30 | 31 | if (WIN32) 32 | target_compile_definitions(sqlite3 PRIVATE UNICODE _WINDOWS _CRT_SECURE_NO_WARNINGS _CRT_SECURE_NO_DEPRECATE) 33 | target_compile_options(sqlite3 PRIVATE "$<$:/GL>") 34 | endif() 35 | 36 | if (UNIX) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.6 FATAL_ERROR) 2 | 3 | if (WIN32) 4 | ENABLE_LANGUAGE(RC) 5 | endif () 6 | 7 | set (V8SQLITE_SRC 8 | v8sqlite_addin.cpp 9 | v8sqlite_addin.h 10 | stdafx.cpp 11 | stdafx.h 12 | addin_base.cpp 13 | sqlite.cpp 14 | sqlite3_unicode.c 15 | ) 16 | 17 | if (WIN32) 18 | LIST(APPEND V8SQLITE_SRC dllmain.cpp v8sqlite.rc v8sqlite.def) 19 | endif() 20 | 21 | add_library(${PROJECT_NAME} SHARED ${V8SQLITE_SRC}) 22 | target_link_libraries(${PROJECT_NAME} sqlite3 simstr) 23 | 24 | add_compile_definitions( 25 | SQLITE_CORE 26 | SQLITE_ENABLE_UNICODE 27 | ) 28 | 29 | if (WIN32) 30 | add_compile_definitions( 31 | UNICODE 32 | _WINDOWS 33 | _USRDLL 34 | _CRT_SECURE_NO_WARNINGS 35 | _CRT_SECURE_NO_DEPRECATE 36 | ADDINCPP_EXPORTS 37 | ) 38 | else () 39 | if (V8SQLITE_STATIC_RUNTIME) 40 | target_link_options(${PROJECT_NAME} PRIVATE -static-libgcc -static-libstdc++) 41 | endif () 42 | target_link_options(${PROJECT_NAME} PRIVATE -Wl,--no-undefined -Xlinker --version-script -Xlinker ${CMAKE_CURRENT_SOURCE_DIR}/version.script) 43 | target_link_libraries(${PROJECT_NAME} pthread) 44 | endif() 45 | 46 | set_target_properties( ${PROJECT_NAME} PROPERTIES 47 | POSITION_INDEPENDENT_CODE ON 48 | ) 49 | 50 | if (NOT WIN32) 51 | add_custom_command( 52 | TARGET ${PROJECT_NAME} POST_BUILD 53 | DEPENDS ${PROJECT_NAME} 54 | COMMAND $<$:strip> 55 | ARGS --strip-all $ 56 | ) 57 | endif() 58 | 59 | if (BUILD_TESTS) 60 | add_subdirectory(tests) 61 | endif () 62 | -------------------------------------------------------------------------------- /src/addin_base.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "addin_base.h" 3 | 4 | namespace { 5 | 6 | AppCapabilities g_capabilities = eAppCapabilitiesInvalid; 7 | 8 | } // namespace 9 | 10 | long GetClassObject(const WCHAR_T* wsName, IComponentBase** pInterface) { 11 | if (!*pInterface) { 12 | stru name{wsName}; 13 | for (const AddinInfo* ptr = AddinInfo::first; ptr; ptr = ptr->next) { 14 | if (ptr->name.equal_iu(name)) { 15 | *pInterface = ptr->create(); 16 | break; 17 | } 18 | } 19 | return *pInterface != nullptr; 20 | } 21 | return 0; 22 | } 23 | 24 | AppCapabilities SetPlatformCapabilities(const AppCapabilities capabilities) { 25 | g_capabilities = capabilities; 26 | return eAppCapabilitiesLast; 27 | } 28 | 29 | AttachType GetAttachType() { 30 | return eCanAttachAny; 31 | } 32 | 33 | long DestroyObject(IComponentBase** pIntf) { 34 | if (!*pIntf) 35 | return -1; 36 | 37 | delete *pIntf; 38 | *pIntf = 0; 39 | return 0; 40 | } 41 | 42 | const WCHAR_T* GetClassNames() { 43 | static auto classes = []() { 44 | lstringu<100> classes; 45 | for (AddinInfo* ptr = AddinInfo::first; ptr; ) { 46 | classes += ptr->name; 47 | ptr = ptr->next; 48 | if (ptr) { 49 | classes += u"|"; 50 | } else { 51 | break; 52 | } 53 | } 54 | return classes; 55 | }(); 56 | return classes; 57 | } 58 | -------------------------------------------------------------------------------- /src/addin_base.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "AddInDefBase.h" 3 | #include "ComponentBase.h" 4 | #include "IMemoryManager.h" 5 | #include "simstr/sstring.h" 6 | #include "core_as/testable_static_assert.h" 7 | #include 8 | 9 | using namespace simstr; 10 | 11 | inline stru varToTextU(const tVariant& v) { 12 | return {v.pwstrVal, v.wstrLen}; 13 | } 14 | 15 | inline stra varToTextA(const tVariant& v) { 16 | return {v.pstrVal, v.strLen}; 17 | } 18 | 19 | inline static bool isInteger(const tVariant& v) { 20 | switch (v.vt) { 21 | case VTYPE_I1: 22 | case VTYPE_I2: 23 | case VTYPE_I4: 24 | case VTYPE_UI1: 25 | case VTYPE_UI2: 26 | case VTYPE_UI4: 27 | case VTYPE_ERROR: 28 | return true; 29 | default: 30 | return false; 31 | } 32 | } 33 | 34 | inline static int getInteger(const tVariant& v) { 35 | switch (v.vt) { 36 | case VTYPE_I1: 37 | return v.i8Val; 38 | case VTYPE_I2: 39 | return v.shortVal; 40 | case VTYPE_I4: 41 | return v.intVal; 42 | case VTYPE_UI1: 43 | return v.ui8Val; 44 | case VTYPE_UI2: 45 | return v.ushortVal; 46 | case VTYPE_UI4: 47 | return v.uintVal; 48 | case VTYPE_ERROR: 49 | return v.errCode; 50 | default: 51 | return 0; 52 | } 53 | } 54 | 55 | class AddinBase : public IComponentBase { 56 | public: 57 | bool ADDIN_API setMemManager(void* mem) override { 58 | memoryManager_ = reinterpret_cast(mem); 59 | return true; 60 | } 61 | 62 | long ADDIN_API GetInfo() override { 63 | return 2000; 64 | } 65 | 66 | void ADDIN_API SetLocale(const WCHAR_T* loc) override { 67 | locale_ = stru{loc}; 68 | } 69 | 70 | void ADDIN_API SetUserInterfaceLanguageCode(const WCHAR_T* lang) override { 71 | uiLanguageCode_ = stru{lang}; 72 | } 73 | 74 | template 75 | T* v8alloc(size_t size) const { 76 | T* destination = nullptr; 77 | memoryManager_->AllocMemory(reinterpret_cast(&destination), (unsigned long)size * sizeof(T)); 78 | return destination; 79 | } 80 | WCHAR_T* copyText(auto&& text) const { 81 | WCHAR_T* destination = v8alloc(text.length() + 1); 82 | *text.place(destination) = 0; 83 | return destination; 84 | } 85 | 86 | protected: 87 | stru selectLocaleStr(stru rus, stru eng) { 88 | return uiLanguageCode_.starts_with_ia(u"ru") || locale_.starts_with_ia(u"ru") ? rus : eng; 89 | } 90 | IAddInDefBaseEx* v8connection_{}; 91 | IMemoryManager* memoryManager_{}; 92 | stringu locale_; 93 | stringu uiLanguageCode_; 94 | stringu lastError_; 95 | bool throwErrors_{}; 96 | }; 97 | 98 | struct AddinInfo { 99 | ssu name; 100 | IComponentBase* (*create)(); 101 | AddinInfo* next; 102 | inline static AddinInfo* first{nullptr}; 103 | 104 | AddinInfo(ssu n, IComponentBase* (*c)()) : name(n), create(c), next(first) { 105 | first = this; 106 | } 107 | }; 108 | 109 | // Основа инфраструктуры работы с описаниями методов/свойств 110 | struct first_def { 111 | enum { 112 | val = -1, 113 | IsFunc = -1, 114 | nParam = -1, 115 | IsDefVal = -1, 116 | IsRead = -1, 117 | IsWrite = -1, 118 | }; 119 | }; 120 | 121 | // Стратегии поиска номера элемента по его имени 122 | 123 | // Стратегия при отсутствии элементов 124 | template 125 | struct nofnd_pol { 126 | static void OnInitFirstObject() {} 127 | static int find(ssu name) { 128 | return -1; 129 | } 130 | }; 131 | 132 | // Стратегия при наличии всего одного элемента 133 | template 134 | struct onefnd_pol { 135 | static void OnInitFirstObject() {} 136 | static int find(ssu name) { 137 | if (0 == name.compare_iu(T::prev::rusName) || 0 == name.compare_iu(T::prev::engName)) 138 | return 0; 139 | return -1; 140 | } 141 | }; 142 | 143 | // Стратегия поиска при количестве элементов от 2 до 3 144 | template 145 | struct simplefnd_pol { 146 | static void OnInitFirstObject() {} 147 | static int find(ssu name) { 148 | return find_(name); 149 | } 150 | 151 | template 152 | static int find_(ssu name) { 153 | if (0 == name.compare_iu(M::rusName) || 0 == name.compare_iu(M::engName)) 154 | return M::val; 155 | return find_(name); 156 | } 157 | 158 | template<> 159 | static int find_(ssu name) { 160 | return -1; 161 | } 162 | }; 163 | 164 | // Стратегия при количестве элементов больше 3 165 | template 166 | struct hashfnd_pol { 167 | inline static hashStrMapUIU names; 168 | static void OnInitFirstObject() { 169 | init(); 170 | } 171 | 172 | template 173 | static void init() { 174 | names.emplace(M::rusName, M::val); 175 | names.emplace(M::engName, M::val); 176 | init(); 177 | } 178 | 179 | template<> 180 | static void init() {} 181 | 182 | static int find(ssu name) { 183 | if (auto fnd = names.find(name); fnd != names.end()) { 184 | return fnd->second; 185 | } 186 | return -1; 187 | } 188 | }; 189 | 190 | // Мета-функция, выбирает тип поиска номера по строке, 191 | // в зависимости от количества методов/свойств. 192 | // Использовать: fndselector::type, fndselector::type 193 | template 194 | struct fndselector { 195 | template 196 | struct tdef; 197 | template<> 198 | struct tdef<0> { 199 | using type = nofnd_pol; 200 | }; 201 | template<> 202 | struct tdef<1> { 203 | using type = onefnd_pol; 204 | }; 205 | template<> 206 | struct tdef<2> { 207 | using type = simplefnd_pol; 208 | }; 209 | template<> 210 | struct tdef<3> { 211 | using type = hashfnd_pol; 212 | }; 213 | 214 | enum { 215 | Count = T::val, 216 | val = Count == 0 ? 0 : (Count == 1 ? 1 : (Count < 4 ? 2 : 3)), 217 | }; 218 | using type = typename tdef::type; 219 | }; 220 | 221 | // Возврат имени из массива имен по индексу. 222 | // Массив имен создается при первом обращении к name. 223 | template 224 | struct getname { 225 | template 226 | struct finder { 227 | stru names[Count][2]; 228 | finder() { 229 | fill(); 230 | } 231 | template 232 | void fill() { 233 | names[M::val][0] = M::engName; 234 | names[M::val][1] = M::rusName; 235 | fill(); 236 | } 237 | template<> 238 | void fill() {} 239 | 240 | static const WCHAR_T* name(unsigned methodNumber, unsigned language, AddinBase* a) { 241 | static const finder f; 242 | if (methodNumber < Count && language < 2) { 243 | return a->copyText(f.names[methodNumber][language]); 244 | } 245 | return nullptr; 246 | } 247 | }; 248 | template<> 249 | struct finder<0> { 250 | static const WCHAR_T* name(unsigned, unsigned, AddinBase*) { 251 | return nullptr; 252 | } 253 | }; 254 | using type = finder; 255 | }; 256 | 257 | // Набор различных метафункций, работающих со списками типов 258 | // описаний методов и свойств. 259 | 260 | // Проверка на то, есть END_EXTENSION() и он в правильном месте 261 | template 262 | struct test_last { 263 | testable_static_assert(I < T::val, "No END_EXTENSION in your class"); 264 | }; 265 | 266 | // Проверка на то, что методы с параметрами по умолчанию имеют не меньше 1 параметра 267 | template 268 | struct check_defval { 269 | testable_static_assert(!(bDef && I == 0), "Def val with 0 params"); 270 | }; 271 | 272 | // Мета-функция, подсчитывает, сколько среди методов процедур и функций. 273 | // Использовать: 274 | // meth_count::proc - количество процедур 275 | // meth_count::func - количество функций 276 | template 277 | struct meth_count { 278 | template 279 | struct counter { 280 | template 281 | struct check { 282 | enum { 283 | next = M::prev::val == -1 ? 0 : 1, 284 | val = static_cast(M::IsFunc) + counter::template check::val, 285 | }; 286 | }; 287 | }; 288 | template<> 289 | struct counter<0> { 290 | template 291 | struct check { 292 | enum { val = 0 }; 293 | }; 294 | }; 295 | 296 | enum { 297 | first = T::endmeths::prev::val == -1 ? 0 : 1, 298 | func = counter::template check::val, 299 | proc = static_cast(T::endmeths::val) - func, 300 | }; 301 | }; 302 | 303 | // Мета-функция, определяет, во всех-ли методах одинаковое количество параметров 304 | // Использовать: same_np::val 305 | // Возвращает: -1 - в методах разное количество параметров, N - во всех методах N параметров 306 | template 307 | struct same_np { 308 | template 309 | struct _same_np { 310 | template 311 | struct check { 312 | enum { val = I }; 313 | }; 314 | }; 315 | template<> 316 | struct _same_np<-1> { 317 | template 318 | struct check { 319 | enum { val = -1 }; 320 | }; 321 | }; 322 | template<> 323 | struct _same_np<-2> { 324 | template 325 | struct check { 326 | enum { 327 | stop = M::prev::val == -1 ? 1 : 0, 328 | same = int(M::nParam) == M::prev::nParam ? 1 : 0, 329 | next = stop ? M::nParam : (same ? -2 : -1), 330 | val = _same_np::template check::val, 331 | }; 332 | }; 333 | }; 334 | enum { first = T::endmeths::prev::val == -1 ? 0 : -2, val = _same_np::template check::val }; 335 | }; 336 | 337 | // Мета-функция, подсчитывает, сколько параметров доступно для чтения/записи. 338 | // Использовать: 339 | // rw_count::read - доступных для чтения 340 | // rw_count::write - доступных для записи 341 | // rw_count::noread - недоступных для чтения 342 | // rw_count::nowrite - недоступных для записи 343 | template 344 | struct rw_count { 345 | template 346 | struct rcounter { 347 | template 348 | struct check { 349 | enum { 350 | next = M::prev::val == -1 ? 0 : 1, 351 | val = static_cast(M::IsRead) + static_cast(rcounter::template check::val), 352 | }; 353 | }; 354 | }; 355 | template<> 356 | struct rcounter<0> { 357 | template 358 | struct check { 359 | enum { val = 0 }; 360 | }; 361 | }; 362 | 363 | template 364 | struct wcounter { 365 | template 366 | struct check { 367 | enum { 368 | next = M::prev::val == -1 ? 0 : 1, 369 | val = static_cast(M::IsWrite) + static_cast(wcounter::template check::val), 370 | }; 371 | }; 372 | }; 373 | template<> 374 | struct wcounter<0> { 375 | template 376 | struct check { 377 | enum { val = 0 }; 378 | }; 379 | }; 380 | 381 | enum { 382 | first = T::endprops::prev::val == -1 ? 0 : 1, 383 | read = rcounter::template check::val, 384 | write = wcounter::template check::val, 385 | noread = static_cast(T::endprops::val) - read, 386 | nowrite = static_cast(T::endprops::val) - write, 387 | }; 388 | }; 389 | 390 | template 391 | struct meth_pol { 392 | enum { 393 | isFuncMin = meth_count::func <= meth_count::proc ? 1 : 0, 394 | nParamCmn = same_np::val, 395 | }; 396 | 397 | static bool HasRetVal(unsigned method) { 398 | return policy::template hasRetVal(method); 399 | } 400 | 401 | static long GetNParams(unsigned method) { 402 | return nparam::GetNParams(method); 403 | } 404 | 405 | static bool GetDefVal(const T* ptr, unsigned method, unsigned param, tVariant* value) { 406 | return defVal(ptr, method, param, value); 407 | } 408 | 409 | static bool CallAsProc(T* ptr, unsigned method, tVariant* params, unsigned count) { 410 | return callAsProc(ptr, method, params, count); 411 | } 412 | static bool CallAsFunc(T* ptr, unsigned method, tVariant& ret, tVariant* params, unsigned count) { 413 | return callAsFunc(ptr, method, ret, params, count); 414 | } 415 | 416 | template 417 | struct policy { 418 | template 419 | static bool hasRetVal(unsigned method) { 420 | if constexpr (M::IsFunc != 0) { 421 | if (method == M::val) { 422 | return true; 423 | } 424 | } 425 | return hasRetVal(method); 426 | } 427 | template<> 428 | static bool hasRetVal(unsigned) { 429 | return false; 430 | } 431 | }; 432 | template<> 433 | struct policy { 434 | template 435 | static bool hasRetVal(unsigned method) { 436 | if constexpr (!M::IsFunc) { 437 | if (method == M::val) { 438 | return false; 439 | } 440 | } 441 | return hasRetVal(method); 442 | } 443 | template<> 444 | static bool hasRetVal(unsigned) { 445 | return true; 446 | } 447 | }; 448 | 449 | template 450 | struct nparam { 451 | static long GetNParams(unsigned) { 452 | return I; 453 | } 454 | }; 455 | 456 | template<> 457 | struct nparam<-1> { 458 | static long GetNParams(unsigned method) { 459 | return _GetNParams(method); 460 | } 461 | template 462 | static long _GetNParams(unsigned method) { 463 | if (method == M::val) 464 | return M::nParam; 465 | return _GetNParams(method); 466 | } 467 | template<> 468 | static long _GetNParams(unsigned) { 469 | return -1; 470 | } 471 | }; 472 | 473 | template 474 | static bool defVal(const T* ptr, unsigned method, unsigned param, tVariant* value) { 475 | if (method == M::val) { 476 | if (M::IsDefVal && param < M::nParam) { 477 | return M::defVal(ptr, param, value); 478 | } 479 | return false; 480 | } 481 | return defVal(ptr, method, param, value); 482 | } 483 | template<> 484 | static bool defVal(const T*, unsigned, unsigned, tVariant*) { 485 | return false; 486 | } 487 | 488 | template 489 | static bool callAsProc(T* ptr, unsigned method, tVariant* params, unsigned count) { 490 | if (method == M::val) 491 | return !M::IsFunc && (!M::nParam || params != nullptr) && M::callProc(ptr, params, count); 492 | return callAsProc(ptr, method, params, count); 493 | } 494 | template<> 495 | static bool callAsProc(T*, unsigned, tVariant*, unsigned) { 496 | return false; 497 | } 498 | 499 | template 500 | static bool callAsFunc(T* ptr, unsigned method, tVariant& ret, tVariant* params, unsigned count) { 501 | if (method == M::val) 502 | return M::IsFunc && (!M::nParam || params != nullptr) && M::callFunc(ptr, ret, params, count); 503 | return callAsFunc(ptr, method, ret, params, count); 504 | } 505 | template<> 506 | static bool callAsFunc(T*, unsigned, tVariant&, tVariant*, unsigned) { 507 | return false; 508 | } 509 | }; 510 | 511 | template 512 | struct prop_pol { 513 | enum { 514 | bReadMin = rw_count::read < rw_count::noread ? 1 : 0, 515 | bWriteMin = rw_count::write < rw_count::nowrite ? 1 : 0, 516 | }; 517 | 518 | static bool IsPropReadable(unsigned prop) { 519 | return policy::template isRead(prop); 520 | } 521 | 522 | static bool IsPropWritable(unsigned prop) { 523 | return policy::template isWrite(prop); 524 | } 525 | 526 | static bool ReadProp(const T* ptr, unsigned prop, tVariant& val) { 527 | return readProp(ptr, prop, val); 528 | } 529 | 530 | static int WriteProp(T* ptr, unsigned prop, const tVariant& val) { 531 | return writeProp(ptr, prop, val); 532 | } 533 | 534 | template 535 | struct policy { 536 | template 537 | static bool isRead(unsigned prop) { 538 | // Читаемых меньше, чем не читаемых 539 | if constexpr (M::IsRead != 0) { 540 | if (prop == M::val) { 541 | return true; 542 | } 543 | } 544 | return isRead(prop); 545 | } 546 | template<> 547 | static bool isRead(unsigned) { 548 | return false; 549 | } 550 | 551 | template 552 | static bool isWrite(unsigned prop) { 553 | // Записываемых меньше, чем не записываемых 554 | if constexpr (M::IsWrite != 0) { 555 | if (prop == M::val) { 556 | return true; 557 | } 558 | } 559 | return isWrite(prop); 560 | } 561 | template<> 562 | static bool isWrite(unsigned) { 563 | return false; 564 | } 565 | }; 566 | template<> 567 | struct policy { 568 | template 569 | static bool isRead(unsigned prop) { 570 | // Читаемых больше, чем не читаемых 571 | if constexpr (!M::IsRead) { 572 | if (prop == M::val) { 573 | return false; 574 | } 575 | } 576 | return isRead(prop); 577 | } 578 | template<> 579 | static bool isRead(unsigned) { 580 | return true; 581 | } 582 | 583 | template 584 | static bool isWrite(unsigned prop) { 585 | // Записываемых больше, чем не записываемых 586 | if constexpr (!M::IsWrite) { 587 | if (prop == M::val) { 588 | return false; 589 | } 590 | } 591 | return isWrite(prop); 592 | } 593 | template<> 594 | static bool isWrite(unsigned) { 595 | return true; 596 | } 597 | }; 598 | 599 | template 600 | static bool readProp(const T* ptr, unsigned prop, tVariant& val) { 601 | if (M::IsRead && prop == M::val) 602 | return M::readProp(ptr, val); 603 | return readProp(ptr, prop, val); 604 | } 605 | template<> 606 | static bool readProp(const T*, unsigned, tVariant&) { 607 | return false; 608 | } 609 | 610 | template 611 | static bool writeProp(T* ptr, unsigned prop, const tVariant& val) { 612 | if (M::IsWrite && prop == M::val) 613 | return M::writeProp(ptr, val); 614 | return writeProp(ptr, prop, val); 615 | } 616 | template<> 617 | static bool writeProp(T*, unsigned, const tVariant&) { 618 | return false; 619 | } 620 | }; 621 | 622 | template 623 | class V8Addin : public AddinBase { 624 | public: 625 | using ImplType = Impl; 626 | ImplType& d() { 627 | return static_cast(*this); 628 | } 629 | const ImplType& d() const { 630 | return static_cast(*this); 631 | } 632 | bool init() { 633 | return true; 634 | } 635 | void done() {} 636 | 637 | template 638 | struct end_def { 639 | using prev = P; 640 | enum { val = prev::val + 1 }; 641 | }; 642 | // В конечном классе эти типы возможно будут переопределены, указывая на последний метод/свойство 643 | using endmeths = end_def; 644 | using endprops = end_def; 645 | 646 | struct init_all_map { 647 | init_all_map() { 648 | fndselector::type::OnInitFirstObject(); 649 | fndselector::type::OnInitFirstObject(); 650 | } 651 | }; 652 | 653 | V8Addin() { 654 | static const init_all_map iam; 655 | } 656 | 657 | bool ADDIN_API Init(void* pConnection) override { 658 | v8connection_ = reinterpret_cast(pConnection); 659 | return d().init(); 660 | } 661 | bool error(stru rus, stru eng) { 662 | return error(selectLocaleStr(rus, eng)); 663 | } 664 | bool error(auto&& err) { 665 | lastError_ = std::move(err); 666 | if (throwErrors_) { 667 | v8connection_->AddError(ADDIN_E_FAIL, ImplType::ExtensionName.symbols(), lastError_, 0); 668 | } 669 | return false; 670 | } 671 | 672 | void ADDIN_API Done() override { 673 | d().done(); 674 | } 675 | 676 | bool ADDIN_API RegisterExtensionAs(WCHAR_T** wsExtensionName) override { 677 | if (wsExtensionName) { 678 | *wsExtensionName = copyText(ImplType::ExtensionName); 679 | } 680 | return true; 681 | } 682 | 683 | long ADDIN_API GetNProps() override { 684 | return ImplType::endprops::val; 685 | } 686 | 687 | long ADDIN_API FindProp(const WCHAR_T* propName) override { 688 | return propName ? fndselector::type::find(stru{propName}) : -1; 689 | } 690 | 691 | const WCHAR_T* ADDIN_API GetPropName(long prop, long language) override { 692 | return getname::type::name(prop, language, this); 693 | } 694 | 695 | bool ADDIN_API GetPropVal(const long prop, tVariant* value) override { 696 | return value != nullptr && (unsigned)prop < ImplType::endprops::val && prop_pol::ReadProp(&d(), prop, *value); 697 | } 698 | 699 | bool ADDIN_API SetPropVal(const long prop, tVariant* value) override { 700 | return value != nullptr && (unsigned)prop < ImplType::endprops::val && prop_pol::WriteProp(&d(), prop, *value); 701 | } 702 | 703 | bool ADDIN_API IsPropReadable(const long prop) override { 704 | return (unsigned)prop < ImplType::endprops::val && prop_pol::IsPropReadable(prop); 705 | } 706 | 707 | bool ADDIN_API IsPropWritable(const long prop) override { 708 | return (unsigned)prop < ImplType::endprops::val && prop_pol::IsPropWritable(prop); 709 | } 710 | 711 | long ADDIN_API GetNMethods() override { 712 | return ImplType::endmeths::val; 713 | } 714 | 715 | long ADDIN_API FindMethod(const WCHAR_T* methodName) override { 716 | return methodName ? fndselector::type::find(stru{methodName}) : -1; 717 | } 718 | 719 | const WCHAR_T* ADDIN_API GetMethodName(const long method, const long language) override { 720 | return getname::type::name(method, language, this); 721 | } 722 | 723 | long ADDIN_API GetNParams(const long method) override { 724 | return (unsigned)method < ImplType::endmeths::val ? meth_pol::GetNParams(method) : -1; 725 | } 726 | 727 | bool ADDIN_API GetParamDefValue(const long method, const long param, tVariant* value) override { 728 | if (!value) 729 | return false; 730 | value->vt = VTYPE_EMPTY; 731 | return (unsigned)method >= ImplType::endmeths::val || meth_pol::GetDefVal(&d(), method, param, value); 732 | } 733 | 734 | bool ADDIN_API HasRetVal(const long method) override { 735 | return (unsigned)method < ImplType::endmeths::val && meth_pol::HasRetVal(method); 736 | } 737 | 738 | bool ADDIN_API CallAsProc(const long method, tVariant* params, const long count) override { 739 | return (unsigned)method < ImplType::endmeths::val && meth_pol::CallAsProc(&d(), method, params, count); 740 | } 741 | 742 | bool ADDIN_API CallAsFunc(const long method, tVariant* result, tVariant* params, const long count) override { 743 | return result != nullptr && meth_pol::CallAsFunc(&d(), method, *result, params, count); 744 | } 745 | }; 746 | 747 | #define CONCAT2(x, y) x##y 748 | #define CONCAT(x, y) CONCAT2(x, y) 749 | #define STRINGIZE(x) #x 750 | #define UEFY(x) CONCAT(u, STRINGIZE(x)) 751 | 752 | #define REGISTER_EXTENSION(eName) \ 753 | inline static constexpr ssu ExtensionName{eName}; \ 754 | static IComponentBase* create() { \ 755 | return new ImplType; \ 756 | } \ 757 | inline static AddinInfo registerAddin{eName, create}; \ 758 | template \ 759 | struct prev_type { \ 760 | using type = typename prev_type::type; \ 761 | }; \ 762 | template<> \ 763 | struct prev_type { \ 764 | using type = first_def; \ 765 | }; \ 766 | template<> \ 767 | struct prev_type { \ 768 | using type = first_def; \ 769 | }; 770 | 771 | #define END_EXTENSION() \ 772 | using endmeths = end_def::type>; \ 773 | using endprops = end_def::type> 774 | 775 | #define BEGIN_DECLARE(parNameE, parNameR, parMeth) \ 776 | struct parNameE##_def { \ 777 | using prev = prev_type::type; \ 778 | enum { val = prev::val + 1 }; \ 779 | inline static const stru engName{UEFY(parNameE)}; \ 780 | inline static const stru rusName{parNameR}; 781 | 782 | #define END_DECLARE(parNameE, parMeth) \ 783 | } \ 784 | ; \ 785 | friend struct parNameE##_def; \ 786 | template<> \ 787 | struct prev_type { \ 788 | using type = parNameE##_def; \ 789 | }; 790 | 791 | #define EXT_METHOD_DEF(parNameEng, parNameRus, parNumParam, parCallFunc, parCallProc, parDefProc, parIsFunc, parIsDef) \ 792 | BEGIN_DECLARE(parNameEng, parNameRus, true) \ 793 | static int callProc(ImplType* p, tVariant* pp, long s) { \ 794 | return parCallProc; \ 795 | } \ 796 | static int callFunc(ImplType* p, tVariant& r, tVariant* pp, long s) { \ 797 | return parCallFunc; \ 798 | } \ 799 | static int defVal(const ImplType* p, long n, tVariant* v) { \ 800 | return parDefProc; \ 801 | } \ 802 | enum { nParam = parNumParam, IsFunc = parIsFunc, IsDefVal = parIsDef }; \ 803 | static void test() { \ 804 | check_defval t{}; \ 805 | (void)t; \ 806 | test_last t2{}; \ 807 | (void)t2; \ 808 | } \ 809 | END_DECLARE(parNameEng, true) 810 | 811 | #define EXT_DEFVAL_FOR(parNameEng) bool parNameEng##_defVal(long param, tVariant* value) const 812 | 813 | #define EXT_PROC_(parNameEng, parNameRus, parNumParam, parDefVal, parIsDef) \ 814 | EXT_METHOD_DEF(parNameEng, parNameRus, parNumParam, false, (p->parNameEng(pp, s)), parDefVal, 0, parIsDef) \ 815 | bool parNameEng(tVariant* params, unsigned count) 816 | 817 | #define EXT_FUNC_(parNameEng, parNameRus, parNumParam, parDefVal, parIsDef) \ 818 | EXT_METHOD_DEF(parNameEng, parNameRus, parNumParam, (p->parNameEng(r, pp, s)), false, parDefVal, 1, parIsDef) \ 819 | bool parNameEng(tVariant& retVal, tVariant* params, unsigned count) 820 | 821 | // Объявить метод - процедуру контекста, без параметров по умолчанию 822 | #define EXT_PROC(parNameEng, parNameRus, parNumParam) EXT_PROC_(parNameEng, parNameRus, parNumParam, false, 0) 823 | 824 | // Объявить метод - процедуру контекста, имеющую параметры по умолчанию 825 | #define EXT_PROC_WITH_DEFVAL(parNameEng, parNameRus, parNumParam) \ 826 | EXT_PROC_(parNameEng, parNameRus, parNumParam, (p->parNameEng##_defVal(n, v)), 1) 827 | 828 | // Объявить метод - функцию контекста, без параметров по умолчанию 829 | #define EXT_FUNC(parNameEng, parNameRus, parNumParam) EXT_FUNC_(parNameEng, parNameRus, parNumParam, false, 0) 830 | 831 | // Объявить метод - функцию контекста, имеющую параметры по умолчанию 832 | #define EXT_FUNC_WITH_DEFVAL(parNameEng, parNameRus, parNumParam) \ 833 | EXT_FUNC_(parNameEng, parNameRus, parNumParam, (p->parNameEng##_defVal(n, v)), 1) 834 | 835 | #define EXT_PROP_(parNameE, parNameR, parGet, parSet, parR, parW) \ 836 | BEGIN_DECLARE(parNameE, parNameR, false) \ 837 | static bool readProp(const ImplType* p, tVariant& v) { \ 838 | return parGet; \ 839 | } \ 840 | static bool writeProp(ImplType* p, const tVariant& v) { \ 841 | return parSet; \ 842 | } \ 843 | enum { IsRead = parR, IsWrite = parW }; \ 844 | static void test() { \ 845 | test_last t2{}; \ 846 | (void)t2; \ 847 | } \ 848 | END_DECLARE(parNameE, false) 849 | 850 | #define EXT_PROP_READ(parName) bool parName(tVariant& value) const 851 | // Объявить обработчик записи свойства для свойства с чтением/записью 852 | #define EXT_PROP_WRITE(parName) bool set##parName(const tVariant& value) 853 | 854 | // Объявить свойство контекста, только для чтения 855 | #define EXT_PROP_RO(parNameEng, parNameRus) \ 856 | EXT_PROP_(parNameEng, parNameRus, (p->parNameEng(v)), false, 1, 0) \ 857 | EXT_PROP_READ(parNameEng) 858 | 859 | // Объявить свойство контекста, только для записи 860 | #define EXT_PROP_WO(parNameEng, parNameRus) \ 861 | EXT_PROP_(parNameEng, parNameRus, false, (p->set##parNameEng(v)), 0, 1) \ 862 | EXT_PROP_WRITE(parNameEng) 863 | 864 | // Объявить свойство контекста, чтение/запись 865 | #define EXT_PROP_RW(parNameEng, parNameRus) \ 866 | EXT_PROP_(parNameEng, parNameRus, (p->parNameEng(v)), (p->set##parNameEng(v)), 1, 1) \ 867 | EXT_PROP_READ(parNameEng) 868 | -------------------------------------------------------------------------------- /src/dllmain.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : Defines the entry point for the DLL application. 2 | #include "stdafx.h" 3 | 4 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID) { 5 | switch (ul_reason_for_call) { 6 | case DLL_PROCESS_ATTACH: 7 | DisableThreadLibraryCalls(hModule); 8 | break; 9 | case DLL_THREAD_ATTACH: 10 | case DLL_THREAD_DETACH: 11 | case DLL_PROCESS_DETACH: break; 12 | } 13 | return TRUE; 14 | } 15 | -------------------------------------------------------------------------------- /src/sqlite.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "sqlite.h" 3 | 4 | extern "C" int sqlite3_unicode_load(); 5 | 6 | bool SqliteBase::open(stru name) { 7 | static struct SqliteUnicodeInit { 8 | SqliteUnicodeInit() { 9 | sqlite3_unicode_load(); 10 | } 11 | } unicodeInit; 12 | 13 | if (db_) { 14 | close(); 15 | } 16 | return opened_ = SQLITE_OK == sqlite3_open16(name, &db_); 17 | } 18 | 19 | void SqliteBase::close() { 20 | if (db_) { 21 | sqlite3_close_v2(db_); 22 | db_ = nullptr; 23 | opened_ = false; 24 | } 25 | } 26 | 27 | int SqliteBase::exec(stru query) { 28 | return opened_ ? sqlite3_exec(db_, lstringa<4096>{query}, nullptr, nullptr, nullptr) : SQLITE_ERROR; 29 | } 30 | 31 | SqliteQuery SqliteBase::prepare(stru query) { 32 | sqlite3_stmt* stmt = nullptr; 33 | if (opened_) { 34 | sqlite3_prepare16_v3(db_, (void*)query.str, (int)query.length() * 2, 0, &stmt, nullptr); 35 | } 36 | return SqliteQuery(stmt); 37 | } 38 | 39 | double calcJulianDate(tm& dt) { 40 | int Y = dt.tm_year + 1900, M = dt.tm_mon + 1, D = dt.tm_mday, A, B, X1, X2; 41 | 42 | if (M <= 2) { 43 | Y--; 44 | M += 12; 45 | } 46 | 47 | A = Y / 100; 48 | B = 2 - A + (A / 4); 49 | X1 = 36525 * (Y + 4716) / 100; 50 | X2 = 306001 * (M + 1) / 10000; 51 | 52 | double result = X1 + X2 + D + B + (dt.tm_hour * 3600000 + dt.tm_min * 60000 + dt.tm_sec * 1000) / 86400000.0 - 1524.5; 53 | return result; 54 | } 55 | 56 | double calcJulianDate(double winDate) { 57 | return winDate + 2415018.5; 58 | } 59 | 60 | static void computeYMD(double jd, tm& p) { 61 | int64_t iJD = int64_t(jd * 86400000); 62 | int Z, A, B, C, D, E, X1; 63 | Z = (int)((iJD + 43200000) / 86400000); 64 | A = (int)((Z - 1867216.25) / 36524.25); 65 | A = Z + 1 + A - (A / 4); 66 | B = A + 1524; 67 | C = (int)((B - 122.1) / 365.25); 68 | D = (36525 * (C & 32767)) / 100; 69 | E = (int)((B - D) / 30.6001); 70 | X1 = (int)(30.6001 * E); 71 | p.tm_mday = B - D - X1; 72 | p.tm_mon = (E < 14 ? E - 1 : E - 13) - 1; 73 | p.tm_year = (p.tm_mon > 2 ? C - 4716 : C - 4715) - 1900; 74 | jd += 0.5; 75 | int s = int((jd - (int)jd) * 86400.0); 76 | p.tm_hour = s / 3600; 77 | s = s % 3600; 78 | p.tm_min = s / 60; 79 | p.tm_sec = s % 60; 80 | } 81 | 82 | tm winDateToTm(double winDate) { 83 | tm t; 84 | computeYMD(calcJulianDate(winDate), t); 85 | return t; 86 | } 87 | 88 | expr_json_str::expr_json_str(ssu t) : text(t) { 89 | const u16s* ptr = text.symbols(); 90 | size_t add = 0; 91 | 92 | for (size_t i = 0; i < text.length(); i++) { 93 | switch (*ptr++) { 94 | case '\b': 95 | case '\f': 96 | case '\r': 97 | case '\n': 98 | case '\t': 99 | case '\"': 100 | case '\\': 101 | add++; 102 | } 103 | } 104 | l = text.len + add; 105 | } 106 | 107 | simstr::u16s* expr_json_str::place(u16s* ptr) const noexcept { 108 | const u16s *r = text.symbols(); 109 | size_t lenOfText = text.length(), lenOfTail = l; 110 | while (lenOfTail > lenOfText) { 111 | u16s s = *r++; 112 | switch (s) { 113 | case '\b': 114 | *ptr++ = '\\'; 115 | *ptr++ = 'b'; 116 | lenOfTail--; 117 | break; 118 | case '\f': 119 | *ptr++ = '\\'; 120 | *ptr++ = 'f'; 121 | lenOfTail--; 122 | break; 123 | case '\r': 124 | *ptr++ = '\\'; 125 | *ptr++ = 'r'; 126 | lenOfTail--; 127 | break; 128 | case '\n': 129 | *ptr++ = '\\'; 130 | *ptr++ = 'n'; 131 | lenOfTail--; 132 | break; 133 | case '\t': 134 | *ptr++ = '\\'; 135 | *ptr++ = 't'; 136 | lenOfTail--; 137 | break; 138 | case '\"': 139 | *ptr++ = '\\'; 140 | *ptr++ = '\"'; 141 | lenOfTail--; 142 | break; 143 | case '\\': 144 | *ptr++ = '\\'; 145 | *ptr++ = '\\'; 146 | lenOfTail--; 147 | break; 148 | default: 149 | *ptr++ = s; 150 | break; 151 | } 152 | lenOfTail--; 153 | lenOfText--; 154 | } 155 | if (lenOfTail) { 156 | std::char_traits::copy(ptr, r, lenOfTail); 157 | ptr += lenOfTail; 158 | } 159 | return ptr; 160 | } 161 | 162 | expr_str_base64::expr_str_base64(ssa t) : text(t) { 163 | l = (text.len + 2) / 3 * 4; 164 | } 165 | 166 | u16s* expr_str_base64::place(u16s* ptr) const noexcept { 167 | static constexpr u8s alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 168 | 169 | const unsigned char* t = (const unsigned char*)text.str; 170 | 171 | size_t i = 0; 172 | if (text.len > 2) { 173 | for (; i < text.len - 2; i += 3) { 174 | *ptr++ = alphabet[(t[i] >> 2) & 0x3F]; 175 | *ptr++ = alphabet[((t[i] & 0x3) << 4) | ((int)(t[i + 1] & 0xF0) >> 4)]; 176 | *ptr++ = alphabet[((t[i + 1] & 0xF) << 2) | ((int)(t[i + 2] & 0xC0) >> 6)]; 177 | *ptr++ = alphabet[t[i + 2] & 0x3F]; 178 | } 179 | } 180 | 181 | if (i < text.len) { 182 | *ptr++ = alphabet[(t[i] >> 2) & 0x3F]; 183 | if (i == (text.len - 1)) { 184 | *ptr++ = alphabet[((t[i] & 0x3) << 4)]; 185 | *ptr++ = '='; 186 | } else { 187 | *ptr++ = alphabet[((t[i] & 0x3) << 4) | ((int)(t[i + 1] & 0xF0) >> 4)]; 188 | *ptr++ = alphabet[((t[i + 1] & 0xF) << 2)]; 189 | } 190 | *ptr++ = '='; 191 | } 192 | return ptr; 193 | } 194 | 195 | simstr::u16s* expr_str_tm::place(u16s* ptr) const noexcept { 196 | #ifndef __linux__ 197 | std::swprintf(from_w(ptr), 20, L"%04i-%02i-%02i %02i:%02i:%02i", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); 198 | #else 199 | char buf[20]; 200 | std::snprintf(buf, 20, "%04i-%02i-%02i %02i:%02i:%02i", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); 201 | for (unsigned i = 0; i < 19; i++) { 202 | ptr[i] = buf[i]; 203 | } 204 | #endif 205 | return ptr + 19; 206 | } 207 | -------------------------------------------------------------------------------- /src/sqlite.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "simstr/sstring.h" 3 | #include "sqlite3.h" 4 | #include 5 | #include 6 | using namespace simstr; 7 | 8 | class SqliteQuery; 9 | 10 | template 11 | concept QueryResultReceiver = requires(T& t) { 12 | {t.setColumnCount(0u)}; 13 | {t.addColumnName(ssu{})}; 14 | {t.addRow()}; 15 | {t.addNull()}; 16 | {t.addInteger(int64_t{})}; 17 | {t.addReal(double{})}; 18 | {t.addText(ssu{})}; 19 | {t.addBlob(ssa{})}; 20 | {t.setResult(int{}, (sqlite3*)nullptr)}; 21 | }; 22 | 23 | class SqliteBase { 24 | public: 25 | SqliteBase() = default; 26 | ~SqliteBase() { 27 | close(); 28 | } 29 | SqliteBase(const SqliteBase&) = delete; 30 | SqliteBase(SqliteBase&& other) noexcept : db_(other.db_), opened_(other.opened_) { 31 | other.db_ = nullptr; 32 | other.opened_ = false; 33 | } 34 | SqliteBase& operator=(SqliteBase other) noexcept { 35 | std::swap(db_, other.db_); 36 | std::swap(opened_, other.opened_); 37 | return *this; 38 | } 39 | 40 | bool isOpen() const { 41 | return opened_; 42 | } 43 | operator sqlite3*() const { 44 | return db_; 45 | } 46 | 47 | bool open(stru name); 48 | void close(); 49 | int exec(stru query); 50 | stru lastError() const { 51 | return stru{ db_ ? (const u16s*)sqlite3_errmsg16(db_) : nullptr}; 52 | } 53 | int64_t lastId() const { 54 | return opened_ ? sqlite3_last_insert_rowid(db_) : 0; 55 | } 56 | int64_t changes() const { 57 | return opened_ ? sqlite3_changes64(db_) : 0; 58 | } 59 | SqliteQuery prepare(stru query); 60 | 61 | protected: 62 | sqlite3* db_{}; 63 | bool opened_{}; 64 | }; 65 | 66 | template 67 | struct db_traits; 68 | 69 | // Спецификации для установки параметров разными типами данных 70 | template 71 | struct db_int_traits { 72 | static void bind(sqlite3_stmt* stmt, unsigned idx, const T& value) { 73 | sqlite3_bind_int(stmt, idx, static_cast(value)); 74 | } 75 | }; 76 | 77 | template 78 | struct db_int64_traits { 79 | static void bind(sqlite3_stmt* stmt, unsigned idx, const T& value) { 80 | sqlite3_bind_int64(stmt, idx, static_cast(value)); 81 | } 82 | }; 83 | 84 | template 85 | struct db_double_traits { 86 | static void bind(sqlite3_stmt* stmt, unsigned idx, const T& value) { 87 | sqlite3_bind_double(stmt, idx, value); 88 | } 89 | }; 90 | 91 | template<> 92 | struct db_traits : db_int_traits {}; 93 | template<> 94 | struct db_traits : db_int_traits {}; 95 | template<> 96 | struct db_traits : db_int_traits {}; 97 | template<> 98 | struct db_traits : db_int_traits {}; 99 | template<> 100 | struct db_traits : db_int_traits {}; 101 | template<> 102 | struct db_traits : db_int_traits {}; 103 | template<> 104 | struct db_traits : std::conditional_t, db_int64_traits> {}; 105 | template<> 106 | struct db_traits : std::conditional_t, db_int64_traits> {}; 107 | template<> 108 | struct db_traits : db_int64_traits {}; 109 | template<> 110 | struct db_traits : db_int64_traits {}; 111 | template<> 112 | struct db_traits : db_double_traits {}; 113 | template<> 114 | struct db_traits : db_double_traits {}; 115 | template<> 116 | struct db_traits { 117 | static void bind(sqlite3_stmt* stmt, unsigned idx, ssa value) { 118 | sqlite3_bind_blob(stmt, idx, value.str, (int)value.len, SQLITE_TRANSIENT); 119 | } 120 | }; 121 | template<> 122 | struct db_traits { 123 | static void bind(sqlite3_stmt* stmt, unsigned idx, ssu value) { 124 | sqlite3_bind_text16(stmt, idx, value.str, (int)value.len * 2, SQLITE_TRANSIENT); 125 | } 126 | }; 127 | 128 | template 129 | struct db_traits { 130 | static void bind(sqlite3_stmt* stmt, unsigned idx, const u8s (&value)[N]) { 131 | sqlite3_bind_blob(stmt, idx, value, N - 1, 0); 132 | } 133 | }; 134 | 135 | template 136 | struct db_traits { 137 | static void bind(sqlite3_stmt* stmt, unsigned idx, const u16s (&value)[N]) { 138 | sqlite3_bind_text16(stmt, idx, value, (N - 1) * 2, 0); 139 | } 140 | }; 141 | 142 | struct db_null {}; 143 | template<> 144 | struct db_traits { 145 | static void bind(sqlite3_stmt* stmt, unsigned idx, const db_null&) { 146 | sqlite3_bind_null(stmt, idx); 147 | } 148 | }; 149 | 150 | 151 | class SqliteQuery { 152 | public: 153 | SqliteQuery() = default; 154 | SqliteQuery(sqlite3_stmt* stmt) : stmt_(stmt) {} 155 | 156 | ~SqliteQuery() { 157 | close(); 158 | } 159 | 160 | SqliteQuery(const SqliteQuery&) = delete; 161 | 162 | SqliteQuery(SqliteQuery&& other) noexcept : stmt_(other.stmt_) { 163 | other.stmt_ = nullptr; 164 | } 165 | SqliteQuery& operator=(SqliteQuery other) noexcept { 166 | std::swap(stmt_, other.stmt_); 167 | return *this; 168 | } 169 | operator sqlite3_stmt*() const { 170 | return stmt_; 171 | } 172 | 173 | bool valid() const { 174 | return stmt_ != nullptr; 175 | } 176 | 177 | void close() { 178 | if (stmt_) { 179 | sqlite3_finalize(stmt_); 180 | stmt_ = nullptr; 181 | } 182 | } 183 | 184 | template 185 | SqliteQuery& bind(int idx, const T& t) { 186 | db_traits::bind(stmt_, idx, t); 187 | return *this; 188 | } 189 | template 190 | SqliteQuery& bind(stra name, const T& t) { 191 | db_traits::bind(stmt_, sqlite3_bind_parameter_index(stmt_, name), t); 192 | return *this; 193 | } 194 | 195 | template 196 | void exec(Q& receiver) { 197 | if (!stmt_) { 198 | receiver.setResult(SQLITE_ERROR, nullptr); 199 | return; 200 | } 201 | unsigned colCount = sqlite3_column_count(stmt_); 202 | int result; 203 | if (colCount) { 204 | receiver.setColumnCount(colCount); 205 | for (unsigned i = 0; i < colCount; i++) { 206 | receiver.addColumnName(stru{(const u16s*)sqlite3_column_name16(stmt_, i)}); 207 | } 208 | for (;;) { 209 | result = sqlite3_step(stmt_); 210 | if (SQLITE_ROW == result) { 211 | receiver.addRow(); 212 | for (unsigned col = 0; col < colCount; col++) { 213 | switch (sqlite3_column_type(stmt_, col)) { 214 | case SQLITE_NULL: 215 | receiver.addNull(); 216 | break; 217 | case SQLITE_INTEGER: 218 | receiver.addInteger(sqlite3_column_int64(stmt_, col)); 219 | break; 220 | case SQLITE_FLOAT: 221 | receiver.addReal(sqlite3_column_double(stmt_, col)); 222 | break; 223 | case SQLITE_TEXT: 224 | receiver.addText(ssu{(const u16s*)sqlite3_column_text16(stmt_, col), size_t(sqlite3_column_bytes16(stmt_, col) / 2)}); 225 | break; 226 | case SQLITE_BLOB: 227 | receiver.addBlob(ssa{(const u8s*)sqlite3_column_blob(stmt_, col), size_t(sqlite3_column_bytes(stmt_, col))}); 228 | break; 229 | } 230 | } 231 | } else { 232 | break; 233 | } 234 | } 235 | } else { 236 | result = sqlite3_step(stmt_); 237 | } 238 | sqlite3_reset(stmt_); 239 | receiver.setResult(result, sqlite3_db_handle(stmt_)); 240 | } 241 | template 242 | Q exec(Args&&... args) { 243 | Q receiver(std::forward(args)...); 244 | exec(receiver); 245 | return receiver; 246 | } 247 | 248 | protected: 249 | sqlite3_stmt* stmt_{}; 250 | }; 251 | 252 | double calcJulianDate(tm& dt); 253 | double calcJulianDate(double winDate); 254 | tm winDateToTm(double winDate); 255 | 256 | struct expr_json_str { 257 | using symb_type = u16s; 258 | ssu text; 259 | size_t l; 260 | size_t length() const noexcept { 261 | return l; 262 | } 263 | u16s* place(u16s* ptr) const noexcept; 264 | expr_json_str(ssu t); 265 | }; 266 | 267 | struct expr_str_base64 { 268 | using symb_type = u16s; 269 | ssa text; 270 | size_t l; 271 | size_t length() const noexcept { 272 | return l; 273 | } 274 | u16s* place(u16s* ptr) const noexcept; 275 | expr_str_base64(ssa t); 276 | }; 277 | 278 | struct expr_str_tm { 279 | using symb_type = u16s; 280 | const tm& t; 281 | size_t length() const noexcept { 282 | return 19; 283 | } 284 | u16s* place(u16s* ptr) const noexcept; 285 | expr_str_tm(const tm& _t) : t(_t) {} 286 | }; 287 | -------------------------------------------------------------------------------- /src/sqlite3_unicode.c: -------------------------------------------------------------------------------- 1 | #include "simstr/simple_unicode.h" 2 | /************************************************************************************************* 3 | ** The author disclaims copyright to this source code. In place of a legal notice, here is a blessing: 4 | ** 5 | ** May you do good and not evil. 6 | ** May you find forgiveness for yourself and forgive others. 7 | ** May you share freely, never taking more than you give. 8 | ** 9 | ************************************************************************************************** 10 | ** 11 | ** This file implements true UNICODE functionality for SQLite in regards of case-insensitive comparison 12 | ** of unicode data and SQLite. It uses UNICODE mapping tables to provide the following to SQLite: 13 | ** 14 | ** * An implementation of SQL scalar upper(), lower(), title(), fold() functions to normalize strings 15 | ** for comparison by case folding. 16 | ** 17 | ** * An implementation of SQL scalar unaccent() function to normalize strings for comparison by removing accents. 18 | ** 19 | ** * An implementation of the LIKE operator that uses casefolding to provide case-independent matching. 20 | ** 21 | ** Compile the project with the following preprocessor definitions in order to enable the code below. 22 | ** * SQLITE_ENABLE_UNICODE and SQLITE_CORE for static library, (building sqlite3.a | lib) 23 | ** * SQLITE_ENABLE_UNICODE for dynamic library, (building sqlite3.so | dll) 24 | ** 25 | ** 26 | ** The following function needs to be called at program startup to initialise unicode functionality 27 | ** * sqlite3_unicode_load(); 28 | ** 29 | ** The following function needs to be called before program exit to correctly uninitialise unicode functionality 30 | ** * sqlite3_unicode_free(); 31 | */ 32 | #ifdef _MSC_VER 33 | #pragma warning(disable:4305 13 90) 34 | #endif 35 | 36 | /* 37 | ** Un|Comment to provide additional unicode support to SQLite3 or adjust size for unused features 38 | */ 39 | #define SQLITE3_UNICODE_FOLD // ~ 10KB increase 40 | #define SQLITE3_UNICODE_LOWER // ~ 10KB increase 41 | #define SQLITE3_UNICODE_UPPER // ~ 10KB increase 42 | #define SQLITE3_UNICODE_TITLE // ~ 10KB increase 43 | #define SQLITE3_UNICODE_UNACC // ~ 30KB increase 44 | // _______________ 45 | // ~ 70KB increase 46 | 47 | /* 48 | ** SQLITE3_UNICODE_COLLATE will register and use the custom nocase collation instead of the standard 49 | ** one, which supports case folding and unaccenting. 50 | */ 51 | #define SQLITE3_UNICODE_COLLATE // requires SQLITE3_UNICODE_FOLD to be defined as well. 52 | 53 | /* 54 | ** SQLITE3_UNICODE_UNACC_AUTOMATIC will automatically try to unaccent any characters that 55 | ** are over the 0x80 character in the LIKE comparison operation and in the NOCASE collation sequence. 56 | */ 57 | #define SQLITE3_UNICODE_UNACC_AUTOMATIC // requires SQLITE3_UNICODE_UNACC to be defined as well. 58 | 59 | /************************************************************************************************* 60 | ** DO NOT MODIFY BELOW THIS LINE 61 | **************************************************************************************************/ 62 | 63 | /* Generated by builder. Do not modify. Start unicode_version_defines */ 64 | /* 65 | File was generated by : sqlite3_unicode.in 66 | File was generated on : Fri Jun 5 01:10:23 2009 67 | Using unicode data db : UnicodeData.txt 68 | Using unicode fold db : CaseFolding.txt 69 | */ 70 | #define SQLITE3_UNICODE_VERSION_MAJOR 5 71 | #define SQLITE3_UNICODE_VERSION_MINOR 1 72 | #define SQLITE3_UNICODE_VERSION_MICRO 0 73 | #define SQLITE3_UNICODE_VERSION_BUILD 12 74 | 75 | #define __SQLITE3_UNICODE_VERSION_STRING(a,b,c,d) #a "." #b "." #c "." #d 76 | #define _SQLITE3_UNICODE_VERSION_STRING(a,b,c,d) __SQLITE3_UNICODE_VERSION_STRING(a,b,c,d) 77 | #define SQLITE3_UNICODE_VERSION_STRING _SQLITE3_UNICODE_VERSION_STRING(SQLITE3_UNICODE_VERSION_MAJOR,SQLITE3_UNICODE_VERSION_MINOR,SQLITE3_UNICODE_VERSION_MICRO,SQLITE3_UNICODE_VERSION_BUILD) 78 | 79 | /* Generated by builder. Do not modify. End unicode_version_defines */ 80 | 81 | #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_UNICODE) 82 | 83 | #ifndef SQLITE_CORE 84 | #include 85 | SQLITE_EXTENSION_INIT1 86 | #else 87 | #include 88 | #endif 89 | 90 | #include 91 | #include 92 | 93 | #ifndef _SQLITE3_UNICODE_H 94 | #define _SQLITE3_UNICODE_H 95 | 96 | #ifdef __cplusplus 97 | extern "C" { 98 | #endif 99 | 100 | /* 101 | ** Add the ability to override 'extern' 102 | */ 103 | /* 104 | ** 105 | ** The define of SQLITE_EXPORT is necessary to add the ability of exporting 106 | ** functions for both Microsoft Windows and Linux systems without the need 107 | ** of a .def file containing the names of the functions being exported. 108 | */ 109 | #ifndef SQLITE_EXPORT 110 | # if ((defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)) && (!defined(SQLITE_CORE))) 111 | # define SQLITE_EXPORT __declspec(dllexport) 112 | # else 113 | # define SQLITE_EXPORT SQLITE_EXTERN 114 | # endif 115 | #endif 116 | 117 | #ifndef SQLITE_PRIVATE 118 | # define SQLITE_PRIVATE static 119 | #endif 120 | #ifndef SQLITE_API 121 | # define SQLITE_API 122 | #endif 123 | 124 | /* 125 | ** Integers of known sizes. These typedefs might change for architectures 126 | ** where the sizes very. Preprocessor macros are available so that the 127 | ** types can be conveniently redefined at compile-type. Like this: 128 | ** 129 | ** cc '-DUINTPTR_TYPE=long long int' ... 130 | */ 131 | #ifndef UINT32_TYPE 132 | # ifdef HAVE_UINT32_T 133 | # define UINT32_TYPE uint32_t 134 | # else 135 | # define UINT32_TYPE unsigned int 136 | # endif 137 | #endif 138 | #ifndef UINT16_TYPE 139 | # ifdef HAVE_UINT16_T 140 | # define UINT16_TYPE uint16_t 141 | # else 142 | # define UINT16_TYPE unsigned short int 143 | # endif 144 | #endif 145 | #ifndef INT16_TYPE 146 | # ifdef HAVE_INT16_T 147 | # define INT16_TYPE int16_t 148 | # else 149 | # define INT16_TYPE short int 150 | # endif 151 | #endif 152 | #ifndef UINT8_TYPE 153 | # ifdef HAVE_UINT8_T 154 | # define UINT8_TYPE uint8_t 155 | # else 156 | # define UINT8_TYPE unsigned char 157 | # endif 158 | #endif 159 | #ifndef INT8_TYPE 160 | # ifdef HAVE_INT8_T 161 | # define INT8_TYPE int8_t 162 | # else 163 | # define INT8_TYPE signed char 164 | # endif 165 | #endif 166 | #ifndef LONGDOUBLE_TYPE 167 | # define LONGDOUBLE_TYPE long double 168 | #endif 169 | typedef sqlite_int64 i64; /* 8-byte signed integer */ 170 | typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ 171 | typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ 172 | typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ 173 | typedef INT16_TYPE i16; /* 2-byte signed integer */ 174 | typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ 175 | typedef INT8_TYPE i8; /* 1-byte signed integer */ 176 | 177 | /* 178 | ** Another built-in collating sequence: NOCASE. 179 | ** 180 | ** This collating sequence is intended to be used for "case independant 181 | ** comparison". SQLite's knowledge of upper and lower case equivalents 182 | ** extends only to the 26 characters used in the English language. 183 | ** 184 | ** At the moment there is only a UTF-8 implementation. 185 | */ 186 | /* 187 | ** 188 | ** The built-in collating sequence: NOCASE is extended to accomodate the 189 | ** unicode case folding mapping tables to normalize characters to their 190 | ** fold equivalents and test them for equality. 191 | ** 192 | ** Both UTF-8 and UTF-16 implementations are supported. 193 | ** 194 | ** (void *)encoding takes the following values 195 | ** * SQLITE_UTF8 for UTF-8 encoded string comparison 196 | ** * SQLITE_UFT16 for UTF-16 encoded string comparison 197 | */ 198 | SQLITE_EXPORT int sqlite3_unicode_collate( 199 | void *encoding, 200 | int nKey1, const void *pKey1, 201 | int nKey2, const void *pKey2 202 | ); 203 | 204 | /* 205 | ** 206 | ** Register the UNICODE extension functions with database db. 207 | */ 208 | SQLITE_EXPORT int sqlite3_unicode_init(sqlite3 *db); 209 | 210 | /* 211 | ** 212 | ** The following function needs to be called at application startup to load the extension. 213 | */ 214 | SQLITE_EXPORT int sqlite3_unicode_load(); 215 | 216 | /* 217 | ** 218 | ** The following function needs to be called before application exit to unload the extension. 219 | */ 220 | SQLITE_EXPORT void sqlite3_unicode_free(); 221 | 222 | #ifdef __cplusplus 223 | } 224 | #endif 225 | 226 | #endif /* _SQLITE3_UNICODE_H */ 227 | 228 | /* 229 | ** Check to see if this machine uses EBCDIC. (Yes, believe it or 230 | ** not, there are still machines out there that use EBCDIC.) 231 | */ 232 | #if 'A' == '\301' 233 | # define SQLITE_EBCDIC 1 234 | #else 235 | # define SQLITE_ASCII 1 236 | #endif 237 | 238 | /* 239 | ** Assuming zIn points to the first byte of a UTF-8 character, 240 | ** advance zIn to point to the first byte of the next UTF-8 character. 241 | */ 242 | #define SQLITE_SKIP_UTF8(zIn) { \ 243 | if( (*(zIn++))>=0xc0 ){ \ 244 | while( (*zIn & 0xc0)==0x80 ){ zIn++; } \ 245 | } \ 246 | } 247 | 248 | /* 249 | ** pZ is a UTF-8 encoded unicode string. If nByte is less than zero, 250 | ** return the number of unicode characters in pZ up to (but not including) 251 | ** the first 0x00 byte. If nByte is not less than zero, return the 252 | ** number of unicode characters in the first nByte of pZ (or up to 253 | ** the first 0x00, whichever comes first). 254 | */ 255 | SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *zIn, int nByte){ 256 | int r = 0; 257 | const u8 *z = (const u8*)zIn; 258 | const u8 *zTerm; 259 | if( nByte>=0 ){ 260 | zTerm = &z[nByte]; 261 | }else{ 262 | zTerm = (const u8*)(-1); 263 | } 264 | assert( z<=zTerm ); 265 | while( *z!=0 && z=0xc0 ){ \ 317 | c = sqlite3Utf8Trans1[c-0xc0]; \ 318 | while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ 319 | c = (c<<6) + (0x3f & *(zIn++)); \ 320 | } \ 321 | if( c<0x80 \ 322 | || (c&0xFFFFF800)==0xD800 \ 323 | || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ 324 | } 325 | SQLITE_PRIVATE int sqlite3Utf8Read( 326 | const unsigned char *z, /* First byte of UTF-8 character */ 327 | const unsigned char *zTerm, /* Pretend this byte is 0x00 */ 328 | const unsigned char **pzNext /* Write first byte past UTF-8 char here */ 329 | ){ 330 | int c; 331 | READ_UTF8(z, zTerm, c); 332 | *pzNext = z; 333 | return c; 334 | } 335 | 336 | /* An array to map all upper-case characters into their corresponding 337 | ** lower-case character. 338 | ** 339 | ** SQLite only considers US-ASCII (or EBCDIC) characters. We do not 340 | ** handle case conversions for the UTF character set since the tables 341 | ** involved are nearly as big or bigger than SQLite itself. 342 | */ 343 | #ifndef SQLITE3_UNICODE_FOLD 344 | SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = { 345 | #ifdef SQLITE_ASCII 346 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 347 | 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 348 | 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 349 | 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103, 350 | 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121, 351 | 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107, 352 | 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125, 353 | 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, 354 | 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161, 355 | 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179, 356 | 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197, 357 | 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215, 358 | 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233, 359 | 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251, 360 | 252,253,254,255 361 | #endif 362 | #ifdef SQLITE_EBCDIC 363 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 0x */ 364 | 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* 1x */ 365 | 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, /* 2x */ 366 | 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */ 367 | 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */ 368 | 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */ 369 | 96, 97, 66, 67, 68, 69, 70, 71, 72, 73,106,107,108,109,110,111, /* 6x */ 370 | 112, 81, 82, 83, 84, 85, 86, 87, 88, 89,122,123,124,125,126,127, /* 7x */ 371 | 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */ 372 | 144,145,146,147,148,149,150,151,152,153,154,155,156,157,156,159, /* 9x */ 373 | 160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */ 374 | 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */ 375 | 192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */ 376 | 208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */ 377 | 224,225,162,163,164,165,166,167,168,169,232,203,204,205,206,207, /* Ex */ 378 | 239,240,241,242,243,244,245,246,247,248,249,219,220,221,222,255, /* Fx */ 379 | #endif 380 | }; 381 | #endif 382 | 383 | /* 384 | ** For LIKE and GLOB matching on EBCDIC machines, assume that every 385 | ** character is exactly one byte in size. Also, all characters are 386 | ** able to participate in upper-case-to-lower-case mappings in EBCDIC 387 | ** whereas only characters less than 0x80 do in ASCII. 388 | */ 389 | /* 390 | ** 391 | ** The buit-in function has been extended to accomodate UTF-8 and UTF-16 392 | ** unicode strings containing characters over the 0x80 character limit as 393 | ** per the ASCII encoding imposed by SQlite. 394 | ** 395 | ** The functions below will use the sqlite3_unicode_fold() when 396 | ** SQLITE3_UNICODE_FOLD is defined and additonally sqlite_unicode_unacc() 397 | ** when SQLITE3_UNICODE_UNACC_AUTOMATIC is defined to normilize 398 | ** UTF-8 and UTF-16 encoded strings. 399 | */ 400 | #if defined(SQLITE_EBCDIC) 401 | # define sqlite3Utf8Read(A,B,C) (*(A++)) 402 | # define GlogUpperToLower(A) A = sqlite3UpperToLower[A] 403 | #else 404 | # if defined(SQLITE3_UNICODE_UNACC) && defined(SQLITE3_UNICODE_UNACC_AUTOMATIC) && defined(SQLITE3_UNICODE_FOLD) 405 | #define GlogUpperToLower(A) A = simpleUnicodeFold(simpleUnicodeUnacc(A, 0, 0)) 406 | # elif defined(SQLITE3_UNICODE_FOLD) 407 | #define GlogUpperToLower(A) A = simpleUnicodeFold(A) 408 | # else 409 | # define GlogUpperToLower(A) if( A<0x80 ){ A = sqlite3UpperToLower[A]; } 410 | # endif 411 | #endif 412 | 413 | /* 414 | ** Maximum length (in bytes) of the pattern in a LIKE or GLOB 415 | ** operator. 416 | */ 417 | #ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH 418 | # define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000 419 | #endif 420 | 421 | /* 422 | ** A structure defining how to do GLOB-style comparisons. 423 | */ 424 | struct compareInfo { 425 | u8 matchAll; 426 | u8 matchOne; 427 | u8 matchSet; 428 | u8 noCase; 429 | }; 430 | 431 | /* The correct SQL-92 behavior is for the LIKE operator to ignore 432 | ** case. Thus 'a' LIKE 'A' would be true. */ 433 | static const struct compareInfo likeInfoNorm = { '%', '_', 0, 1 }; 434 | 435 | /* 436 | ** Compare two UTF-8 strings for equality where the first string can 437 | ** potentially be a "glob" expression. Return true (1) if they 438 | ** are the same and false (0) if they are different. 439 | ** 440 | ** Globbing rules: 441 | ** 442 | ** '*' Matches any sequence of zero or more characters. 443 | ** 444 | ** '?' Matches exactly one character. 445 | ** 446 | ** [...] Matches one character from the enclosed list of 447 | ** characters. 448 | ** 449 | ** [^...] Matches one character not in the enclosed list. 450 | ** 451 | ** With the [...] and [^...] matching, a ']' character can be included 452 | ** in the list by making it the first character after '[' or '^'. A 453 | ** range of characters can be specified using '-'. Example: 454 | ** "[a-z]" matches any single lower-case letter. To match a '-', make 455 | ** it the last character in the list. 456 | ** 457 | ** This routine is usually quick, but can be N**2 in the worst case. 458 | ** 459 | ** Hints: to match '*' or '?', put them in "[]". Like this: 460 | ** 461 | ** abc[*]xyz Matches "abc*xyz" only 462 | */ 463 | static int patternCompare( 464 | const u8 *zPattern, /* The glob pattern */ 465 | const u8 *zString, /* The string to compare against the glob */ 466 | const struct compareInfo *pInfo, /* Information about how to do the compare */ 467 | const int esc /* The escape character */ 468 | ){ 469 | int c, c2; 470 | int invert; 471 | int seen; 472 | u8 matchOne = pInfo->matchOne; 473 | u8 matchAll = pInfo->matchAll; 474 | u8 matchSet = pInfo->matchSet; 475 | u8 noCase = pInfo->noCase; 476 | int prevEscape = 0; /* True if the previous character was 'escape' */ 477 | 478 | while( (c = sqlite3Utf8Read(zPattern,0,&zPattern))!=0 ){ 479 | if( !prevEscape && c==matchAll ){ 480 | while( (c=sqlite3Utf8Read(zPattern,0,&zPattern)) == matchAll 481 | || c == matchOne ){ 482 | if( c==matchOne && sqlite3Utf8Read(zString, 0, &zString)==0 ){ 483 | return 0; 484 | } 485 | } 486 | if( c==0 ){ 487 | return 1; 488 | }else if( c==esc ){ 489 | c = sqlite3Utf8Read(zPattern, 0, &zPattern); 490 | if( c==0 ){ 491 | return 0; 492 | } 493 | }else if( c==matchSet ){ 494 | assert( esc==0 ); /* This is GLOB, not LIKE */ 495 | assert( matchSet<0x80 ); /* '[' is a single-byte character */ 496 | while( *zString && patternCompare(&zPattern[-1],zString,pInfo,esc)==0 ){ 497 | SQLITE_SKIP_UTF8(zString); 498 | } 499 | return *zString!=0; 500 | } 501 | while( (c2 = sqlite3Utf8Read(zString,0,&zString))!=0 ){ 502 | if( noCase ){ 503 | GlogUpperToLower(c2); 504 | GlogUpperToLower(c); 505 | while( c2 != 0 && c2 != c ){ 506 | c2 = sqlite3Utf8Read(zString, 0, &zString); 507 | GlogUpperToLower(c2); 508 | } 509 | }else{ 510 | while( c2 != 0 && c2 != c ){ 511 | c2 = sqlite3Utf8Read(zString, 0, &zString); 512 | } 513 | } 514 | if( c2==0 ) return 0; 515 | if( patternCompare(zPattern,zString,pInfo,esc) ) return 1; 516 | } 517 | return 0; 518 | }else if( !prevEscape && c==matchOne ){ 519 | if( sqlite3Utf8Read(zString, 0, &zString)==0 ){ 520 | return 0; 521 | } 522 | }else if( c==matchSet ){ 523 | int prior_c = 0; 524 | assert( esc==0 ); /* This only occurs for GLOB, not LIKE */ 525 | seen = 0; 526 | invert = 0; 527 | c = sqlite3Utf8Read(zString, 0, &zString); 528 | if( c==0 ) return 0; 529 | c2 = sqlite3Utf8Read(zPattern, 0, &zPattern); 530 | if( c2=='^' ){ 531 | invert = 1; 532 | c2 = sqlite3Utf8Read(zPattern, 0, &zPattern); 533 | } 534 | if( c2==']' ){ 535 | if( c==']' ) seen = 1; 536 | c2 = sqlite3Utf8Read(zPattern, 0, &zPattern); 537 | } 538 | while( c2 && c2!=']' ){ 539 | if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){ 540 | c2 = sqlite3Utf8Read(zPattern, 0, &zPattern); 541 | if( c>=prior_c && c<=c2 ) seen = 1; 542 | prior_c = 0; 543 | }else{ 544 | if( c==c2 ){ 545 | seen = 1; 546 | } 547 | prior_c = c2; 548 | } 549 | c2 = sqlite3Utf8Read(zPattern, 0, &zPattern); 550 | } 551 | if( c2==0 || (seen ^ invert)==0 ){ 552 | return 0; 553 | } 554 | }else if( esc==c && !prevEscape ){ 555 | prevEscape = 1; 556 | }else{ 557 | c2 = sqlite3Utf8Read(zString, 0, &zString); 558 | if( noCase ){ 559 | GlogUpperToLower(c); 560 | GlogUpperToLower(c2); 561 | } 562 | if( c!=c2 ){ 563 | return 0; 564 | } 565 | prevEscape = 0; 566 | } 567 | } 568 | return *zString==0; 569 | } 570 | 571 | /* 572 | ** Count the number of times that the LIKE operator (or GLOB which is 573 | ** just a variation of LIKE) gets called. This is used for testing 574 | ** only. 575 | */ 576 | #ifdef SQLITE_TEST 577 | SQLITE_API int sqlite3_like_count = 0; 578 | #endif 579 | 580 | /* 581 | ** Implementation of the like() SQL function. This function implements 582 | ** the build-in LIKE operator. The first argument to the function is the 583 | ** pattern and the second argument is the string. So, the SQL statements: 584 | ** 585 | ** A LIKE B 586 | ** 587 | ** is implemented as like(B,A). 588 | ** 589 | ** This same function (with a different compareInfo structure) computes 590 | ** the GLOB operator. 591 | */ 592 | static void likeFunc( 593 | sqlite3_context *context, 594 | int argc, 595 | sqlite3_value **argv 596 | ){ 597 | const unsigned char *zA, *zB; 598 | int escape = 0; 599 | #if 0 600 | sqlite3 *db = sqlite3_context_db_handle(context);*/ 601 | #endif 602 | zB = sqlite3_value_text(argv[0]); 603 | zA = sqlite3_value_text(argv[1]); 604 | 605 | /* Limit the length of the LIKE or GLOB pattern to avoid problems 606 | ** of deep recursion and N*N behavior in patternCompare(). 607 | */ 608 | #if 0 609 | if( sqlite3_value_bytes(argv[0]) > 610 | db->aLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH] ){ 611 | #endif 612 | #if 1 613 | if( sqlite3_value_bytes(argv[0])>SQLITE_MAX_LIKE_PATTERN_LENGTH ){ 614 | #endif 615 | sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1); 616 | return; 617 | } 618 | 619 | assert( zB==sqlite3_value_text(argv[0]) ); /* Encoding did not change */ 620 | 621 | if( argc==3 ){ 622 | /* The escape character string must consist of a single UTF-8 character. 623 | ** Otherwise, return an error. 624 | */ 625 | const unsigned char *zEsc = sqlite3_value_text(argv[2]); 626 | if( zEsc==0 ) return; 627 | if( sqlite3Utf8CharLen((char*)zEsc, -1)!=1 ){ 628 | sqlite3_result_error(context, 629 | "ESCAPE expression must be a single character", -1); 630 | return; 631 | } 632 | escape = sqlite3Utf8Read(zEsc, 0, &zEsc); 633 | } 634 | if( zA && zB ){ 635 | struct compareInfo *pInfo = sqlite3_user_data(context); 636 | #ifdef SQLITE_TEST 637 | sqlite3_like_count++; 638 | #endif 639 | 640 | sqlite3_result_int(context, patternCompare(zB, zA, pInfo, escape)); 641 | } 642 | } 643 | 644 | /* 645 | ** Allocate nByte bytes of space using sqlite3_malloc(). If the 646 | ** allocation fails, call sqlite3_result_error_nomem() to notify 647 | ** the database handle that malloc() has failed. 648 | */ 649 | static void *contextMalloc(sqlite3_context *context, i64 nByte){ 650 | char *z; 651 | #if 0 652 | if( nByte>sqlite3_context_db_handle(context)->aLimit[SQLITE_LIMIT_LENGTH] ){ 653 | sqlite3_result_error_toobig(context); 654 | z = 0; 655 | }else{ 656 | #endif 657 | z = sqlite3_malloc((int)nByte); 658 | if( !z && nByte>0 ){ 659 | sqlite3_result_error_nomem(context); 660 | } 661 | #if 0 662 | } 663 | #endif 664 | return z; 665 | } 666 | 667 | /* 668 | ** 669 | ** Reallocate nByte bytes of space using sqlite3_realloc(). If the 670 | ** allocation fails, call sqlite3_result_error_nomem() to notify 671 | ** the database handle that malloc() has failed. 672 | ** 673 | ** SQlite has not supplied us with a reallocate function so we build our own. 674 | */ 675 | SQLITE_PRIVATE void *contextRealloc(sqlite3_context *context, void* pPrior, i64 nByte){ 676 | char *z = sqlite3_realloc(pPrior, (int)nByte); 677 | if( !z && nByte>0 ){ 678 | sqlite3_result_error_nomem(context); 679 | } 680 | return z; 681 | } 682 | 683 | #if (defined(SQLITE3_UNICODE_FOLD) || defined(SQLITE3_UNICODE_LOWER) || defined(SQLITE3_UNICODE_UPPER) || defined(SQLITE3_UNICODE_TITLE)) 684 | /* 685 | ** 686 | ** Implementation of the FOLD(), UPPER(), LOWER(), TITLE() SQL functions. 687 | ** This function case folds each character in the supplied string to its 688 | ** single character equivalent. 689 | ** 690 | ** The conversion to be made depends on the contents of (sqlite3_context *)context 691 | ** where a pointer to a specific case conversion function is stored. 692 | */ 693 | SQLITE_PRIVATE void caseFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ 694 | u16 *z1; 695 | const u16 *z2; 696 | int i, n; 697 | if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return; 698 | z2 = (u16*)sqlite3_value_text16(argv[0]); 699 | n = sqlite3_value_bytes16(argv[0]); 700 | /* Verify that the call to _bytes() does not invalidate the _text() pointer */ 701 | assert( z2==(u16*)sqlite3_value_text16(argv[0]) ); 702 | if( z2 ){ 703 | z1 = contextMalloc(context, n+2); 704 | if( z1 ){ 705 | typedef u16 (*PFN_CASEFUNC)(u16); 706 | memcpy(z1, z2, n+2); 707 | for(i=0; z1[i]; i++){ 708 | z1[i] = ((PFN_CASEFUNC)sqlite3_user_data(context))(z1[i]); 709 | } 710 | sqlite3_result_text16(context, z1, -1, sqlite3_free); 711 | } 712 | } 713 | } 714 | #endif 715 | 716 | #ifdef SQLITE3_UNICODE_UNACC 717 | /* 718 | ** 719 | ** Implementation of the UNACCENT() SQL function. 720 | ** This function decomposes each character in the supplied string 721 | ** to its components and strips any accents present in the string. 722 | ** 723 | ** This function may result to a longer output string compared 724 | ** to the original input string. Memory has been properly reallocated 725 | ** to accomodate for the extra memory length required. 726 | */ 727 | SQLITE_PRIVATE void unaccFunc(sqlite3_context *context, int argc, sqlite3_value **argv) { 728 | u16 *z1; 729 | const u16 *z2; 730 | unsigned short *p; 731 | int i, o, n, l, k; 732 | if (argc < 1 || SQLITE_NULL == sqlite3_value_type(argv[0])) 733 | return; 734 | z2 = (u16 *)sqlite3_value_text16(argv[0]); 735 | n = sqlite3_value_bytes16(argv[0]); 736 | /* Verify that the call to _bytes() does not invalidate the _text() pointer */ 737 | assert(z2 == (u16 *)sqlite3_value_text16(argv[0])); 738 | if (z2) { 739 | z1 = contextMalloc(context, n + 2); 740 | if (z1) { 741 | memcpy(z1, z2, n + 2); 742 | for (i = 0, o = 0; z2[i]; i++, o++) { 743 | simpleUnicodeUnacc(z2[i], &p, &l); 744 | if (l > 0) { 745 | if (l > 1) { 746 | n += (l - 1) * sizeof(u16); 747 | z1 = contextRealloc(context, z1, n + 2); 748 | } 749 | for (k = 0; k < l; k++) 750 | z1[o + k] = p[k]; 751 | o += --k; 752 | } else 753 | z1[o] = z2[i]; 754 | } 755 | z1[o] = 0; 756 | sqlite3_result_text16(context, z1, -1, sqlite3_free); 757 | } 758 | } 759 | } 760 | #endif 761 | 762 | 763 | #if defined(SQLITE3_UNICODE_COLLATE) && defined(SQLITE3_UNICODE_FOLD) 764 | 765 | #ifndef max 766 | # define max(a,b) (((a) > (b)) ? (a) : (b)) 767 | #endif 768 | 769 | /* 770 | ** Some systems have stricmp(). Others have strcasecmp(). Because 771 | ** there is no consistency, we will define our own. 772 | */ 773 | /* 774 | ** 775 | ** The buit-in function has been extended to accomodate UTF-8 and UTF-16 776 | ** unicode strings containing characters over the 0x80 character limit as 777 | ** per the ASCII encoding imposed by SQlite. 778 | ** 779 | ** The functions below will use the sqlite3_unicode_fold() when 780 | ** SQLITE3_UNICODE_FOLD is defined and additonally sqlite_unicode_unacc() 781 | ** when SQLITE3_UNICODE_UNACC_AUTOMATIC is defined to normilize 782 | ** UTF-8 and UTF-16 encoded strings and then compaire them for equality. 783 | */ 784 | SQLITE_PRIVATE int sqlite3StrNICmp(const unsigned char *zLeft, const unsigned char *zRight, int N){ 785 | const unsigned char *a = zLeft; 786 | const unsigned char *b = zRight; 787 | signed int ua = 0, ub = 0; 788 | int Z = 0; 789 | 790 | do { 791 | ua = sqlite3Utf8Read(a, 0, &a); ub = sqlite3Utf8Read(b, 0, &b); 792 | GlogUpperToLower(ua); GlogUpperToLower(ub); 793 | Z = (int)max(a - zLeft, b - zRight); 794 | } while(N > Z && *a!=0 && ua==ub); 795 | return N<0 ? 0 : ua - ub; 796 | } 797 | SQLITE_PRIVATE int sqlite3StrNICmp16(const void *zLeft, const void *zRight, int N){ 798 | const unsigned short *a = zLeft; 799 | const unsigned short *b = zRight; 800 | signed int ua = 0, ub = 0; 801 | 802 | do { 803 | ua = *a; ub = *b; 804 | GlogUpperToLower(ua); GlogUpperToLower(ub); 805 | a++; b++; 806 | } while(--N > 0 && *a!=0 && ua==ub); 807 | return N<0 ? 0 : ua - ub; 808 | } 809 | 810 | /* 811 | ** Another built-in collating sequence: NOCASE. 812 | ** 813 | ** This collating sequence is intended to be used for "case independant 814 | ** comparison". SQLite's knowledge of upper and lower case equivalents 815 | ** extends only to the 26 characters used in the English language. 816 | ** 817 | ** At the moment there is only a UTF-8 implementation. 818 | */ 819 | /* 820 | ** 821 | ** The built-in collating sequence: NOCASE is extended to accomodate the 822 | ** unicode case folding mapping tables to normalize characters to their 823 | ** fold equivalents and test them for equality. 824 | ** 825 | ** Both UTF-8 and UTF-16 implementations are supported. 826 | ** 827 | ** (void *)encoding takes the following values 828 | ** * SQLITE_UTF8 for UTF-8 encoded string comparison 829 | ** * SQLITE_UFT16 for UTF-16 encoded string comparison 830 | */ 831 | SQLITE_EXPORT int sqlite3_unicode_collate( 832 | void *encoding, 833 | int nKey1, const void *pKey1, 834 | int nKey2, const void *pKey2 835 | ){ 836 | int r = 0; 837 | 838 | if ((void*)SQLITE_UTF8 == encoding) 839 | r = sqlite3StrNICmp( 840 | (const unsigned char *)pKey1, (const unsigned char *)pKey2, (nKey1 854 | ** Implementation of the UNICODE_VERSION(*) function. The result is the version 855 | ** of the unicode library that is running. 856 | */ 857 | SQLITE_PRIVATE void versionFunc( 858 | sqlite3_context *context, 859 | int argc, 860 | sqlite3_value **argv 861 | ){ 862 | (void)argc; 863 | (void)argv; 864 | sqlite3_result_text(context, SQLITE3_UNICODE_VERSION_STRING, -1, SQLITE_STATIC); 865 | } 866 | 867 | /* 868 | ** 869 | ** Register the UNICODE extension functions with database db. 870 | */ 871 | SQLITE_EXPORT int sqlite3_unicode_init(sqlite3 *db){ 872 | struct FuncScalar { 873 | const char *zName; /* Function name */ 874 | int nArg; /* Number of arguments */ 875 | int enc; /* Optimal text encoding */ 876 | void *pContext; /* sqlite3_user_data() context */ 877 | void (*xFunc)(sqlite3_context*,int,sqlite3_value**); 878 | } scalars[] = { 879 | {"unicode_version", 0, SQLITE_ANY, 0, versionFunc}, 880 | 881 | #ifdef SQLITE3_UNICODE_FOLD 882 | {"like", 2, SQLITE_ANY, (void*)&likeInfoNorm, likeFunc }, 883 | {"like", 3, SQLITE_ANY, (void*)&likeInfoNorm, likeFunc }, 884 | 885 | {"fold", 1, SQLITE_ANY, (void*)simpleUnicodeFold, caseFunc }, 886 | #endif 887 | #ifdef SQLITE3_UNICODE_LOWER 888 | {"lower", 1, SQLITE_ANY, (void*)simpleUnicodeLower, caseFunc }, 889 | #endif 890 | #ifdef SQLITE3_UNICODE_UPPER 891 | {"upper", 1, SQLITE_ANY, (void*)simpleUnicodeUpper, caseFunc }, 892 | #endif 893 | #ifdef SQLITE3_UNICODE_TITLE 894 | {"title", 1, SQLITE_ANY, (void*)simpleUnicodeTitle, caseFunc }, 895 | #endif 896 | #ifdef SQLITE3_UNICODE_UNACC 897 | {"unaccent", 1, SQLITE_ANY, 0, unaccFunc}, 898 | #endif 899 | }; 900 | 901 | int rc = SQLITE_OK; 902 | int i; 903 | 904 | for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(struct FuncScalar)); i++){ 905 | struct FuncScalar *p = &scalars[i]; 906 | rc = sqlite3_create_function( 907 | db, p->zName, p->nArg, p->enc, p->pContext, p->xFunc, 0, 0 908 | ); 909 | } 910 | 911 | #if defined(SQLITE3_UNICODE_COLLATE) && defined(SQLITE3_UNICODE_FOLD) 912 | /* Also override the default NOCASE UTF-8 case-insensitive collation sequence. */ 913 | rc = sqlite3_create_collation(db, "NOCASE", SQLITE_UTF8, (void*)SQLITE_UTF8, sqlite3_unicode_collate); 914 | rc = sqlite3_create_collation(db, "NOCASE", SQLITE_UTF16, (void*)SQLITE_UTF16, sqlite3_unicode_collate); 915 | #endif 916 | 917 | return rc; 918 | } 919 | 920 | /* 921 | ** 922 | ** The following function is the default entry point of an SQlite extension built as a 923 | ** dynamically linked library. On calling sqlite3_load_extension() sqlite3 will call 924 | ** this function to initialise unicode functionality. 925 | */ 926 | #ifndef SQLITE_CORE 927 | SQLITE_EXPORT int sqlite3_extension_init( 928 | sqlite3 *db, 929 | char **pzErrMsg, 930 | const sqlite3_api_routines *pApi 931 | ){ 932 | (void)pzErrMsg; 933 | SQLITE_EXTENSION_INIT2(pApi) 934 | return sqlite3_unicode_init(db); 935 | } 936 | #endif 937 | 938 | /* 939 | ** 940 | ** The following function needs to be called at application startup to load the extension. 941 | */ 942 | SQLITE_EXPORT int sqlite3_unicode_load() 943 | { 944 | #ifndef SQLITE_CORE 945 | return sqlite3_auto_extension((void*)sqlite3_extension_init); 946 | #else 947 | return sqlite3_auto_extension((void*)sqlite3_unicode_init); 948 | #endif 949 | } 950 | 951 | /* 952 | ** 953 | ** The following function needs to be called before application exit to unload the extension. 954 | */ 955 | SQLITE_EXPORT void sqlite3_unicode_free() 956 | { 957 | sqlite3_reset_auto_extension(); 958 | } 959 | #endif 960 | 961 | /* 962 | ** 963 | ** Hack for Microsoft Windows where sqlite3_unicode is statically linked to 964 | ** an sqlite3 dynamically linked library. DllMain will be automatically be called 965 | ** by Microsoft Windows when the library is loaded into the application to automatically 966 | ** load extension and when unloaded to automatically unload the extension. 967 | */ 968 | #if ((defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)) && (!defined(SQLITE_CORE))) 969 | int __stdcall DllMain(void *hinstDLL, unsigned long fdwReason, void *lpReserved) 970 | { 971 | if (fdwReason == 1 /*DLL_PROCESS_ATTACH*/) 972 | sqlite3_unicode_load(); 973 | else if (fdwReason == 0 /*DLL_PROCESS_DETACH*/) 974 | sqlite3_unicode_free(); 975 | return 1; 976 | } 977 | #endif 978 | 979 | //#endif //!defined(SQLITE_CORE) || defined(SQLITE_ENABLE_UNICODE) 980 | -------------------------------------------------------------------------------- /src/stdafx.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | // TODO: reference any additional headers you need in STDAFX.H 4 | // and not in this file 5 | -------------------------------------------------------------------------------- /src/stdafx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _WINDOWS 4 | #define WIN32_LEAN_AND_MEAN 5 | #define NOMINMAX 6 | #include 7 | #endif //_WINDOWS 8 | 9 | #if defined(__linux__) || defined(__APPLE__) 10 | #define LINUX_OR_MACOS 11 | #endif 12 | 13 | #include 14 | using namespace simstr; 15 | 16 | #include "AddInDefBase.h" 17 | #include "ComponentBase.h" 18 | #include "IMemoryManager.h" 19 | #include 20 | -------------------------------------------------------------------------------- /src/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeList.txt : CMake project for core_as, include source and define 2 | # project specific logic here. 3 | # 4 | cmake_minimum_required (VERSION 3.15) 5 | add_executable(testv8sqlite test_addin_base.cpp ../addin_base.cpp test_sqlite.cpp ../sqlite.cpp ../sqlite3_unicode.c) 6 | target_include_directories(testv8sqlite PRIVATE "..") 7 | add_compile_definitions(BUILD_TESTS SQLITE_CORE SQLITE_ENABLE_UNICODE) 8 | add_dependencies(testv8sqlite simstr) 9 | target_link_libraries(testv8sqlite sqlite3 simstr gtest_main) 10 | add_test(NAME testv8sqlite COMMAND testv8sqlite) 11 | -------------------------------------------------------------------------------- /src/tests/test_addin_base.cpp: -------------------------------------------------------------------------------- 1 | #include "addin_base.h" 2 | #include 3 | #include 4 | 5 | /* 6 | * Мок для 1Сного менеджера памяти 7 | */ 8 | struct MemoryManager : IMemoryManager { 9 | bool ADDIN_API AllocMemory(void** pMemory, unsigned long ulCountByte) override { 10 | if (pMemory) { 11 | *pMemory = malloc(ulCountByte); 12 | return *pMemory != nullptr; 13 | } 14 | return false; 15 | } 16 | void ADDIN_API FreeMemory(void** pMemory) override { 17 | if (pMemory && *pMemory) { 18 | ::free(*pMemory); 19 | } 20 | } 21 | template 22 | void free(const T* ptr) { 23 | FreeMemory((void**)&ptr); 24 | } 25 | }; 26 | 27 | /* 28 | * Простой пустой аддин, для него не нужно END_EXTENSION 29 | * Должен отрабатывать все методы аддина с пустым результатом 30 | */ 31 | struct TestEmptyAddin : public V8Addin { 32 | REGISTER_EXTENSION(u"TestEmptyAddin"); 33 | }; 34 | 35 | /* 36 | * Аддин с одной процедурой, для которой установленно наличие параметров 37 | * по умолчанию, но при этом ноль параметров. Для проверки срабатывания 38 | * статического ассерта на такие ситуации. Не в тестовом окружении это 39 | * не скомпилируется. 40 | */ 41 | struct TestAddinProc1 : public V8Addin { 42 | REGISTER_EXTENSION(u"TestAddinProc1"); 43 | 44 | EXT_PROC_WITH_DEFVAL(Proc1, u"Процедура1", 0) { 45 | return true; 46 | } 47 | EXT_DEFVAL_FOR(Proc1) { 48 | return true; 49 | } 50 | 51 | EXT_PROP_RO(Prop1, u"Свойство1") { 52 | value.vt = VTYPE_BOOL; 53 | value.bVal = true; 54 | return true; 55 | } 56 | 57 | END_EXTENSION(); 58 | }; 59 | 60 | struct TestAddinProc2 : public V8Addin { 61 | REGISTER_EXTENSION(u"TestAddinProc2"); 62 | 63 | EXT_PROC_WITH_DEFVAL(Proc1, u"Процедура1", 1) { 64 | return true; 65 | } 66 | EXT_DEFVAL_FOR(Proc1) { 67 | return true; 68 | } 69 | 70 | EXT_FUNC(Func1, u"Функция1", 0) { 71 | return true; 72 | } 73 | 74 | EXT_PROP_WO(Prop1, u"Свойство1") { 75 | return true; 76 | } 77 | 78 | END_EXTENSION(); 79 | }; 80 | 81 | struct TestAddinProc3 : public V8Addin { 82 | REGISTER_EXTENSION(u"TestAddinProc3"); 83 | 84 | EXT_PROC_WITH_DEFVAL(Proc1, u"Процедура1", 1) { 85 | return true; 86 | } 87 | EXT_DEFVAL_FOR(Proc1) { 88 | return true; 89 | } 90 | 91 | EXT_FUNC(Func1, u"Функция1", 1) { 92 | return true; 93 | } 94 | 95 | EXT_PROC(Proc3, u"Процедура3", 2) { 96 | return true; 97 | } 98 | 99 | END_EXTENSION(); 100 | }; 101 | 102 | 103 | /* 104 | * Аддин с одной процедурой, но при этом без завершающего макроса END_EXTENSION 105 | * Для проверки срабатывания статического ассерта на такие ситуации. 106 | * Не в тестовом окружении это не скомпилируется. 107 | */ 108 | struct TestNoEndExtesion : public V8Addin { 109 | public: 110 | REGISTER_EXTENSION(u"TestNoEndExtesion"); 111 | 112 | EXT_PROC(Proc1, u"Процедура1", 1) { 113 | return true; 114 | } 115 | }; 116 | 117 | class TestAddin : public V8Addin { 118 | 119 | public: 120 | int result{0}; 121 | REGISTER_EXTENSION(u"TestAddin"); 122 | 123 | EXT_PROC(TestProc, u"ТестПроцедура", 2) { 124 | EXPECT_EQ(count, 2u); 125 | EXPECT_EQ(params[0].vt, VTYPE_BOOL); 126 | EXPECT_TRUE(params[0].bVal); 127 | EXPECT_EQ(params[1].vt, VTYPE_UI4); 128 | EXPECT_EQ(params[1].uintVal, 0xDEADBEEF); 129 | result = params[1].uintVal; 130 | return true; 131 | } 132 | 133 | EXT_PROC_WITH_DEFVAL(TestDefValProc, u"ТестЗначПроц", 1) { 134 | return true; 135 | } 136 | EXT_DEFVAL_FOR(TestDefValProc) { 137 | if (param == 0) { 138 | if (value) { 139 | value->vt = VTYPE_BOOL; 140 | value->bVal = true; 141 | } 142 | return true; 143 | } 144 | return false; 145 | } 146 | 147 | EXT_FUNC(TestFunc, u"ТестФункция", 0) { 148 | return true; 149 | } 150 | 151 | EXT_FUNC_WITH_DEFVAL(TestDefValFunc, u"ТестЗначФунк", 2) { 152 | EXPECT_EQ(count, 2u); 153 | EXPECT_EQ(params[0].vt, VTYPE_BOOL); 154 | EXPECT_TRUE(params[0].bVal); 155 | EXPECT_EQ(params[1].vt, VTYPE_UI4); 156 | EXPECT_EQ(params[1].uintVal, 0xDEADBEEF); 157 | retVal.vt = VTYPE_BOOL; 158 | retVal.bVal = true; 159 | result = params[1].uintVal; 160 | return true; 161 | } 162 | EXT_DEFVAL_FOR(TestDefValFunc) { 163 | if (param == 1) { 164 | if (value) { 165 | value->vt = VTYPE_UI4; 166 | value->uintVal = 0xDEADBEEF; 167 | } 168 | return true; 169 | } 170 | return false; 171 | } 172 | 173 | EXT_PROP_RO(TestPropRead, u"ТестСвойствоЧтение") { 174 | return true; 175 | } 176 | 177 | EXT_PROP_WO(TestPropWrite, u"ТестСвойствоЗапись") { 178 | return true; 179 | } 180 | 181 | EXT_PROP_RW(TestPropRW, u"ТестСвойствоЧЗ") { 182 | value.vt = VTYPE_BOOL; 183 | value.bVal = true; 184 | return true; 185 | } 186 | EXT_PROP_WRITE(TestPropRW) { 187 | result = 0xDEADBEEF; 188 | return true; 189 | } 190 | END_EXTENSION(); 191 | }; 192 | 193 | /* 194 | * Проверяем, что все типы аддинов зарегистрировались и сформировали строку описания классов. 195 | * Метод GetClassNames должен возвращать строку с названиями всех возможных типов аддинов, 196 | * разделённых символом '|' 197 | */ 198 | TEST(AddinBase, GetClassNames) { 199 | hashStrMapU addinNames{ 200 | {u"TestEmptyAddin"_h, 0}, 201 | {u"TestAddin"_h, 0}, 202 | {u"TestAddinProc1"_h, 0}, 203 | {u"TestAddinProc2"_h, 0}, 204 | {u"TestAddinProc3"_h, 0}, 205 | {u"TestNoEndExtesion"_h, 0}}; 206 | 207 | auto names = stru{GetClassNames()}.splitter(u"|"); 208 | 209 | EXPECT_FALSE(names.isDone()); 210 | 211 | while (!names.isDone()) { 212 | auto fnd = addinNames.find(names.next()); 213 | EXPECT_NE(fnd, addinNames.end()); 214 | addinNames.erase(fnd); 215 | } 216 | EXPECT_TRUE(addinNames.empty()); 217 | } 218 | 219 | void testCreate(stru name) { 220 | IComponentBase* addin = nullptr; 221 | EXPECT_TRUE(GetClassObject(name, &addin)); 222 | EXPECT_TRUE(addin); 223 | 224 | MemoryManager mm; 225 | addin->setMemManager(&mm); 226 | 227 | u16s* addinName; 228 | addin->RegisterExtensionAs(&addinName); 229 | EXPECT_EQ(stru{addinName}, name); 230 | mm.free(addinName); 231 | 232 | addin->Done(); 233 | DestroyObject((IComponentBase**)&addin); 234 | } 235 | 236 | /* 237 | * Проверим, что через GetClassObject действительно создаются объекты 238 | * и что они нужного типа 239 | */ 240 | TEST(AddinBase, GetClassObject) { 241 | testCreate(u"TestEmptyAddin"); 242 | testCreate(u"TestAddin"); 243 | } 244 | 245 | // Проверка, что срабатывает статический ассерт, если забыли END_EXTENSION 246 | TEST(AddinBase, CheckEndExtesion) { 247 | EXPECT_THROW(TestNoEndExtesion::Proc1_def::test(), std::logic_error); 248 | } 249 | 250 | // Проверка, что срабатывает статический ассерт, если забыли в методе с параметрами по умолчанию 0 параметров 251 | TEST(AddinBase, CheckDefvalWithZeroParams) { 252 | EXPECT_THROW(TestAddinProc1::Proc1_def::test(), std::logic_error); 253 | } 254 | 255 | // Проверка количества методов 256 | TEST(AddinBase, GetNMethods) { 257 | TestEmptyAddin te; 258 | EXPECT_EQ(te.GetNMethods(), 0); 259 | 260 | TestAddinProc1 t1; 261 | EXPECT_EQ(t1.GetNMethods(), 1); 262 | 263 | TestAddinProc2 t2; 264 | EXPECT_EQ(t2.GetNMethods(), 2); 265 | 266 | TestAddinProc3 t3; 267 | EXPECT_EQ(t3.GetNMethods(), 3); 268 | 269 | TestAddin ta; 270 | EXPECT_EQ(ta.GetNMethods(), 4); 271 | } 272 | 273 | // Проверка поиска методов 274 | TEST(AddinBase, FindMethod) { 275 | EXPECT_EQ(TestEmptyAddin{}.FindMethod(u"Proc1"), -1); 276 | EXPECT_EQ(TestAddinProc1{}.FindMethod(u"Noproc"), -1); 277 | EXPECT_EQ(TestAddinProc1{}.FindMethod(u"proc1"), 0); 278 | EXPECT_EQ(TestAddinProc1{}.FindMethod(u"процедура1"), 0); 279 | EXPECT_EQ(TestAddinProc2{}.FindMethod(u"процедура1"), 0); 280 | EXPECT_EQ(TestAddinProc2{}.FindMethod(u"Func1"), 1); 281 | EXPECT_EQ(TestAddinProc3{}.FindMethod(u"процЕдура3"), 2); 282 | EXPECT_EQ(TestAddinProc3{}.FindMethod(u"---"), -1); 283 | EXPECT_EQ(TestAddin{}.FindMethod(u"---"), -1); 284 | EXPECT_EQ(TestAddin{}.FindMethod(u"ТестФункция"), 2); 285 | EXPECT_EQ(TestAddin{}.FindMethod(u"testdefvalfunc"), 3); 286 | EXPECT_EQ(TestAddin{}.FindMethod(nullptr), -1); 287 | } 288 | 289 | template 290 | void checkParamsCount(stru name, int metNum, int result) { 291 | T addin; 292 | int method = addin.FindMethod(name); 293 | EXPECT_EQ(method,metNum); 294 | EXPECT_EQ(addin.GetNParams(method), result); 295 | } 296 | 297 | // Проверка количества параметров 298 | TEST(AddinBase, GetNParams) { 299 | checkParamsCount(u"proc1", 0, 0); 300 | checkParamsCount(u"proc1", 0, 1); 301 | checkParamsCount(u"func1", 1, 0); 302 | checkParamsCount(u"proc3", 2, 2); 303 | checkParamsCount(u"TestDefValFunc", 3, 2); 304 | checkParamsCount(u"---", -1, -1); 305 | EXPECT_EQ(TestAddin{}.GetNParams(100), -1); 306 | } 307 | 308 | // Проверка HasRetVal 309 | TEST(AddinBase, HasRetVal) { 310 | EXPECT_EQ(TestAddinProc1{}.HasRetVal(0), false); 311 | EXPECT_EQ(TestAddinProc2{}.HasRetVal(0), false); 312 | EXPECT_EQ(TestAddinProc2{}.HasRetVal(1), true); 313 | 314 | EXPECT_EQ(TestAddinProc3{}.HasRetVal(0), false); 315 | EXPECT_EQ(TestAddinProc3{}.HasRetVal(1), true); 316 | EXPECT_EQ(TestAddinProc3{}.HasRetVal(2), false); 317 | 318 | EXPECT_EQ(TestAddin{}.HasRetVal(0), false); 319 | EXPECT_EQ(TestAddin{}.HasRetVal(2), true); 320 | EXPECT_EQ(TestAddin{}.HasRetVal(3), true); 321 | } 322 | 323 | // Проверка GetMethodName 324 | TEST(AddinBase, GetMethodName) { 325 | MemoryManager mm; 326 | TestAddinProc1 a1; 327 | a1.setMemManager(&mm); 328 | EXPECT_EQ(stru{a1.GetMethodName(0, 0)}, u"Proc1"); 329 | EXPECT_EQ(stru{a1.GetMethodName(0, 1)}, u"Процедура1"); 330 | EXPECT_EQ(TestAddinProc1{}.GetMethodName(0, 2), nullptr); 331 | EXPECT_EQ(TestAddinProc1{}.GetMethodName(1, 1), nullptr); 332 | TestAddin ta; 333 | ta.setMemManager(&mm); 334 | EXPECT_EQ(stru{ta.GetMethodName(1, 0)}, u"TestDefValProc"); 335 | EXPECT_EQ(stru{ta.GetMethodName(1, 1)}, u"ТестЗначПроц"); 336 | } 337 | 338 | // Проверка GetParamDefValue 339 | TEST(AddinBase, GetParamDefValue) { 340 | TestAddin addin; 341 | EXPECT_FALSE(addin.GetParamDefValue(1, 0, nullptr)); 342 | EXPECT_FALSE(addin.GetParamDefValue(10, 0, nullptr)); 343 | EXPECT_FALSE(addin.GetParamDefValue(0, 1, nullptr)); 344 | 345 | tVariant param; 346 | EXPECT_TRUE(addin.GetParamDefValue(1, 0, ¶m)); 347 | EXPECT_EQ(param.vt, VTYPE_BOOL); 348 | EXPECT_TRUE(param.bVal); 349 | 350 | EXPECT_FALSE(addin.GetParamDefValue(3, 0, ¶m)); 351 | EXPECT_FALSE(addin.GetParamDefValue(3, 2, ¶m)); 352 | EXPECT_TRUE(addin.GetParamDefValue(3, 1, ¶m)); 353 | EXPECT_EQ(param.vt, VTYPE_UI4); 354 | EXPECT_EQ(param.uintVal, 0xDEADBEEF); 355 | } 356 | 357 | // Проверка CallAsProc 358 | TEST(AddinBase, CallAsProc) { 359 | TestAddin addin; 360 | // Вызов процедуры с пустыми параметрами 361 | EXPECT_FALSE(addin.CallAsProc(0, nullptr, 2)); 362 | tVariant params[2]; 363 | // Вызов несуществующей процедуры 364 | EXPECT_FALSE(addin.CallAsProc(20, params, 2)); 365 | // Вызов функции как процедуры 366 | EXPECT_FALSE(addin.CallAsProc(2, params, 2)); 367 | 368 | params[0].vt = VTYPE_BOOL; 369 | params[0].bVal = true; 370 | params[1].vt = VTYPE_UI4; 371 | params[1].uintVal = 0xDEADBEEF; 372 | 373 | EXPECT_TRUE(addin.CallAsProc(0, params, 2)); 374 | EXPECT_EQ(addin.result, 0xDEADBEEF); 375 | } 376 | 377 | // Проверка CallAsFunc 378 | TEST(AddinBase, CallAsFunc) { 379 | TestAddin addin; 380 | tVariant params[2], retVal; 381 | // Вызов функции с пустыми параметрами 382 | EXPECT_FALSE(addin.CallAsFunc(3, &retVal, nullptr, 2)); 383 | // Вызов функции с пустым возвращаемым значением 384 | EXPECT_FALSE(addin.CallAsFunc(3, nullptr, params, 2)); 385 | // Вызов несуществующей функции 386 | EXPECT_FALSE(addin.CallAsFunc(20, &retVal, params, 2)); 387 | // Вызов процедуры как функции 388 | EXPECT_FALSE(addin.CallAsFunc(0, &retVal, params, 2)); 389 | 390 | params[0].vt = VTYPE_BOOL; 391 | params[0].bVal = true; 392 | params[1].vt = VTYPE_UI4; 393 | params[1].uintVal = 0xDEADBEEF; 394 | retVal.vt = VTYPE_EMPTY; 395 | 396 | EXPECT_TRUE(addin.CallAsFunc(3, &retVal, params, 2)); 397 | EXPECT_EQ(addin.result, 0xDEADBEEF); 398 | 399 | EXPECT_EQ(retVal.vt, VTYPE_BOOL); 400 | EXPECT_TRUE(retVal.bVal); 401 | } 402 | 403 | // Проверка GetNProps 404 | TEST(AddinBase, GetNProps) { 405 | EXPECT_EQ(TestEmptyAddin{}.GetNProps(), 0); 406 | EXPECT_EQ(TestAddinProc1{}.GetNProps(), 1); 407 | EXPECT_EQ(TestAddinProc2{}.GetNProps(), 1); 408 | EXPECT_EQ(TestAddinProc3{}.GetNProps(), 0); 409 | EXPECT_EQ(TestAddin{}.GetNProps(), 3); 410 | } 411 | 412 | // Проверка FindProp 413 | TEST(AddinBase, FindProp) { 414 | EXPECT_EQ(TestEmptyAddin{}.FindProp(u"Prop"), -1); 415 | EXPECT_EQ(TestAddinProc1{}.FindProp(u"prop1"), 0); 416 | EXPECT_EQ(TestAddinProc1{}.FindProp(u"свойство1"), 0); 417 | EXPECT_EQ(TestAddinProc2{}.FindProp(u"prop1"), 0); 418 | EXPECT_EQ(TestAddinProc2{}.FindProp(u"свойство1"), 0); 419 | EXPECT_EQ(TestAddin{}.FindProp(u"TestPropRead"), 0); 420 | EXPECT_EQ(TestAddin{}.FindProp(u"TestPropRW"), 2); 421 | EXPECT_EQ(TestAddin{}.FindProp(nullptr), -1); 422 | } 423 | 424 | // Проверка GetPropName 425 | TEST(AddinBase, GetPropName) { 426 | MemoryManager mm; 427 | EXPECT_EQ(TestEmptyAddin{}.GetPropName(0, 0), nullptr); 428 | TestAddinProc1 t1; 429 | t1.setMemManager(&mm); 430 | EXPECT_EQ(stru{t1.GetPropName(0, 0)}, u"Prop1"); 431 | EXPECT_EQ(stru{t1.GetPropName(0, 1)}, u"Свойство1"); 432 | EXPECT_EQ(TestAddinProc1{}.GetPropName(0, 2), nullptr); 433 | EXPECT_EQ(TestAddinProc1{}.GetPropName(10, 1), nullptr); 434 | TestAddin ta; 435 | ta.setMemManager(&mm); 436 | EXPECT_EQ(stru{ta.GetPropName(1, 0)}, u"TestPropWrite"); 437 | EXPECT_EQ(stru{ta.GetPropName(1, 1)}, u"ТестСвойствоЗапись"); 438 | } 439 | 440 | // Проверка IsPropReadable 441 | TEST(AddinBase, IsPropReadable) { 442 | EXPECT_FALSE(TestEmptyAddin{}.IsPropReadable(0)); 443 | EXPECT_TRUE(TestAddinProc1{}.IsPropReadable(0)); 444 | EXPECT_FALSE(TestAddinProc1{}.IsPropReadable(1)); 445 | EXPECT_TRUE(TestAddin{}.IsPropReadable(0)); 446 | EXPECT_FALSE(TestAddin{}.IsPropReadable(1)); 447 | EXPECT_TRUE(TestAddin{}.IsPropReadable(2)); 448 | } 449 | 450 | // Проверка IsPropWritable 451 | TEST(AddinBase, IsPropWritable) { 452 | EXPECT_FALSE(TestEmptyAddin{}.IsPropWritable(0)); 453 | EXPECT_FALSE(TestAddinProc1{}.IsPropWritable(0)); 454 | EXPECT_FALSE(TestAddinProc1{}.IsPropWritable(1)); 455 | EXPECT_FALSE(TestAddin{}.IsPropWritable(0)); 456 | EXPECT_TRUE(TestAddin{}.IsPropWritable(1)); 457 | EXPECT_TRUE(TestAddin{}.IsPropWritable(2)); 458 | } 459 | 460 | // Проверка GetPropVal 461 | TEST(AddinBase, GetPropVal) { 462 | tVariant prop; 463 | prop.vt = VTYPE_EMPTY; 464 | EXPECT_FALSE(TestEmptyAddin{}.GetPropVal(0, &prop)); 465 | EXPECT_FALSE(TestEmptyAddin{}.GetPropVal(0, nullptr)); 466 | 467 | EXPECT_FALSE(TestAddinProc1{}.GetPropVal(0, nullptr)); 468 | EXPECT_TRUE(TestAddinProc1{}.GetPropVal(0, &prop)); 469 | EXPECT_EQ(prop.vt, VTYPE_BOOL); 470 | EXPECT_TRUE(prop.bVal); 471 | 472 | EXPECT_FALSE(TestAddinProc2{}.GetPropVal(0, &prop)); 473 | prop.vt = VTYPE_EMPTY; 474 | EXPECT_TRUE(TestAddin{}.GetPropVal(2, &prop)); 475 | EXPECT_EQ(prop.vt, VTYPE_BOOL); 476 | EXPECT_TRUE(prop.bVal); 477 | } 478 | 479 | // Проверка SetPropVal 480 | TEST(AddinBase, SetPropVal) { 481 | tVariant prop; 482 | prop.vt = VTYPE_EMPTY; 483 | EXPECT_FALSE(TestEmptyAddin{}.SetPropVal(0, &prop)); 484 | EXPECT_FALSE(TestEmptyAddin{}.SetPropVal(0, nullptr)); 485 | 486 | EXPECT_FALSE(TestAddinProc1{}.SetPropVal(0, nullptr)); 487 | EXPECT_FALSE(TestAddinProc1{}.SetPropVal(0, &prop)); 488 | 489 | EXPECT_TRUE(TestAddinProc2{}.SetPropVal(0, &prop)); 490 | 491 | TestAddin ta; 492 | 493 | EXPECT_TRUE(ta.SetPropVal(2, &prop)); 494 | EXPECT_EQ(ta.result, 0xDEADBEEF); 495 | } 496 | -------------------------------------------------------------------------------- /src/tests/test_sqlite.cpp: -------------------------------------------------------------------------------- 1 | #include "../sqlite.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef _WIN32 8 | #include 9 | #include 10 | #include 11 | #endif 12 | 13 | 14 | TEST(Sqlite, CreateBase) { 15 | SqliteBase base; 16 | EXPECT_FALSE(base.isOpen()); 17 | EXPECT_EQ(base.lastError(), u""); 18 | EXPECT_EQ(base.changes(), 0ll); 19 | EXPECT_EQ(base.lastId(), 0ll); 20 | 21 | EXPECT_TRUE(base.open(u":memory:")); 22 | EXPECT_TRUE(base.isOpen()); 23 | base.close(); 24 | EXPECT_FALSE(base.isOpen()); 25 | } 26 | 27 | TEST(Sqlite, Move) { 28 | SqliteBase base; 29 | EXPECT_FALSE(base.isOpen()); 30 | EXPECT_EQ(base.lastError(), u""); 31 | EXPECT_EQ(base.changes(), 0ll); 32 | EXPECT_EQ(base.lastId(), 0ll); 33 | 34 | EXPECT_TRUE(base.open(u":memory:")); 35 | EXPECT_TRUE(base.isOpen()); 36 | 37 | sqlite3* db = base; 38 | 39 | SqliteBase other(std::move(base)); 40 | EXPECT_TRUE(other.isOpen()); 41 | EXPECT_FALSE(base.isOpen()); 42 | EXPECT_EQ(db, static_cast(other)); 43 | 44 | base = std::move(other); 45 | EXPECT_TRUE(base.isOpen()); 46 | EXPECT_FALSE(other.isOpen()); 47 | EXPECT_EQ(db, static_cast(base)); 48 | } 49 | 50 | TEST(Sqlite, ErrorCreateBase) { 51 | SqliteBase base; 52 | #ifdef _WIN32 53 | EXPECT_FALSE(base.open(u"c:\\nul\\com")); 54 | #else 55 | EXPECT_FALSE(base.open(u"/sys")); 56 | #endif 57 | EXPECT_EQ(base.lastError(), u"unable to open database file"); 58 | EXPECT_FALSE(base.isOpen()); 59 | } 60 | 61 | TEST(Sqlite, Exec) { 62 | SqliteBase base; 63 | base.open(u":memory:"); 64 | EXPECT_NE(base.exec(u"create table test(a, b, c"), SQLITE_OK); 65 | EXPECT_EQ(base.lastError(), u"incomplete input"); 66 | EXPECT_EQ(base.exec(u"create table test(a, b, c)"), SQLITE_OK); 67 | EXPECT_EQ(base.lastError(), u"not an error"); 68 | EXPECT_EQ(base.changes(), 0ll); 69 | EXPECT_EQ(base.lastId(), 0ll); 70 | EXPECT_EQ(base.exec(u"insert into test values(1, 2, 3)"), SQLITE_OK); 71 | EXPECT_EQ(base.lastError(), u"not an error"); 72 | EXPECT_EQ(base.changes(), 1ll); 73 | EXPECT_EQ(base.lastId(), 1ll); 74 | } 75 | 76 | TEST(Sqlite, Prepare) { 77 | SqliteBase base; 78 | base.open(u":memory:"); 79 | EXPECT_EQ(base.exec(u"create table test(a, b, c)"), SQLITE_OK); 80 | auto query = base.prepare(u"select * from test"); 81 | EXPECT_TRUE(query.valid()); 82 | } 83 | 84 | struct SimpleResultReceiver { 85 | std::vector columns; 86 | int64_t lastId_{0}; 87 | int64_t changes_{0}; 88 | int error_{0}; 89 | stringu errMessage_; 90 | 91 | void setColumnCount(unsigned cc) { 92 | columns.reserve(cc); 93 | } 94 | void addColumnName(ssu name) { 95 | columns.emplace_back(name); 96 | } 97 | void setResult(int e, sqlite3* db) { 98 | error_ = e; 99 | if (db) { 100 | lastId_ = sqlite3_last_insert_rowid(db); 101 | changes_ = sqlite3_changes64(db); 102 | errMessage_ = stru{(const u16s*)sqlite3_errmsg16(db)}; 103 | } 104 | } 105 | struct value { 106 | enum class Type { 107 | Null, Int, Real, Text, Blob 108 | } type; 109 | value() : type(Type::Null) {} 110 | value(int64_t i) : type(Type::Int), iVal(i) {} 111 | value(double i) : type(Type::Real), dVal(i) {} 112 | value(ssa i) : type(Type::Blob) { 113 | new (bVal) stringa(i); 114 | } 115 | value(ssu i) : type(Type::Text) { 116 | new (tVal) stringu(i); 117 | } 118 | ~value() { 119 | if (type == Type::Text) { 120 | text().~stringu(); 121 | } else if (type == Type::Blob) { 122 | blob().~stringa(); 123 | } 124 | } 125 | value(const value&) = delete; 126 | value(value&& other) noexcept { 127 | memcpy(this, &other, sizeof(*this)); 128 | other.type = Type::Null; 129 | } 130 | value& operator=(value other) noexcept { 131 | this->~value(); 132 | new (this) value(std::move(other)); 133 | return *this; 134 | } 135 | 136 | stringa& blob() { 137 | return *(stringa*)bVal; 138 | } 139 | stringu& text() { 140 | return *(stringu*)tVal; 141 | } 142 | union { 143 | int64_t iVal; 144 | double dVal; 145 | char tVal[sizeof(stringu)]; 146 | char bVal[sizeof(stringa)]; 147 | }; 148 | }; 149 | std::vector> rows; 150 | 151 | void addRow() { 152 | rows.emplace_back(); 153 | rows.back().reserve(columns.size()); 154 | } 155 | void addNull() { 156 | rows.back().emplace_back(); 157 | } 158 | void addInteger(int64_t v) { 159 | rows.back().emplace_back(v); 160 | } 161 | void addReal(double v) { 162 | rows.back().emplace_back(v); 163 | } 164 | void addText(ssu v) { 165 | rows.back().emplace_back(v); 166 | } 167 | void addBlob(ssa v){ 168 | rows.back().emplace_back(v); 169 | } 170 | }; 171 | 172 | TEST(Sqlite, ExecPrepared) { 173 | SqliteBase base; 174 | base.open(u":memory:"); 175 | EXPECT_EQ(base.exec(u"create table test(a, b, c)"), SQLITE_OK); 176 | EXPECT_EQ(base.exec(u"insert into test values(1, 2.0, 'text')"), SQLITE_OK); 177 | EXPECT_EQ(base.exec(u"insert into test values(null, x'4142430041', '3')"), SQLITE_OK); 178 | auto query = base.prepare(u"select * from test"); 179 | EXPECT_TRUE(query.valid()); 180 | 181 | SimpleResultReceiver sr; 182 | query.exec(sr); 183 | 184 | EXPECT_EQ(sr.columns.size(), 3u); 185 | EXPECT_EQ(sr.rows.size(), 2u); 186 | EXPECT_EQ(sr.rows[0][0].type, SimpleResultReceiver::value::Type::Int); 187 | EXPECT_EQ(sr.rows[0][0].iVal, 1); 188 | EXPECT_EQ(sr.rows[0][1].type, SimpleResultReceiver::value::Type::Real); 189 | EXPECT_EQ(sr.rows[0][1].dVal, 2.0); 190 | EXPECT_EQ(sr.rows[0][2].type, SimpleResultReceiver::value::Type::Text); 191 | EXPECT_EQ(sr.rows[0][2].text(), u"text"); 192 | EXPECT_EQ(sr.rows[1][0].type, SimpleResultReceiver::value::Type::Null); 193 | EXPECT_EQ(sr.rows[1][1].type, SimpleResultReceiver::value::Type::Blob); 194 | EXPECT_EQ(sr.rows[1][1].blob(), "ABC\0A"); 195 | EXPECT_EQ(sr.rows[1][2].type, SimpleResultReceiver::value::Type::Text); 196 | EXPECT_EQ(sr.rows[1][2].text(), u"3"); 197 | } 198 | 199 | TEST(Sqlite, ExecPreparedBind) { 200 | SqliteBase base; 201 | base.open(u":memory:"); 202 | EXPECT_EQ(base.exec(u"create table test(a, b, c)"), SQLITE_OK); 203 | auto query = base.prepare(u"insert into test values(?,?,?)"); 204 | EXPECT_TRUE(query.valid()); 205 | 206 | auto srInsert = query.bind(1, 1).bind(2, 2.0).bind(3, u"text").exec(); 207 | 208 | EXPECT_EQ(srInsert.error_, SQLITE_DONE); 209 | EXPECT_EQ(srInsert.changes_, 1ll); 210 | EXPECT_EQ(srInsert.lastId_, 1ll); 211 | 212 | query.bind(1, db_null{}).bind(2, "ABC\0A").bind(3, u"3").exec(srInsert); 213 | EXPECT_EQ(srInsert.error_, SQLITE_DONE); 214 | EXPECT_EQ(srInsert.changes_, 1ll); 215 | EXPECT_EQ(srInsert.lastId_, 2ll); 216 | 217 | query = base.prepare(u"select * from test"); 218 | EXPECT_TRUE(query.valid()); 219 | 220 | SimpleResultReceiver sr; 221 | query.exec(sr); 222 | 223 | EXPECT_EQ(sr.columns.size(), 3u); 224 | EXPECT_EQ(sr.rows.size(), 2u); 225 | EXPECT_EQ(sr.rows[0][0].type, SimpleResultReceiver::value::Type::Int); 226 | EXPECT_EQ(sr.rows[0][0].iVal, 1); 227 | EXPECT_EQ(sr.rows[0][1].type, SimpleResultReceiver::value::Type::Real); 228 | EXPECT_EQ(sr.rows[0][1].dVal, 2.0); 229 | EXPECT_EQ(sr.rows[0][2].type, SimpleResultReceiver::value::Type::Text); 230 | EXPECT_EQ(sr.rows[0][2].text(), u"text"); 231 | EXPECT_EQ(sr.rows[1][0].type, SimpleResultReceiver::value::Type::Null); 232 | EXPECT_EQ(sr.rows[1][1].type, SimpleResultReceiver::value::Type::Blob); 233 | EXPECT_EQ(sr.rows[1][1].blob(), "ABC\0A"); 234 | EXPECT_EQ(sr.rows[1][2].type, SimpleResultReceiver::value::Type::Text); 235 | EXPECT_EQ(sr.rows[1][2].text(), u"3"); 236 | } 237 | 238 | TEST(Sqlite, JulianDay) { 239 | SqliteBase base; 240 | base.open(u":memory:"); 241 | auto query = base.prepare(u"select 0.0 + strftime('%J', 'now')"); 242 | SimpleResultReceiver sr; 243 | query.exec(sr); 244 | 245 | auto _t = time(0); 246 | tm t = *gmtime(&_t); 247 | double jd = calcJulianDate(t); 248 | 249 | #ifdef _WIN32 250 | SYSTEMTIME systm; 251 | GetSystemTime(&systm); 252 | double winTime; 253 | SystemTimeToVariantTime(&systm, &winTime); 254 | tm newT = winDateToTm(winTime); 255 | EXPECT_EQ(systm.wYear - 1900, newT.tm_year); 256 | EXPECT_EQ(systm.wMonth - 1, newT.tm_mon); 257 | EXPECT_EQ(systm.wDay, newT.tm_mday); 258 | EXPECT_EQ(systm.wHour, newT.tm_hour); 259 | EXPECT_EQ(systm.wMinute, newT.tm_min); 260 | //EXPECT_EQ(systm.wSecond, newT.tm_sec); 261 | 262 | winTime = calcJulianDate(winTime); 263 | EXPECT_LT(fabs(winTime - jd), 0.001); 264 | 265 | #endif 266 | 267 | EXPECT_EQ(sr.rows.size(), 1u); 268 | EXPECT_EQ(sr.rows[0][0].type, SimpleResultReceiver::value::Type::Real); 269 | EXPECT_LT(fabs(sr.rows[0][0].dVal - jd), 0.001); 270 | 271 | 272 | } 273 | 274 | TEST(Sqlite, JsonBase64) { 275 | EXPECT_EQ(lstringu<20>{expr_str_base64("a")}, u"YQ=="); 276 | EXPECT_EQ(lstringu<20>{expr_str_base64("ab")}, u"YWI="); 277 | EXPECT_EQ(lstringu<20>{expr_str_base64("abc")}, u"YWJj"); 278 | lstringu<20> t{expr_str_base64("ABC")}; 279 | EXPECT_EQ(t, u"QUJD"); 280 | 281 | EXPECT_EQ(lstringu<20>{expr_json_str(u"abc\n\"\r")}, u"abc\\n\\\"\\r"); 282 | } 283 | 284 | TEST(Sqlite, Unicode) { 285 | SqliteBase base; 286 | base.open(u":memory:"); 287 | { 288 | SimpleResultReceiver srr; 289 | base.prepare(u"select upper('РусскийТекст')").exec(srr); 290 | EXPECT_EQ(srr.rows[0][0].type, SimpleResultReceiver::value::Type::Text); 291 | EXPECT_EQ(srr.rows[0][0].text(), u"РУССКИЙТЕКСТ"); 292 | } 293 | { 294 | SimpleResultReceiver srr; 295 | base.prepare(u"select lower('РусскийТекст')").exec(srr); 296 | EXPECT_EQ(srr.rows[0][0].type, SimpleResultReceiver::value::Type::Text); 297 | EXPECT_EQ(srr.rows[0][0].text(), u"русскийтекст"); 298 | } 299 | { 300 | SimpleResultReceiver srr; 301 | base.prepare(u"select 'РусскийТекст' collate nocase ='рУсскИЙтекст'").exec(srr); 302 | EXPECT_EQ(srr.rows[0][0].type, SimpleResultReceiver::value::Type::Int); 303 | EXPECT_EQ(srr.rows[0][0].iVal, 1); 304 | } 305 | { 306 | SimpleResultReceiver srr; 307 | base.prepare(u"select unaccent('āăąēîïĩíĝġńñšŝśûůŷё')").exec(srr); 308 | EXPECT_EQ(srr.rows[0][0].type, SimpleResultReceiver::value::Type::Text); 309 | stringu tt = srr.rows[0][0].text(); 310 | EXPECT_EQ(srr.rows[0][0].text(), u"aaaeiiiiggnnsssuuyе"); 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /src/tests/v8sqlite.epf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orefkov/v8sqlite/5c81e335b882cd2d90af16448816d0834b7968cd/src/tests/v8sqlite.epf -------------------------------------------------------------------------------- /src/v8sqlite.def: -------------------------------------------------------------------------------- 1 | LIBRARY "v8sqlite.dll" 2 | 3 | EXPORTS 4 | GetClassObject 5 | DestroyObject 6 | GetClassNames 7 | SetPlatformCapabilities 8 | GetAttachType 9 | -------------------------------------------------------------------------------- /src/v8sqlite.rc: -------------------------------------------------------------------------------- 1 | #pragma code_page(65001) 2 | 3 | // Microsoft Visual C++ generated resource script. 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // Russian (Russia) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) 19 | LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT 20 | 21 | #ifdef APSTUDIO_INVOKED 22 | ///////////////////////////////////////////////////////////////////////////// 23 | // 24 | // TEXTINCLUDE 25 | 26 | 27 | 2 TEXTINCLUDE 28 | BEGIN 29 | "#include ""winres.h""\r\n" 30 | "\0" 31 | END 32 | 33 | 3 TEXTINCLUDE 34 | BEGIN 35 | "\r\n" 36 | "\0" 37 | END 38 | 39 | #endif // APSTUDIO_INVOKED 40 | 41 | #include "version.h" 42 | #include "version_str.h" 43 | 44 | ///////////////////////////////////////////////////////////////////////////// 45 | // 46 | // Version 47 | // 48 | 49 | VS_VERSION_INFO VERSIONINFO 50 | FILEVERSION F_VERSION 51 | PRODUCTVERSION F_VERSION 52 | FILEFLAGSMASK 0x3fL 53 | #ifdef _DEBUG 54 | FILEFLAGS 0x1L 55 | #else 56 | FILEFLAGS 0x0L 57 | #endif 58 | FILEOS 0x40004L 59 | FILETYPE 0x2L 60 | FILESUBTYPE 0x0L 61 | BEGIN 62 | BLOCK "StringFileInfo" 63 | BEGIN 64 | BLOCK "041904b0" 65 | BEGIN 66 | VALUE "CompanyName", "Александр Орефков" 67 | VALUE "FileDescription", "ВК 1С8 для работы с sqlite" 68 | VALUE "FileVersion", P_VERSION 69 | VALUE "InternalName", "v8sqlite.dll" 70 | VALUE "LegalCopyright", COPY_RIGHT 71 | VALUE "OriginalFilename", "v8sqlite.dll" 72 | VALUE "ProductName", "Проект ""V8sqlite""" 73 | VALUE "ProductVersion", P_VERSION 74 | END 75 | END 76 | BLOCK "VarFileInfo" 77 | BEGIN 78 | VALUE "Translation", 0x419, 1200 79 | END 80 | END 81 | 82 | 83 | 84 | #endif // Russian (Russia) resources 85 | ///////////////////////////////////////////////////////////////////////////// 86 | 87 | 88 | 89 | #ifndef APSTUDIO_INVOKED 90 | ///////////////////////////////////////////////////////////////////////////// 91 | // 92 | // Generated from the TEXTINCLUDE 3 resource. 93 | // 94 | 95 | 96 | ///////////////////////////////////////////////////////////////////////////// 97 | #endif // not APSTUDIO_INVOKED 98 | -------------------------------------------------------------------------------- /src/v8sqlite_addin.cpp: -------------------------------------------------------------------------------- 1 | #include "v8sqlite_addin.h" 2 | #include "stdafx.h" 3 | #include 4 | 5 | V8SqliteAddin::V8SqliteAddin() = default; 6 | 7 | V8SqliteAddin::~V8SqliteAddin() = default; 8 | 9 | bool V8SqliteAddin::OpenDataBase(tVariant* params, unsigned count) { 10 | if (params[0].vt != VTYPE_PWSTR) { 11 | return error(u"Ожидается строковый параметр", u"String parameter needed"); 12 | } 13 | db_.open(varToTextU(params[0])); 14 | if (!db_.isOpen()) { 15 | return reportDbError(); 16 | } 17 | lastError_.make_empty(); 18 | return true; 19 | } 20 | 21 | bool V8SqliteAddin::CloseDataBase(tVariant* params, unsigned count) { 22 | prepared_.clear(); 23 | db_.close(); 24 | lastError_.make_empty(); 25 | return true; 26 | } 27 | 28 | bool V8SqliteAddin::Exec(tVariant* params, unsigned count) { 29 | if (!db_.isOpen()) { 30 | return error(u"База данных не открыта", u"Database not opened"); 31 | } 32 | if (params[0].vt != VTYPE_PWSTR) { 33 | return error(u"Ожидается строковый параметр", u"String parameter needed"); 34 | } 35 | lastError_.make_empty(); 36 | return db_.exec(varToTextU(params[0])) == SQLITE_OK || reportDbError(); 37 | } 38 | 39 | bool V8SqliteAddin::PrepareQuery(tVariant* params, unsigned count) { 40 | if (!db_.isOpen()) { 41 | return error(u"База данных не открыта", u"Database not opened"); 42 | } 43 | if (params[0].vt != VTYPE_PWSTR) { 44 | return error(u"Первый параметр должен быть строкой", u"The first parameter must be a string"); 45 | } 46 | if (params[1].vt != VTYPE_PWSTR) { 47 | return error(u"Второй параметр должен быть строкой", u"The second parameter must be a string"); 48 | } 49 | auto query = db_.prepare(varToTextU(params[1])); 50 | if (!query.valid()) { 51 | return reportDbError(); 52 | } 53 | prepared_.emplace(varToTextU(params[0]), std::move(query)); 54 | lastError_.make_empty(); 55 | return true; 56 | } 57 | 58 | static bool makeBind(SqliteQuery& query, tVariant& param, unsigned paramNum) { 59 | switch (param.vt) { 60 | case VTYPE_EMPTY: 61 | case VTYPE_NULL: 62 | query.bind(paramNum, db_null{}); 63 | break; 64 | case VTYPE_BOOL: 65 | query.bind(paramNum, param.bVal ? 1 : 0); 66 | break; 67 | case VTYPE_I1: 68 | query.bind(paramNum, param.i8Val); 69 | break; 70 | case VTYPE_I2: 71 | query.bind(paramNum, param.shortVal); 72 | break; 73 | case VTYPE_I4: 74 | query.bind(paramNum, param.intVal); 75 | break; 76 | case VTYPE_ERROR: 77 | query.bind(paramNum, param.errCode); 78 | break; 79 | case VTYPE_I8: 80 | query.bind(paramNum, param.llVal); 81 | break; 82 | case VTYPE_UI1: 83 | query.bind(paramNum, param.ui8Val); 84 | break; 85 | case VTYPE_UI2: 86 | query.bind(paramNum, param.ushortVal); 87 | break; 88 | case VTYPE_UI4: 89 | query.bind(paramNum, param.uintVal); 90 | break; 91 | case VTYPE_UI8: 92 | query.bind(paramNum, param.ullVal); 93 | break; 94 | case VTYPE_R4: 95 | query.bind(paramNum, param.fltVal); 96 | break; 97 | case VTYPE_R8: 98 | query.bind(paramNum, param.dblVal); 99 | break; 100 | case VTYPE_PWSTR: 101 | query.bind(paramNum, (ssu)varToTextU(param)); 102 | break; 103 | case VTYPE_BLOB: 104 | case VTYPE_PSTR: 105 | query.bind(paramNum, (ssa)varToTextA(param)); 106 | break; 107 | case VTYPE_DATE: 108 | query.bind(paramNum, lstringu<30>{expr_str_tm{winDateToTm(param.date)}}.to_str()); 109 | break; 110 | case VTYPE_TM: 111 | query.bind(paramNum, lstringu<30>{expr_str_tm{param.tmVal}}.to_str()); 112 | break; 113 | default: 114 | return false; 115 | } 116 | return true; 117 | } 118 | 119 | bool V8SqliteAddin::BindParam(tVariant* params, unsigned count) { 120 | if (!db_.isOpen()) { 121 | return error(u"База данных не открыта", u"Database not opened"); 122 | } 123 | if (params[0].vt != VTYPE_PWSTR) { 124 | return error(u"Первый параметр должен быть строкой", u"The first parameter must be a string"); 125 | } 126 | if (params[1].vt != VTYPE_PWSTR && !isInteger(params[1])) { 127 | return error(u"Второй параметр должен быть строкой или целым числом", u"The second parameter must be a string or an integer"); 128 | } 129 | auto find = prepared_.find(varToTextU(params[0])); 130 | if (find == prepared_.end()) { 131 | return error(u"Запрос с таким именем не найден", u"Query with this name not found"); 132 | } 133 | 134 | SqliteQuery& query = find->second; 135 | 136 | int paramNum = params[1].vt == VTYPE_PWSTR 137 | ? sqlite3_bind_parameter_index(query, lstringa<100>{varToTextU(params[1])}) 138 | : getInteger(params[1]); 139 | 140 | if (!paramNum || paramNum > sqlite3_bind_parameter_count(query)) { 141 | return error(u"Неверный параметр запроса", u"Bad query param"); 142 | } 143 | 144 | if (!makeBind(query, params[2], paramNum)) { 145 | return error(u"Неизвестный тип параметра", u"Bad param type"); 146 | } 147 | lastError_.make_empty(); 148 | return true; 149 | } 150 | 151 | struct ToTextReceiver { 152 | chunked_string_builder& vtText; 153 | hashStrMapUIU& datesColumns; 154 | std::vector dates; 155 | unsigned currentCol{0}; 156 | unsigned colCount{0}; 157 | int error{0}; 158 | 159 | void doSetColCount(unsigned cc) { 160 | colCount = cc; 161 | dates.resize(colCount, 0); 162 | for (const auto& [d, _1]: datesColumns) { 163 | auto [colIdx, err, _2] = d.to_str().to_int(); 164 | if (err == IntConvertResult::Success && colIdx < colCount) { 165 | dates[colIdx] = 1; 166 | } 167 | } 168 | } 169 | 170 | void checkColumnForDates(ssu colName) { 171 | if (datesColumns.find(colName) != datesColumns.end()) { 172 | dates[currentCol] = 1; 173 | } 174 | } 175 | 176 | ToTextReceiver(chunked_string_builder& t, hashStrMapUIU& d) : vtText(t), datesColumns(d) {} 177 | }; 178 | 179 | struct ValueTableReceiver : ToTextReceiver { 180 | using ToTextReceiver::ToTextReceiver; 181 | 182 | unsigned rowCount{0}; 183 | unsigned startOfRowCount = 0; 184 | 185 | void setColumnCount(unsigned cc) { 186 | doSetColCount(cc); 187 | vtText << u"{\"#\",acf6192e-81ca-46ef-93a6-5a6968b78663,{9,{"_ss + cc + u","; 188 | } 189 | 190 | void addColumnName(ssu name) { 191 | checkColumnForDates(name); 192 | if (name.find('\"') != str::npos) { 193 | lstringu<200> idName{e_repl(name, u"\"", u"\"\"")}; 194 | vtText << u"{"_ss + currentCol + u",\"" + idName + u"\",{\"Pattern\"},\"" + idName + u"\",0},"; 195 | } else { 196 | vtText << u"{"_ss + currentCol + u",\"" + name + u"\",{\"Pattern\"},\"" + name + u"\",0},"; 197 | } 198 | currentCol++; 199 | } 200 | enum {SpaceForRowCount = 20}; 201 | void addRow() { 202 | if (rowCount == 0) { 203 | vtText << u"},{2,"_ss + colCount + u","; 204 | for (unsigned i = 0; i < colCount; i++) { 205 | vtText << eeu + i + u"," + i + u","; 206 | } 207 | vtText << u"{1,"; 208 | startOfRowCount = (int)vtText.length(); 209 | vtText << expr_spaces{} + u","; 210 | } else { 211 | vtText << u"0},"; 212 | } 213 | vtText << u"{2,"_ss + rowCount + u"," + currentCol + u","; 214 | currentCol = 0; 215 | rowCount++; 216 | } 217 | void addNull() { 218 | vtText << u"{\"L\"},"; 219 | currentCol++; 220 | } 221 | void addInteger(int64_t v) { 222 | vtText << u"{\"N\","_ss + v + u"},"; 223 | currentCol++; 224 | } 225 | void addReal(double v) { 226 | #ifndef __linux__ 227 | vtText << u"{\"N\","_ss + v + u"},"; 228 | #else 229 | vtText << u"{\"N\"," + lstringu<40>{lstringa<40>{eea + v}} + u"},"; 230 | #endif 231 | currentCol++; 232 | } 233 | void addText(ssu v) { 234 | if (dates[currentCol] && v.len == 19) { 235 | vtText << u"{\"D\"," + v(0, 4) + v(5, 2) + v(8, 2) + v(11, 2) + v(14, 2) + v(17, 2) + u"},"; 236 | } else { 237 | vtText << u"{\"S\",\"" + e_repl(v, u"\"", u"\"\"") + u"\"},"; 238 | } 239 | currentCol++; 240 | } 241 | void addBlob(ssa v) { 242 | vtText << u"{\"#\",87126200-3e98-44e0-b931-ccb1d7edc497,{1,{#base64:" + expr_str_base64(v) + u"}}},"; 243 | currentCol++; 244 | } 245 | void setResult(int e, sqlite3* db) { 246 | error = e; 247 | if (SQLITE_DONE == e && colCount) { 248 | if (!rowCount) { 249 | vtText << u"},{2,"_ss + colCount + u","; 250 | for (unsigned i = 0; i < colCount; i++) { 251 | vtText << eeu + i + u"," + i + u","; 252 | } 253 | vtText << u"{1,0},2,-1},{0,0}}}"; 254 | } else 255 | vtText << u"0}},1,2},{0,0}}}"; 256 | } 257 | } 258 | 259 | void fixAnswer(WCHAR_T* answer) const { 260 | if (rowCount) { 261 | fromInt(answer + startOfRowCount + SpaceForRowCount, rowCount); 262 | } 263 | } 264 | }; 265 | 266 | struct JsonReceiver : ToTextReceiver { 267 | using ToTextReceiver::ToTextReceiver; 268 | 269 | void fixAnswer(WCHAR_T* answer) {} 270 | 271 | void setColumnCount(unsigned cc) { 272 | doSetColCount(cc); 273 | vtText << uR"({"#type":"jv8:Array","#value":[{"#type":"jv8:Array","#value":[)"; 274 | } 275 | 276 | void addDelim() { 277 | currentCol++; 278 | if (currentCol == colCount) { 279 | vtText << u"]}"; 280 | } else 281 | vtText << u","; 282 | } 283 | 284 | void addColumnName(ssu name) { 285 | checkColumnForDates(name); 286 | vtText << uR"({"#type":"jxs:string","#value":")" + expr_json_str(name) + u"\"}"; 287 | addDelim(); 288 | } 289 | void addRow() { 290 | vtText << uR"(,{"#type":"jv8:Array","#value":[)"; 291 | currentCol = 0; 292 | } 293 | void addNull() { 294 | vtText << uR"({"#type":"jv8:Null","#value":""})"; 295 | addDelim(); 296 | } 297 | void addInteger(int64_t v) { 298 | vtText << uR"({"#type":"jxs:decimal","#value":)"_ss + v + u"}"; 299 | addDelim(); 300 | } 301 | void addReal(double v) { 302 | #ifndef __linux__ 303 | vtText << uR"({"#type":"jxs:decimal","#value":)"_ss + v + u"}"; 304 | #else 305 | vtText << uR"({"#type":"jxs:decimal","#value":)" + lstringu<40>{lstringa<40>{eea + v}} + u"}"; 306 | #endif 307 | addDelim(); 308 | } 309 | void addText(ssu v) { 310 | if (dates[currentCol] && v.len == 19) { 311 | vtText << uR"({"#type":"jxs:dateTime","#value":")" + v(0, 10) + u'T' + v(11) + u"\"}"; 312 | } else { 313 | vtText << uR"({"#type":"jxs:string","#value":")" + expr_json_str(v) + u"\"}"; 314 | } 315 | addDelim(); 316 | } 317 | void addBlob(ssa v) { 318 | vtText << uR"({"#type":"jxs:base64Binary","#value":")" + expr_str_base64(v) + u"\"}"; 319 | addDelim(); 320 | } 321 | void setResult(int e, sqlite3* db) { 322 | error = e; 323 | if (SQLITE_DONE == e && colCount) { 324 | vtText << u"]}"; 325 | } 326 | } 327 | }; 328 | 329 | template 330 | static bool execQuery(SqliteQuery& query, tVariant& retVal, hashStrMapUIU& dates, IMemoryManager* mm) { 331 | chunked_string_builder text; 332 | T receiver(text, dates); 333 | query.exec(receiver); 334 | if (SQLITE_DONE != receiver.error) { 335 | return false; 336 | } 337 | if (receiver.colCount) { 338 | retVal.vt = VTYPE_PWSTR; 339 | retVal.wstrLen = (int)text.length(); 340 | mm->AllocMemory((void**)&retVal.pwstrVal, (int)(text.length() + 1) * 2); 341 | 342 | *text.place(retVal.pwstrVal) = 0; 343 | 344 | receiver.fixAnswer(retVal.pwstrVal); 345 | } 346 | return true; 347 | } 348 | 349 | bool V8SqliteAddin::ExecQuery(tVariant& retVal, tVariant* params, unsigned count) { 350 | if (!db_.isOpen()) { 351 | return error(u"База данных не открыта", u"Database not opened"); 352 | } 353 | if (params[0].vt != VTYPE_PWSTR) { 354 | return error(u"Первый параметр должен быть строкой", u"First param must be string"); 355 | } 356 | if (params[1].vt != VTYPE_PWSTR) { 357 | return error(u"Второй параметр должен быть строкой", u"Second parameter must be a string"); 358 | } 359 | 360 | stru resultFormat = varToTextU(params[1]); 361 | 362 | hashStrMapUIU dates; 363 | 364 | if (params[2].vt != VTYPE_NULL) { 365 | if (params[2].vt == VTYPE_PWSTR) { 366 | auto vals = varToTextU(params[2]).splitter(u","); 367 | while (!vals.isDone()) { 368 | dates.emplace(vals.next().trimmed(), 0); 369 | } 370 | } else { 371 | return error(u"Третий параметр должен быть строкой или отсутствовать", u"Third param must be string or empty"); 372 | } 373 | } 374 | 375 | SqliteQuery tmpQuery, *query = nullptr; 376 | auto find = prepared_.find(varToTextU(params[0])); 377 | if (find == prepared_.end()) { 378 | tmpQuery = db_.prepare(varToTextU(params[0])); 379 | if (!tmpQuery.valid()) 380 | return reportDbError(); 381 | query = &tmpQuery; 382 | } else { 383 | query = &find->second; 384 | } 385 | 386 | bool result = false; 387 | lastError_.make_empty(); 388 | 389 | if (resultFormat.compare_ia(u"ValueTable") == 0 || resultFormat.compare_iu(u"ТаблицаЗначений") == 0) { 390 | result = execQuery(*query, retVal, dates, memoryManager_); 391 | } else if (resultFormat.compare_ia(u"JSON") == 0) { 392 | result = execQuery(*query, retVal, dates, memoryManager_); 393 | } else { 394 | return error(u"Неизвестный формат для результата", u"Unknown result format"); 395 | } 396 | return result || reportDbError(); 397 | } 398 | 399 | bool V8SqliteAddin::RemoveQuery(tVariant* params, unsigned count) { 400 | if (params[0].vt != VTYPE_PWSTR) { 401 | return error(u"Первый параметр должен быть строкой", u"First param must be string"); 402 | } 403 | auto find = prepared_.find(varToTextU(params[0])); 404 | if (find == prepared_.end()) { 405 | return error(u"Запрос с таким именем не найден", u"No query found for that name"); 406 | } 407 | prepared_.erase(find); 408 | lastError_.make_empty(); 409 | return true; 410 | } 411 | 412 | bool V8SqliteAddin::reportDbError() { 413 | return error(selectLocaleStr(u"Произошла ошибка базы данных", u"Database error") + u": " + db_.lastError()); 414 | } 415 | -------------------------------------------------------------------------------- /src/v8sqlite_addin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "addin_base.h" 3 | #include "sqlite.h" 4 | #include "version.h" 5 | 6 | #define ADDIN_NAME u"v8sqlite" 7 | 8 | class V8SqliteAddin : public V8Addin { 9 | protected: 10 | 11 | SqliteBase db_; 12 | hashStrMapUIU prepared_; 13 | 14 | bool reportDbError(); 15 | 16 | public: 17 | V8SqliteAddin(); 18 | virtual ~V8SqliteAddin(); 19 | 20 | REGISTER_EXTENSION(ADDIN_NAME); 21 | EXT_PROP_RO(Version, u"Версия") { 22 | lstringu<40> v{ssa{P_VERSION}}; 23 | value.vt = VTYPE_PWSTR; 24 | value.pwstrVal = copyText(v); 25 | value.wstrLen = (int)v.length(); 26 | return true; 27 | } 28 | 29 | EXT_PROP_RW(ThrowErrorDescription, u"БросатьОписаниеОшибки") { 30 | value.vt = VTYPE_BOOL; 31 | value.bVal = throwErrors_; 32 | return true; 33 | } 34 | EXT_PROP_WRITE(ThrowErrorDescription) { 35 | if (value.vt == VTYPE_BOOL) { 36 | throwErrors_ = value.bVal; 37 | return true; 38 | } 39 | return error(u"Ожидается булево значение", u"Boolean value needed"); 40 | } 41 | 42 | EXT_PROP_RO(ErrorDescription, u"ОписаниеОшибки") { 43 | value.vt = VTYPE_PWSTR; 44 | value.pwstrVal = copyText(lastError_); 45 | value.wstrLen = (int)lastError_.length(); 46 | return true; 47 | } 48 | 49 | EXT_PROP_RO(isDataBaseOpen, u"БазаДанныхОткрыта") { 50 | value.vt = VTYPE_BOOL; 51 | value.bVal = db_.isOpen(); 52 | return true; 53 | } 54 | EXT_PROP_RO(LastId, u"ПоследнийИд") { 55 | // 1С почему-то не может принять число VTYPE_I8; 56 | int64_t lastId = db_.lastId(); 57 | if (lastId > INT_MAX || lastId < INT_MIN ) { 58 | value.vt = VTYPE_R8; 59 | value.dblVal = (double)lastId; 60 | } else { 61 | value.vt = VTYPE_I4; 62 | value.intVal = (int)lastId; 63 | } 64 | return true; 65 | } 66 | EXT_PROP_RO(Changes, u"СтрокИзменено") { 67 | // 1С почему-то не может принять число VTYPE_I8; 68 | int64_t c = db_.changes(); 69 | if (c > INT_MAX || c < INT_MIN) { 70 | value.vt = VTYPE_R8; 71 | value.dblVal = (double)c; 72 | } else { 73 | value.vt = VTYPE_I4; 74 | value.intVal = (int)c; 75 | } 76 | return true; 77 | } 78 | 79 | EXT_PROC(OpenDataBase, u"ОткрытьБазуДанных", 1); 80 | EXT_PROC(CloseDataBase, u"ЗакрытьБазуДанных", 0); 81 | EXT_PROC(Exec, u"Выполнить", 1); 82 | EXT_PROC(PrepareQuery, u"ПодготовитьЗапрос", 2); 83 | EXT_PROC_WITH_DEFVAL(BindParam, u"УстановитьПараметр", 3); 84 | EXT_DEFVAL_FOR(BindParam) { 85 | if (param >= 2) { 86 | if (value) { 87 | value->vt = VTYPE_NULL; 88 | } 89 | } 90 | return true; 91 | } 92 | EXT_FUNC_WITH_DEFVAL(ExecQuery, u"ВыполнитьЗапрос", 3); 93 | EXT_DEFVAL_FOR(ExecQuery) { 94 | if (param >= 2) { 95 | if (value) { 96 | value->vt = VTYPE_NULL; 97 | } 98 | } 99 | return true; 100 | } 101 | EXT_PROC(RemoveQuery, u"УдалитьЗапрос", 1); 102 | 103 | END_EXTENSION(); 104 | }; 105 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | #define F_VERSION 1,0,0,8 2 | #define P_VERSION "1.0.0.8" 3 | -------------------------------------------------------------------------------- /src/version.script: -------------------------------------------------------------------------------- 1 | { 2 | global: 3 | *DllMain*; 4 | *GetClassObject*; 5 | *DestroyObject*; 6 | *GetClassNames*; 7 | *SetPlatformCapabilities*; 8 | *GetAttachType*; 9 | local: 10 | *; 11 | }; 12 | -------------------------------------------------------------------------------- /src/version_str.h: -------------------------------------------------------------------------------- 1 | #define COPY_RIGHT "© Александр Орефков, 2025" 2 | --------------------------------------------------------------------------------