├── bin └── winx64 │ ├── node20 │ └── mg-dbx.node │ ├── node22 │ └── mg-dbx.node │ └── node24 │ └── mg-dbx.node ├── binding.gyp ├── package.json ├── src ├── mg-cursor.h ├── mg-class.h ├── mg-global.h ├── mg-net.h ├── mg-class.cpp ├── mg-cursor.cpp └── mg-net.cpp ├── LICENSE └── README.md /bin/winx64/node20/mg-dbx.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisemunt/mg-dbx/HEAD/bin/winx64/node20/mg-dbx.node -------------------------------------------------------------------------------- /bin/winx64/node22/mg-dbx.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisemunt/mg-dbx/HEAD/bin/winx64/node22/mg-dbx.node -------------------------------------------------------------------------------- /bin/winx64/node24/mg-dbx.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisemunt/mg-dbx/HEAD/bin/winx64/node24/mg-dbx.node -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "mg-dbx", 5 | "sources": [ 6 | "src/mg-dbx.cpp", 7 | "src/mg-global.cpp", 8 | "src/mg-cursor.cpp", 9 | "src/mg-class.cpp", 10 | "src/mg-net.cpp" 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Chris Munt (http://www.gateway.com/)", 3 | "name": "mg-dbx", 4 | "description": "High speed Synchronous and Asynchronous access to InterSystems Cache/IRIS and YottaDB from Node.js.", 5 | "version": "2.4.31", 6 | "maintainers": "Chris Munt ", 7 | "homepage": "https://github.com/chrisemunt/mg-dbx", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/chrisemunt/mg-dbx.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/chrisemunt/mg-dbx/issues" 14 | }, 15 | "os": [ 16 | "linux", 17 | "darwin", 18 | "win32" 19 | ], 20 | "keywords": [ 21 | "nosql", 22 | "intersystems", 23 | "cache", 24 | "iris", 25 | "yottadb", 26 | "m", 27 | "mumps" 28 | ], 29 | "scripts": { 30 | "install": "node-gyp rebuild", 31 | "test": "echo \"Error: no test specified\" && exit 1" 32 | }, 33 | "main": "./build/Release/mg-dbx", 34 | "license": "Apache-2.0", 35 | "engines": { 36 | "node": ">=8.0.x" 37 | }, 38 | "gypfile": true, 39 | "directories": { 40 | "doc": "doc" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/mg-cursor.h: -------------------------------------------------------------------------------- 1 | /* 2 | ---------------------------------------------------------------------------- 3 | | mg-dbx.node | 4 | | Author: Chris Munt cmunt@mgateway.com | 5 | | chris.e.munt@gmail.com | 6 | | Copyright (c) 2019-2026 MGateway Ltd | 7 | | Surrey UK. | 8 | | All rights reserved. | 9 | | | 10 | | http://www.mgateway.com | 11 | | | 12 | | Licensed under the Apache License, Version 2.0 (the "License"); you may | 13 | | not use this file except in compliance with the License. | 14 | | You may obtain a copy of the License at | 15 | | | 16 | | http://www.apache.org/licenses/LICENSE-2.0 | 17 | | | 18 | | Unless required by applicable law or agreed to in writing, software | 19 | | distributed under the License is distributed on an "AS IS" BASIS, | 20 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 21 | | See the License for the specific language governing permissions and | 22 | | limitations under the License. | 23 | | | 24 | ---------------------------------------------------------------------------- 25 | */ 26 | 27 | #ifndef MG_CURSOR_H 28 | #define MG_CURSOR_H 29 | 30 | #define MG_CURSOR_CHECK_CLASS(a) \ 31 | if (a->c == NULL) { \ 32 | v8::Isolate* isolatex = args.GetIsolate(); \ 33 | isolatex->ThrowException(v8::Exception::Error(dbx_new_string8(isolatex, (char *) "Error in the instantiation of the mcursor class", 1))); \ 34 | return; \ 35 | } \ 36 | 37 | class mcursor : public node::ObjectWrap 38 | { 39 | public: 40 | 41 | int dbx_count; 42 | short context; 43 | short getdata; 44 | short multilevel; 45 | short format; 46 | int counter; 47 | char global_name[256]; 48 | int global_name_len; 49 | unsigned short global_name16[256]; 50 | int global_name16_len; 51 | DBXQR *pqr_prev; 52 | DBXQR *pqr_next; 53 | DBXSTR data; 54 | DBXSQL *psql; 55 | DBX_DBNAME *c; 56 | 57 | 58 | #if DBX_NODE_VERSION >= 100000 59 | static void Init (v8::Local exports); 60 | #else 61 | static void Init (v8::Handle exports); 62 | #endif 63 | explicit mcursor (int value = 0); 64 | ~mcursor (); 65 | 66 | static mcursor * NewInstance (const v8::FunctionCallbackInfo& args); 67 | static int async_callback (mcursor *cx); 68 | static int delete_mcursor_template (mcursor *cx); 69 | 70 | static void Execute (const v8::FunctionCallbackInfo& args); 71 | static void Cleanup (const v8::FunctionCallbackInfo& args); 72 | static void Next (const v8::FunctionCallbackInfo& args); 73 | static void Previous (const v8::FunctionCallbackInfo& args); 74 | static void Reset (const v8::FunctionCallbackInfo& args); 75 | static void Close (const v8::FunctionCallbackInfo& args); 76 | 77 | private: 78 | 79 | static void New (const v8::FunctionCallbackInfo& args); 80 | static v8::Persistent constructor; 81 | }; 82 | 83 | int dbx_escape_output (DBXSTR *pdata, char *item, int item_len, short context); 84 | int dbx_escape_output16 (DBXSTR *pdata, unsigned short *item, int item_len, short context); 85 | 86 | #endif 87 | 88 | -------------------------------------------------------------------------------- /src/mg-class.h: -------------------------------------------------------------------------------- 1 | /* 2 | ---------------------------------------------------------------------------- 3 | | mg-dbx.node | 4 | | Author: Chris Munt cmunt@mgateway.com | 5 | | chris.e.munt@gmail.com | 6 | | Copyright (c) 2019-2026 MGateway Ltd | 7 | | Surrey UK. | 8 | | All rights reserved. | 9 | | | 10 | | http://www.mgateway.com | 11 | | | 12 | | Licensed under the Apache License, Version 2.0 (the "License"); you may | 13 | | not use this file except in compliance with the License. | 14 | | You may obtain a copy of the License at | 15 | | | 16 | | http://www.apache.org/licenses/LICENSE-2.0 | 17 | | | 18 | | Unless required by applicable law or agreed to in writing, software | 19 | | distributed under the License is distributed on an "AS IS" BASIS, | 20 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 21 | | See the License for the specific language governing permissions and | 22 | | limitations under the License. | 23 | | | 24 | ---------------------------------------------------------------------------- 25 | */ 26 | 27 | #ifndef MG_CLASS_H 28 | #define MG_CLASS_H 29 | 30 | #define MG_CLASS_CHECK_CLASS(a) \ 31 | if (a->c == NULL) { \ 32 | v8::Isolate* isolatex = args.GetIsolate(); \ 33 | isolatex->ThrowException(v8::Exception::Error(dbx_new_string8(isolatex, (char *) "Error in the instantiation of the mclass class", 1))); \ 34 | return; \ 35 | } \ 36 | 37 | class mclass : public node::ObjectWrap 38 | { 39 | public: 40 | 41 | 42 | int dbx_count; 43 | int oref; 44 | char class_name[256]; 45 | int class_name_len; 46 | unsigned short class_name16[256]; 47 | int class_name16_len; 48 | DBX_DBNAME *c; 49 | 50 | 51 | #if DBX_NODE_VERSION >= 100000 52 | static void Init (v8::Local exports); 53 | #else 54 | static void Init (v8::Handle exports); 55 | #endif 56 | explicit mclass (int value = 0); 57 | ~mclass (); 58 | 59 | static mclass * NewInstance (const v8::FunctionCallbackInfo& args); 60 | static int async_callback (mclass *clx); 61 | static int delete_mclass_template (mclass *clx); 62 | 63 | static void ClassMethod (const v8::FunctionCallbackInfo& args); 64 | static void ClassMethod_bx (const v8::FunctionCallbackInfo& args); 65 | static void ClassMethodEx (const v8::FunctionCallbackInfo& args, int binary); 66 | static void Method (const v8::FunctionCallbackInfo& args); 67 | static void Method_bx (const v8::FunctionCallbackInfo& args); 68 | static void MethodEx (const v8::FunctionCallbackInfo& args, int binary); 69 | static void SetProperty (const v8::FunctionCallbackInfo& args); 70 | static void GetProperty (const v8::FunctionCallbackInfo& args); 71 | static void GetProperty_bx (const v8::FunctionCallbackInfo& args); 72 | static void GetPropertyEx (const v8::FunctionCallbackInfo& args, int binary); 73 | static void Reset (const v8::FunctionCallbackInfo& args); 74 | static void Close (const v8::FunctionCallbackInfo& args); 75 | 76 | private: 77 | 78 | static void New (const v8::FunctionCallbackInfo& args); 79 | static v8::Persistent constructor; 80 | }; 81 | 82 | #endif 83 | 84 | -------------------------------------------------------------------------------- /src/mg-global.h: -------------------------------------------------------------------------------- 1 | /* 2 | ---------------------------------------------------------------------------- 3 | | mg-dbx.node | 4 | | Author: Chris Munt cmunt@mgateway.com | 5 | | chris.e.munt@gmail.com | 6 | | Copyright (c) 2019-2026 MGateway Ltd | 7 | | Surrey UK. | 8 | | All rights reserved. | 9 | | | 10 | | http://www.mgateway.com | 11 | | | 12 | | Licensed under the Apache License, Version 2.0 (the "License"); you may | 13 | | not use this file except in compliance with the License. | 14 | | You may obtain a copy of the License at | 15 | | | 16 | | http://www.apache.org/licenses/LICENSE-2.0 | 17 | | | 18 | | Unless required by applicable law or agreed to in writing, software | 19 | | distributed under the License is distributed on an "AS IS" BASIS, | 20 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 21 | | See the License for the specific language governing permissions and | 22 | | limitations under the License. | 23 | | | 24 | ---------------------------------------------------------------------------- 25 | */ 26 | 27 | #ifndef MG_GLOBAL_H 28 | #define MG_GLOBAL_H 29 | 30 | #define MG_GLOBAL_CHECK_CLASS(a) \ 31 | if (a->c == NULL) { \ 32 | v8::Isolate* isolatex = args.GetIsolate(); \ 33 | isolatex->ThrowException(v8::Exception::Error(dbx_new_string8(isolatex, (char *) "Error in the instantiation of the mglobal class", 1))); \ 34 | return; \ 35 | } \ 36 | 37 | class mglobal : public node::ObjectWrap 38 | { 39 | public: 40 | 41 | int dbx_count; 42 | char global_name[256]; 43 | int global_name_len; 44 | unsigned short global_name16[256]; 45 | int global_name16_len; 46 | DBXVAL *pkey; 47 | DBX_DBNAME *c; 48 | 49 | static v8::Persistent constructor; 50 | 51 | #if DBX_NODE_VERSION >= 100000 52 | static void Init (v8::Local exports); 53 | #else 54 | static void Init (v8::Handle exports); 55 | #endif 56 | explicit mglobal (int value = 0); 57 | ~mglobal (); 58 | 59 | static mglobal * NewInstance (const v8::FunctionCallbackInfo& args); 60 | static int async_callback (mglobal *gx); 61 | static int delete_mglobal_template (mglobal *gx); 62 | 63 | static void Get (const v8::FunctionCallbackInfo& args); 64 | static void Get_bx (const v8::FunctionCallbackInfo& args); 65 | static void GetEx (const v8::FunctionCallbackInfo& args, int binary); 66 | static void Set (const v8::FunctionCallbackInfo& args); 67 | static void Defined (const v8::FunctionCallbackInfo& args); 68 | static void Delete (const v8::FunctionCallbackInfo& args); 69 | static void Next (const v8::FunctionCallbackInfo& args); 70 | static void Previous (const v8::FunctionCallbackInfo& args); 71 | static void Increment (const v8::FunctionCallbackInfo& args); 72 | static void Lock (const v8::FunctionCallbackInfo& args); 73 | static void Unlock (const v8::FunctionCallbackInfo& args); 74 | static void Merge (const v8::FunctionCallbackInfo& args); 75 | static void Reset (const v8::FunctionCallbackInfo& args); 76 | static void Close (const v8::FunctionCallbackInfo& args); 77 | 78 | private: 79 | 80 | static void New (const v8::FunctionCallbackInfo& args); 81 | }; 82 | 83 | 84 | #endif 85 | 86 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/mg-net.h: -------------------------------------------------------------------------------- 1 | /* 2 | ---------------------------------------------------------------------------- 3 | | mg-dbx.node | 4 | | Author: Chris Munt cmunt@mgateway.com | 5 | | chris.e.munt@gmail.com | 6 | | Copyright (c) 2019-2026 MGateway Ltd | 7 | | Surrey UK. | 8 | | All rights reserved. | 9 | | | 10 | | http://www.mgateway.com | 11 | | | 12 | | Licensed under the Apache License, Version 2.0 (the "License"); you may | 13 | | not use this file except in compliance with the License. | 14 | | You may obtain a copy of the License at | 15 | | | 16 | | http://www.apache.org/licenses/LICENSE-2.0 | 17 | | | 18 | | Unless required by applicable law or agreed to in writing, software | 19 | | distributed under the License is distributed on an "AS IS" BASIS, | 20 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 21 | | See the License for the specific language governing permissions and | 22 | | limitations under the License. | 23 | | | 24 | ---------------------------------------------------------------------------- 25 | */ 26 | 27 | #ifndef MG_NET_H 28 | #define MG_NET_H 29 | 30 | #define NETX_TIMEOUT 10 31 | #define NETX_IPV6 1 32 | #define NETX_READ_EOF 0 33 | #define NETX_READ_NOCON -1 34 | #define NETX_READ_ERROR -2 35 | #define NETX_READ_TIMEOUT -3 36 | #define NETX_RECV_BUFFER 32768 37 | 38 | 39 | #if defined(LINUX) 40 | #define NETX_MEMCPY(a,b,c) memmove(a,b,c) 41 | #else 42 | #define NETX_MEMCPY(a,b,c) memcpy(a,b,c) 43 | #endif 44 | 45 | /* 46 | typedef void * (* MG_MALLOC) (unsigned long size); 47 | typedef void * (* MG_REALLOC) (void *p, unsigned long size); 48 | typedef int (* MG_FREE) (void *p); 49 | */ 50 | 51 | #if defined(_WIN32) 52 | 53 | #if defined(MG_DBA_DSO) 54 | #define DBX_EXTFUN(a) __declspec(dllexport) a __cdecl 55 | #else 56 | #define DBX_EXTFUN(a) a 57 | #endif 58 | 59 | #define NETX_WSASOCKET netx_so.p_WSASocket 60 | #define NETX_WSAGETLASTERROR netx_so.p_WSAGetLastError 61 | #define NETX_WSASTARTUP netx_so.p_WSAStartup 62 | #define NETX_WSACLEANUP netx_so.p_WSACleanup 63 | #define NETX_WSAFDISET netx_so.p_WSAFDIsSet 64 | #define NETX_WSARECV netx_so.p_WSARecv 65 | #define NETX_WSASEND netx_so.p_WSASend 66 | 67 | #define NETX_WSASTRINGTOADDRESS netx_so.p_WSAStringToAddress 68 | #define NETX_WSAADDRESSTOSTRING netx_so.p_WSAAddressToString 69 | #define NETX_GETADDRINFO netx_so.p_getaddrinfo 70 | #define NETX_FREEADDRINFO netx_so.p_freeaddrinfo 71 | #define NETX_GETNAMEINFO netx_so.p_getnameinfo 72 | #define NETX_GETPEERNAME netx_so.p_getpeername 73 | #define NETX_INET_NTOP netx_so.p_inet_ntop 74 | #define NETX_INET_PTON netx_so.p_inet_pton 75 | 76 | #define NETX_CLOSESOCKET netx_so.p_closesocket 77 | #define NETX_GETHOSTNAME netx_so.p_gethostname 78 | #define NETX_GETHOSTBYNAME netx_so.p_gethostbyname 79 | #define NETX_SETSERVBYNAME netx_so.p_getservbyname 80 | #define NETX_GETHOSTBYADDR netx_so.p_gethostbyaddr 81 | #define NETX_HTONS netx_so.p_htons 82 | #define NETX_HTONL netx_so.p_htonl 83 | #define NETX_NTOHL netx_so.p_ntohl 84 | #define NETX_NTOHS netx_so.p_ntohs 85 | #define NETX_CONNECT netx_so.p_connect 86 | #define NETX_INET_ADDR netx_so.p_inet_addr 87 | #define NETX_INET_NTOA netx_so.p_inet_ntoa 88 | #define NETX_SOCKET netx_so.p_socket 89 | #define NETX_SETSOCKOPT netx_so.p_setsockopt 90 | #define NETX_GETSOCKOPT netx_so.p_getsockopt 91 | #define NETX_GETSOCKNAME netx_so.p_getsockname 92 | #define NETX_SELECT netx_so.p_select 93 | #define NETX_RECV netx_so.p_recv 94 | #define NETX_SEND netx_so.p_send 95 | #define NETX_SHUTDOWN netx_so.p_shutdown 96 | #define NETX_BIND netx_so.p_bind 97 | #define NETX_LISTEN netx_so.p_listen 98 | #define NETX_ACCEPT netx_so.p_accept 99 | 100 | #define NETX_FD_ISSET(fd, set) netx_so.p_WSAFDIsSet((SOCKET)(fd), (fd_set *)(set)) 101 | 102 | typedef int (WINAPI * MG_LPFN_WSAFDISSET) (SOCKET, fd_set *); 103 | 104 | typedef DWORD DBXTHID; 105 | typedef HINSTANCE DBXPLIB; 106 | typedef FARPROC DBXPROC; 107 | 108 | typedef LPSOCKADDR xLPSOCKADDR; 109 | typedef u_long *xLPIOCTL; 110 | typedef const char *xLPSENDBUF; 111 | typedef char *xLPRECVBUF; 112 | 113 | #ifdef _WIN64 114 | typedef int socklen_netx; 115 | #else 116 | typedef size_t socklen_netx; 117 | #endif 118 | 119 | #define SOCK_ERROR(n) (n == SOCKET_ERROR) 120 | #define INVALID_SOCK(n) (n == INVALID_SOCKET) 121 | #define NOT_BLOCKING(n) (n != WSAEWOULDBLOCK) 122 | 123 | #define BZERO(b,len) (memset((b), '\0', (len)), (void) 0) 124 | 125 | #else /* #if defined(_WIN32) */ 126 | 127 | #define DBX_EXTFUN(a) a 128 | 129 | #define NETX_WSASOCKET WSASocket 130 | #define NETX_WSAGETLASTERROR WSAGetLastError 131 | #define NETX_WSASTARTUP WSAStartup 132 | #define NETX_WSACLEANUP WSACleanup 133 | #define NETX_WSAFDIsSet WSAFDIsSet 134 | #define NETX_WSARECV WSARecv 135 | #define NETX_WSASEND WSASend 136 | 137 | #define NETX_WSASTRINGTOADDRESS WSAStringToAddress 138 | #define NETX_WSAADDRESSTOSTRING WSAAddressToString 139 | #define NETX_GETADDRINFO getaddrinfo 140 | #define NETX_FREEADDRINFO freeaddrinfo 141 | #define NETX_GETNAMEINFO getnameinfo 142 | #define NETX_GETPEERNAME getpeername 143 | #define NETX_INET_NTOP inet_ntop 144 | #define NETX_INET_PTON inet_pton 145 | 146 | #define NETX_CLOSESOCKET closesocket 147 | #define NETX_GETHOSTNAME gethostname 148 | #define NETX_GETHOSTBYNAME gethostbyname 149 | #define NETX_SETSERVBYNAME getservbyname 150 | #define NETX_GETHOSTBYADDR gethostbyaddr 151 | #define NETX_HTONS htons 152 | #define NETX_HTONL htonl 153 | #define NETX_NTOHL ntohl 154 | #define NETX_NTOHS ntohs 155 | #define NETX_CONNECT connect 156 | #define NETX_INET_ADDR inet_addr 157 | #define NETX_INET_NTOA inet_ntoa 158 | #define NETX_SOCKET socket 159 | #define NETX_SETSOCKOPT setsockopt 160 | #define NETX_GETSOCKOPT getsockopt 161 | #define NETX_GETSOCKNAME getsockname 162 | #define NETX_SELECT select 163 | #define NETX_RECV recv 164 | #define NETX_SEND send 165 | #define NETX_SHUTDOWN shutdown 166 | #define NETX_BIND bind 167 | #define NETX_LISTEN listen 168 | #define NETX_ACCEPT accept 169 | 170 | #define NETX_FD_ISSET(fd, set) FD_ISSET(fd, set) 171 | 172 | typedef int WSADATA; 173 | typedef struct sockaddr SOCKADDR; 174 | typedef struct sockaddr * LPSOCKADDR; 175 | typedef struct hostent HOSTENT; 176 | typedef struct hostent * LPHOSTENT; 177 | typedef struct servent SERVENT; 178 | typedef struct servent * LPSERVENT; 179 | 180 | #ifdef NETX_BS_GEN_PTR 181 | typedef const void * xLPSOCKADDR; 182 | typedef void * xLPIOCTL; 183 | typedef const void * xLPSENDBUF; 184 | typedef void * xLPRECVBUF; 185 | #else 186 | typedef LPSOCKADDR xLPSOCKADDR; 187 | typedef char * xLPIOCTL; 188 | typedef const char * xLPSENDBUF; 189 | typedef char * xLPRECVBUF; 190 | #endif /* #ifdef NETX_BS_GEN_PTR */ 191 | 192 | #if defined(OSF1) || defined(HPUX) || defined(HPUX10) || defined(HPUX11) 193 | typedef int socklen_netx; 194 | #elif defined(LINUX) || defined(AIX) || defined(AIX5) || defined(MACOSX) 195 | typedef socklen_t socklen_netx; 196 | #else 197 | typedef size_t socklen_netx; 198 | #endif 199 | 200 | #ifndef INADDR_NONE 201 | #define INADDR_NONE -1 202 | #endif 203 | 204 | #define SOCK_ERROR(n) (n < 0) 205 | #define INVALID_SOCK(n) (n < 0) 206 | #define NOT_BLOCKING(n) (n != EWOULDBLOCK && n != 2) 207 | 208 | #define BZERO(b, len) (bzero(b, len)) 209 | 210 | #endif /* #if defined(_WIN32) */ 211 | 212 | 213 | 214 | #if defined(__MINGW32__) 215 | 216 | typedef INT (WSAAPI * LPFN_INET_PTONA) (INT Family, PCSTR pszAddrString, PVOID pAddrBuf); 217 | #define LPFN_INET_PTON LPFN_INET_PTONA 218 | typedef PCSTR (WSAAPI * LPFN_INET_NTOPA) (INT Family, PVOID pAddr, PSTR pStringBuf, size_t StringBufSize); 219 | #define LPFN_INET_NTOP LPFN_INET_NTOPA 220 | 221 | #endif 222 | 223 | 224 | #if defined(_WIN32) 225 | #if defined(MG_USE_MS_TYPEDEFS) 226 | 227 | typedef LPFN_WSASOCKET MG_LPFN_WSASOCKET; 228 | typedef LPFN_WSAGETLASTERROR MG_LPFN_WSAGETLASTERROR; 229 | typedef LPFN_WSASTARTUP MG_LPFN_WSASTARTUP; 230 | typedef LPFN_WSACLEANUP MG_LPFN_WSACLEANUP; 231 | typedef LPFN_WSARECV MG_LPFN_WSARECV; 232 | typedef LPFN_WSASEND MG_LPFN_WSASEND; 233 | 234 | #if defined(NETX_IPV6) 235 | typedef LPFN_WSASTRINGTOADDRESS MG_LPFN_WSASTRINGTOADDRESS; 236 | typedef LPFN_WSAADDRESSTOSTRING MG_LPFN_WSAADDRESSTOSTRING; 237 | typedef LPFN_GETADDRINFO MG_LPFN_GETADDRINFO; 238 | typedef LPFN_FREEADDRINFO MG_LPFN_FREEADDRINFO; 239 | typedef LPFN_GETNAMEINFO MG_LPFN_GETNAMEINFO; 240 | typedef LPFN_GETPEERNAME MG_LPFN_GETPEERNAME; 241 | typedef LPFN_INET_NTOP MG_LPFN_INET_NTOP; 242 | typedef LPFN_INET_PTON MG_LPFN_INET_PTON; 243 | #endif 244 | 245 | typedef LPFN_CLOSESOCKET MG_LPFN_CLOSESOCKET; 246 | typedef LPFN_GETHOSTNAME MG_LPFN_GETHOSTNAME; 247 | typedef LPFN_GETHOSTBYNAME MG_LPFN_GETHOSTBYNAME; 248 | typedef LPFN_GETHOSTBYADDR MG_LPFN_GETHOSTBYADDR; 249 | typedef LPFN_GETSERVBYNAME MG_LPFN_GETSERVBYNAME; 250 | 251 | typedef LPFN_HTONS MG_LPFN_HTONS; 252 | typedef LPFN_HTONL MG_LPFN_HTONL; 253 | typedef LPFN_NTOHL MG_LPFN_NTOHL; 254 | typedef LPFN_NTOHS MG_LPFN_NTOHS; 255 | typedef LPFN_CONNECT MG_LPFN_CONNECT; 256 | typedef LPFN_INET_ADDR MG_LPFN_INET_ADDR; 257 | typedef LPFN_INET_NTOA MG_LPFN_INET_NTOA; 258 | 259 | typedef LPFN_SOCKET MG_LPFN_SOCKET; 260 | typedef LPFN_SETSOCKOPT MG_LPFN_SETSOCKOPT; 261 | typedef LPFN_GETSOCKOPT MG_LPFN_GETSOCKOPT; 262 | typedef LPFN_GETSOCKNAME MG_LPFN_GETSOCKNAME; 263 | typedef LPFN_SELECT MG_LPFN_SELECT; 264 | typedef LPFN_RECV MG_LPFN_RECV; 265 | typedef LPFN_SEND MG_LPFN_SEND; 266 | typedef LPFN_SHUTDOWN MG_LPFN_SHUTDOWN; 267 | typedef LPFN_BIND MG_LPFN_BIND; 268 | typedef LPFN_LISTEN MG_LPFN_LISTEN; 269 | typedef LPFN_ACCEPT MG_LPFN_ACCEPT; 270 | 271 | #else 272 | 273 | typedef int (WSAAPI * MG_LPFN_WSASTARTUP) (WORD wVersionRequested, LPWSADATA lpWSAData); 274 | typedef int (WSAAPI * MG_LPFN_WSACLEANUP) (void); 275 | typedef int (WSAAPI * MG_LPFN_WSAGETLASTERROR) (void); 276 | typedef SOCKET (WSAAPI * MG_LPFN_WSASOCKET) (int af, int type, int protocol, LPWSAPROTOCOL_INFOA lpProtocolInfo, GROUP g, DWORD dwFlags); 277 | typedef int (WSAAPI * MG_LPFN_WSARECV) (SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); 278 | typedef int (WSAAPI * MG_LPFN_WSASEND) (SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); 279 | typedef INT (WSAAPI * MG_LPFN_WSASTRINGTOADDRESS) (LPSTR AddressString, INT AddressFamily, LPWSAPROTOCOL_INFOA lpProtocolInfo, LPSOCKADDR lpAddress, LPINT lpAddressLength); 280 | typedef INT (WSAAPI * MG_LPFN_WSAADDRESSTOSTRING) (LPSOCKADDR lpsaAddress, DWORD dwAddressLength, LPWSAPROTOCOL_INFOA lpProtocolInfo, LPSTR lpszAddressString, LPDWORD lpdwAddressStringLength); 281 | typedef INT (WSAAPI * MG_LPFN_GETADDRINFO) (PCSTR pNodeName, PCSTR pServiceName, const ADDRINFOA * pHints, PADDRINFOA * ppResult); 282 | typedef VOID (WSAAPI * MG_LPFN_FREEADDRINFO) (PADDRINFOA pAddrInfo); 283 | typedef int (WSAAPI * MG_LPFN_GETNAMEINFO) (const SOCKADDR * pSockaddr, socklen_t SockaddrLength, PCHAR pNodeBuffer, DWORD NodeBufferSize, PCHAR pServiceBuffer, DWORD ServiceBufferSize, INT Flags); 284 | typedef int (WSAAPI * MG_LPFN_GETPEERNAME) (SOCKET s, struct sockaddr FAR * name, int FAR * namelen); 285 | typedef PCSTR (WSAAPI * MG_LPFN_INET_NTOP) (INT Family, PVOID pAddr, PSTR pStringBuf, size_t StringBufSize); 286 | typedef INT (WSAAPI * MG_LPFN_INET_PTON) (INT Family, PCSTR pszAddrString, PVOID pAddrBuf); 287 | typedef int (WSAAPI * MG_LPFN_CLOSESOCKET) (SOCKET s); 288 | typedef int (WSAAPI * MG_LPFN_GETHOSTNAME) (char FAR * name, int namelen); 289 | typedef struct hostent FAR * (WSAAPI * MG_LPFN_GETHOSTBYNAME) (const char FAR * name); 290 | typedef struct hostent FAR * (WSAAPI * MG_LPFN_GETHOSTBYADDR) (const char FAR * addr, int len, int type); 291 | typedef struct servent FAR * (WSAAPI * MG_LPFN_GETSERVBYNAME) (const char FAR * name, const char FAR * proto); 292 | typedef u_short (WSAAPI * MG_LPFN_HTONS) (u_short hostshort); 293 | typedef u_long (WSAAPI * MG_LPFN_HTONL) (u_long hostlong); 294 | typedef u_long (WSAAPI * MG_LPFN_NTOHL) (u_long netlong); 295 | typedef u_short (WSAAPI * MG_LPFN_NTOHS) (u_short netshort); 296 | typedef char FAR * (WSAAPI * MG_LPFN_INET_NTOA) (struct in_addr in); 297 | typedef int (WSAAPI * MG_LPFN_CONNECT) (SOCKET s, const struct sockaddr FAR * name, int namelen); 298 | typedef unsigned long (WSAAPI * MG_LPFN_INET_ADDR) (const char FAR * cp); 299 | typedef SOCKET (WSAAPI * MG_LPFN_SOCKET) (int af, int type, int protocol); 300 | typedef int (WSAAPI * MG_LPFN_SETSOCKOPT) (SOCKET s, int level, int optname, const char FAR * optval, int optlen); 301 | typedef int (WSAAPI * MG_LPFN_GETSOCKOPT) (SOCKET s, int level, int optname, char FAR * optval, int FAR * optlen); 302 | typedef int (WSAAPI * MG_LPFN_GETSOCKNAME) (SOCKET s, struct sockaddr FAR * name, int FAR * namelen); 303 | typedef int (WSAAPI * MG_LPFN_SELECT) (int nfds, fd_set FAR * readfds, fd_set FAR * writefds, fd_set FAR *exceptfds, const struct timeval FAR * timeout); 304 | typedef int (WSAAPI * MG_LPFN_RECV) (SOCKET s, char FAR * buf, int len, int flags); 305 | typedef int (WSAAPI * MG_LPFN_SEND) (SOCKET s, const char FAR * buf, int len, int flags); 306 | typedef int (WSAAPI * MG_LPFN_SHUTDOWN) (SOCKET s, int how); 307 | typedef int (WSAAPI * MG_LPFN_BIND) (SOCKET s, const struct sockaddr FAR * name, int namelen); 308 | typedef int (WSAAPI * MG_LPFN_LISTEN) (SOCKET s, int backlog); 309 | typedef SOCKET (WSAAPI * MG_LPFN_ACCEPT) (SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen); 310 | 311 | #endif 312 | #endif /* #if defined(_WIN32) */ 313 | 314 | typedef struct tagNETXSOCK { 315 | 316 | unsigned char winsock_ready; 317 | short sock; 318 | short load_attempted; 319 | short nagle_algorithm; 320 | short winsock; 321 | short ipv6; 322 | DBXPLIB plibrary; 323 | 324 | char libnam[256]; 325 | 326 | #if defined(_WIN32) 327 | WSADATA wsadata; 328 | int wsastartup; 329 | WORD version_requested; 330 | MG_LPFN_WSASOCKET p_WSASocket; 331 | MG_LPFN_WSAGETLASTERROR p_WSAGetLastError; 332 | MG_LPFN_WSASTARTUP p_WSAStartup; 333 | MG_LPFN_WSACLEANUP p_WSACleanup; 334 | MG_LPFN_WSAFDISSET p_WSAFDIsSet; 335 | MG_LPFN_WSARECV p_WSARecv; 336 | MG_LPFN_WSASEND p_WSASend; 337 | 338 | #if defined(NETX_IPV6) 339 | MG_LPFN_WSASTRINGTOADDRESS p_WSAStringToAddress; 340 | MG_LPFN_WSAADDRESSTOSTRING p_WSAAddressToString; 341 | MG_LPFN_GETADDRINFO p_getaddrinfo; 342 | MG_LPFN_FREEADDRINFO p_freeaddrinfo; 343 | MG_LPFN_GETNAMEINFO p_getnameinfo; 344 | MG_LPFN_GETPEERNAME p_getpeername; 345 | MG_LPFN_INET_NTOP p_inet_ntop; 346 | MG_LPFN_INET_PTON p_inet_pton; 347 | #else 348 | LPVOID p_WSAStringToAddress; 349 | LPVOID p_WSAAddressToString; 350 | LPVOID p_getaddrinfo; 351 | LPVOID p_freeaddrinfo; 352 | LPVOID p_getnameinfo; 353 | LPVOID p_getpeername; 354 | LPVOID p_inet_ntop; 355 | LPVOID p_inet_pton; 356 | #endif 357 | 358 | MG_LPFN_CLOSESOCKET p_closesocket; 359 | MG_LPFN_GETHOSTNAME p_gethostname; 360 | MG_LPFN_GETHOSTBYNAME p_gethostbyname; 361 | MG_LPFN_GETHOSTBYADDR p_gethostbyaddr; 362 | MG_LPFN_GETSERVBYNAME p_getservbyname; 363 | 364 | MG_LPFN_HTONS p_htons; 365 | MG_LPFN_HTONL p_htonl; 366 | MG_LPFN_NTOHL p_ntohl; 367 | MG_LPFN_NTOHS p_ntohs; 368 | MG_LPFN_CONNECT p_connect; 369 | MG_LPFN_INET_ADDR p_inet_addr; 370 | MG_LPFN_INET_NTOA p_inet_ntoa; 371 | 372 | MG_LPFN_SOCKET p_socket; 373 | MG_LPFN_SETSOCKOPT p_setsockopt; 374 | MG_LPFN_GETSOCKOPT p_getsockopt; 375 | MG_LPFN_GETSOCKNAME p_getsockname; 376 | MG_LPFN_SELECT p_select; 377 | MG_LPFN_RECV p_recv; 378 | MG_LPFN_SEND p_send; 379 | MG_LPFN_SHUTDOWN p_shutdown; 380 | MG_LPFN_BIND p_bind; 381 | MG_LPFN_LISTEN p_listen; 382 | MG_LPFN_ACCEPT p_accept; 383 | #endif /* #if defined(_WIN32) */ 384 | 385 | } NETXSOCK, *PNETXSOCK; 386 | 387 | 388 | int netx_load_winsock (DBXCON *pcon, int context); 389 | int netx_tcp_connect (DBXCON *pcon, int context); 390 | int netx_tcp_handshake (DBXCON *pcon, int context); 391 | int netx_tcp_command (DBXMETH *pmeth, int command, int context); 392 | int netx_tcp_connect_ex (DBXCON *pcon, xLPSOCKADDR p_srv_addr, socklen_netx srv_addr_len, int timeout); 393 | int netx_tcp_disconnect (DBXCON *pcon, int context); 394 | int netx_tcp_write (DBXCON *pcon, unsigned char *data, int size); 395 | int netx_tcp_read (DBXCON *pcon, unsigned char *data, int size, int timeout, int context); 396 | int netx_get_last_error (int context); 397 | int netx_get_error_message (int error_code, char *message, int size, int context); 398 | int netx_get_std_error_message (int error_code, char *message, int size, int context); 399 | 400 | 401 | #endif 402 | 403 | -------------------------------------------------------------------------------- /src/mg-class.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ---------------------------------------------------------------------------- 3 | | mg-dbx.node | 4 | | Author: Chris Munt cmunt@mgateway.com | 5 | | chris.e.munt@gmail.com | 6 | | Copyright (c) 2019-2026 MGateway Ltd | 7 | | Surrey UK. | 8 | | All rights reserved. | 9 | | | 10 | | http://www.mgateway.com | 11 | | | 12 | | Licensed under the Apache License, Version 2.0 (the "License"); you may | 13 | | not use this file except in compliance with the License. | 14 | | You may obtain a copy of the License at | 15 | | | 16 | | http://www.apache.org/licenses/LICENSE-2.0 | 17 | | | 18 | | Unless required by applicable law or agreed to in writing, software | 19 | | distributed under the License is distributed on an "AS IS" BASIS, | 20 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 21 | | See the License for the specific language governing permissions and | 22 | | limitations under the License. | 23 | | | 24 | ---------------------------------------------------------------------------- 25 | */ 26 | 27 | #include "mg-dbx.h" 28 | #include "mg-class.h" 29 | 30 | using namespace v8; 31 | using namespace node; 32 | 33 | Persistent mclass::constructor; 34 | 35 | mclass::mclass(int value) : dbx_count(value) 36 | { 37 | } 38 | 39 | 40 | mclass::~mclass() 41 | { 42 | delete_mclass_template(this); 43 | } 44 | 45 | 46 | #if DBX_NODE_VERSION >= 100000 47 | void mclass::Init(Local exports) 48 | #else 49 | void mclass::Init(Handle exports) 50 | #endif 51 | { 52 | #if DBX_NODE_VERSION >= 120000 53 | /* v2.4.31 */ 54 | #if DBX_NODE_VERSION >= 250000 55 | Isolate* isolate = Isolate::GetCurrent(); 56 | #else 57 | Isolate* isolate = exports->GetIsolate(); 58 | #endif 59 | Local icontext = isolate->GetCurrentContext(); 60 | 61 | Local tpl = FunctionTemplate::New(isolate, New); 62 | tpl->SetClassName(String::NewFromUtf8(isolate, "mclass", NewStringType::kNormal).ToLocalChecked()); 63 | tpl->InstanceTemplate()->SetInternalFieldCount(3); /* 2.0.14 */ 64 | #else 65 | Isolate* isolate = Isolate::GetCurrent(); 66 | 67 | /* Prepare constructor template */ 68 | Local tpl = FunctionTemplate::New(isolate, New); 69 | tpl->SetClassName(String::NewFromUtf8(isolate, "mclass")); 70 | tpl->InstanceTemplate()->SetInternalFieldCount(3); /* v2.3.24 */ 71 | #endif 72 | 73 | /* Prototypes */ 74 | 75 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "classmethod", ClassMethod); 76 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "classmethod_bx", ClassMethod_bx); 77 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "method", Method); 78 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "method_bx", Method_bx); 79 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "setproperty", SetProperty); 80 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "getproperty", GetProperty); 81 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "getproperty_bx", GetProperty_bx); 82 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "reset", Reset); 83 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "_close", Close); 84 | 85 | #if DBX_NODE_VERSION >= 120000 86 | constructor.Reset(isolate, tpl->GetFunction(icontext).ToLocalChecked()); 87 | exports->Set(icontext, String::NewFromUtf8(isolate, "mclass", NewStringType::kNormal).ToLocalChecked(), tpl->GetFunction(icontext).ToLocalChecked()).FromJust(); 88 | #else 89 | constructor.Reset(isolate, tpl->GetFunction()); 90 | #endif 91 | 92 | } 93 | 94 | 95 | void mclass::New(const FunctionCallbackInfo& args) 96 | { 97 | Isolate* isolate = args.GetIsolate(); 98 | #if DBX_NODE_VERSION >= 100000 99 | Local icontext = isolate->GetCurrentContext(); 100 | #endif 101 | HandleScope scope(isolate); 102 | int rc, fc, mn, argc, otype; 103 | DBX_DBNAME *c = NULL; 104 | DBXCON *pcon = NULL; 105 | DBXMETH *pmeth = NULL; 106 | Local obj; 107 | Local cname; 108 | 109 | /* 1.4.10 */ 110 | argc = args.Length(); 111 | if (argc > 0) { 112 | obj = dbx_is_object(args[0], &otype); 113 | if (otype) { 114 | fc = obj->InternalFieldCount(); 115 | if (fc == 3) { 116 | /* v2.4.29 */ 117 | #if DBX_NODE_VERSION >= 220000 118 | mn = obj->GetInternalField(2).As().As()->Int32Value(icontext).FromJust(); 119 | #else 120 | mn = DBX_INT32_VALUE(obj->GetInternalField(2)); 121 | #endif 122 | if (mn == DBX_MAGIC_NUMBER) { 123 | c = ObjectWrap::Unwrap(obj); 124 | } 125 | } 126 | } 127 | } 128 | 129 | if (args.IsConstructCall()) { 130 | /* Invoked as constructor: `new mclass(...)` */ 131 | int value = args[0]->IsUndefined() ? 0 : DBX_INT32_VALUE(args[0]); 132 | mclass * obj = new mclass(value); 133 | obj->c = NULL; 134 | 135 | if (c) { /* 1.4.10 */ 136 | if (c->pcon == NULL) { 137 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "No Connection to the database", 1))); 138 | return; 139 | } 140 | obj->c = c; 141 | pcon = c->pcon; 142 | pmeth = dbx_request_memory(pcon, 1, 1); 143 | pmeth->argc = argc; 144 | if (argc > 1) { 145 | cname = DBX_TO_STRING(args[1]); 146 | dbx_write_char8(isolate, cname, obj->class_name, sizeof(obj->class_name), obj->c->pcon->utf8); 147 | obj->class_name_len = (int) strlen(obj->class_name); 148 | if (pcon->utf16) { 149 | dbx_write_char16(isolate, cname, obj->class_name16); 150 | obj->class_name16_len = (int) dbx_string16_length(isolate, cname); 151 | } 152 | else { 153 | obj->class_name16[0] = 0; 154 | obj->class_name16_len = 0; 155 | } 156 | if (argc > 2) { 157 | DBX_DBFUN_START(c, c->pcon, pmeth); 158 | pmeth->ibuffer_used = 0; 159 | rc = c->ClassReference(c, args, pmeth, NULL, 1, pcon->net_connection); 160 | if (c->pcon->net_connection) { 161 | rc = dbx_classmethod(pmeth); 162 | } 163 | else { 164 | rc = pcon->p_isc_so->p_CacheInvokeClassMethod(pmeth->cargc - 2); 165 | if (rc == CACHE_SUCCESS) { 166 | rc = isc_pop_value(pmeth, &(pmeth->output_val), DBX_DTYPE_STR); 167 | } 168 | } 169 | if (rc != CACHE_SUCCESS) { 170 | dbx_error_message(pmeth, rc); 171 | if (pcon->error_mode == 1) { /* v2.2.21 */ 172 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) pcon->error, 1))); 173 | } 174 | } 175 | DBX_DBFUN_END(c); 176 | DBX_DB_UNLOCK(); 177 | if (pmeth->output_val.type == DBX_DTYPE_OREF) { 178 | obj->oref = pmeth->output_val.num.oref; 179 | } 180 | } 181 | } 182 | dbx_request_memory_free(pcon, pmeth, 0); 183 | } 184 | obj->Wrap(args.This()); 185 | args.This()->SetInternalField(2, DBX_INTEGER_NEW(DBX_MAGIC_NUMBER_MCLASS)); /* 2.0.14 */ 186 | args.GetReturnValue().Set(args.This()); 187 | } 188 | else { 189 | /* Invoked as plain function `mclass(...)`, turn into construct call. */ 190 | const int argc = 1; 191 | Local argv[argc] = { args[0] }; 192 | Local cons = Local::New(isolate, constructor); 193 | args.GetReturnValue().Set(cons->NewInstance(isolate->GetCurrentContext(), argc, argv).ToLocalChecked()); 194 | } 195 | 196 | } 197 | 198 | 199 | mclass * mclass::NewInstance(const FunctionCallbackInfo& args) 200 | { 201 | Isolate* isolate = args.GetIsolate(); 202 | Local icontext = isolate->GetCurrentContext(); 203 | HandleScope scope(isolate); 204 | 205 | #if DBX_NODE_VERSION >= 100000 206 | Local argv[2]; 207 | #else 208 | Handle argv[2]; 209 | #endif 210 | const unsigned argc = 1; 211 | 212 | argv[0] = args[0]; 213 | 214 | Local cons = Local::New(isolate, constructor); 215 | 216 | Local instance = cons->NewInstance(icontext, argc, argv).ToLocalChecked(); 217 | mclass *clx = ObjectWrap::Unwrap(instance); 218 | 219 | args.GetReturnValue().Set(instance); 220 | 221 | return clx; 222 | } 223 | 224 | 225 | int mclass::async_callback(mclass *clx) 226 | { 227 | clx->Unref(); 228 | return 0; 229 | } 230 | 231 | 232 | int mclass::delete_mclass_template(mclass *clx) 233 | { 234 | return 0; 235 | } 236 | 237 | 238 | void mclass::ClassMethod(const FunctionCallbackInfo& args) 239 | { 240 | return ClassMethodEx(args, 0); 241 | } 242 | 243 | 244 | void mclass::ClassMethod_bx(const FunctionCallbackInfo& args) 245 | { 246 | return ClassMethodEx(args, 1); 247 | } 248 | 249 | 250 | void mclass::ClassMethodEx(const FunctionCallbackInfo& args, int binary) 251 | { 252 | short async; 253 | int rc; 254 | DBXCON *pcon; 255 | DBXMETH *pmeth; 256 | Local str; 257 | DBXCREF cref; 258 | mclass *clx = ObjectWrap::Unwrap(args.This()); 259 | MG_CLASS_CHECK_CLASS(clx); 260 | DBX_DBNAME *c = clx->c; 261 | DBX_GET_ISOLATE; 262 | clx->dbx_count ++; 263 | 264 | pcon = c->pcon; 265 | if (pcon->log_functions) { 266 | c->LogFunction(c, args, (void *) clx, (char *) "mclass::classmethod"); 267 | } 268 | pmeth = dbx_request_memory(pcon, 1, 0); 269 | 270 | pmeth->binary = binary; 271 | cref.class_name = clx->class_name; 272 | cref.class_name_len = clx->class_name_len; 273 | cref.class_name16 = clx->class_name16; 274 | cref.class_name16_len = clx->class_name16_len; 275 | cref.oref = clx->oref; 276 | 277 | DBX_CALLBACK_FUN(pmeth->argc, async); 278 | 279 | if (pmeth->argc >= DBX_MAXARGS) { 280 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "Too many arguments on ClassMethod", 1))); 281 | dbx_request_memory_free(pcon, pmeth, 0); 282 | return; 283 | } 284 | 285 | DBX_DBFUN_START(c, pcon, pmeth); 286 | 287 | cref.optype = 0; 288 | rc = c->ClassReference(c, args, pmeth, &cref, 0, (async || pcon->net_connection)); 289 | 290 | if (pcon->log_transmissions) { 291 | dbx_log_transmission(pcon, pmeth, (char *) "mclass::classmethod"); 292 | } 293 | 294 | if (async) { 295 | DBX_DBNAME::dbx_baton_t *baton = c->dbx_make_baton(c, pmeth); 296 | baton->clx = (void *) clx; 297 | baton->isolate = isolate; 298 | baton->pmeth->p_dbxfun = (int (*) (struct tagDBXMETH * pmeth)) dbx_classmethod; 299 | Local cb = Local::Cast(args[pmeth->argc]); 300 | baton->cb.Reset(isolate, cb); 301 | clx->Ref(); 302 | if (c->dbx_queue_task((void *) c->dbx_process_task, (void *) c->dbx_invoke_callback, baton, 0)) { 303 | char error[DBX_ERROR_SIZE]; 304 | T_STRCPY(error, _dbxso(error), pcon->error); 305 | c->dbx_destroy_baton(baton, pmeth); 306 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, error, 1))); 307 | dbx_request_memory_free(pcon, pmeth, 0); 308 | return; 309 | } 310 | return; 311 | } 312 | 313 | if (pcon->net_connection) { 314 | rc = dbx_classmethod(pmeth); 315 | } 316 | else { 317 | rc = pcon->p_isc_so->p_CacheInvokeClassMethod(pmeth->cargc - 2); 318 | if (rc == CACHE_SUCCESS) { 319 | rc = isc_pop_value(pmeth, &(pmeth->output_val), DBX_DTYPE_STR); 320 | } 321 | } 322 | 323 | if (rc != CACHE_SUCCESS) { 324 | dbx_error_message(pmeth, rc); 325 | if (pcon->error_mode == 1) { /* v2.2.21 */ 326 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) pcon->error, 1))); 327 | } 328 | } 329 | 330 | DBX_DBFUN_END(c); 331 | DBX_DB_UNLOCK(); 332 | 333 | if (pcon->log_transmissions == 2) { 334 | dbx_log_response(pcon, (char *) pmeth->output_val.svalue.buf_addr, (int) pmeth->output_val.svalue.len_used, (char *) "mclass::classmethod"); 335 | } 336 | 337 | if (pmeth->output_val.type != DBX_DTYPE_OREF) { 338 | if (binary) { 339 | Local bx = node::Buffer::New(isolate, (char *) pmeth->output_val.svalue.buf_addr, (size_t) pmeth->output_val.svalue.len_used).ToLocalChecked(); 340 | args.GetReturnValue().Set(bx); 341 | } 342 | else { 343 | str = pcon->utf16 ? dbx_new_string16n(isolate, pmeth->output_val.cvalue.buf16_addr, pmeth->output_val.cvalue.len_used) : dbx_new_string8n(isolate, pmeth->output_val.svalue.buf_addr, pmeth->output_val.svalue.len_used, pcon->utf8); 344 | args.GetReturnValue().Set(str); 345 | } 346 | dbx_request_memory_free(pcon, pmeth, 0); 347 | return; 348 | } 349 | 350 | DBX_DB_LOCK(0); 351 | mclass *clx1 = mclass::NewInstance(args); 352 | DBX_DB_UNLOCK(); 353 | 354 | clx1->c = c; 355 | clx1->oref = pmeth->output_val.num.oref; 356 | clx1->class_name[0] = '\0'; 357 | clx1->class_name_len = 0; 358 | clx1->class_name16[0] = 0; 359 | clx1->class_name16_len = 0; 360 | 361 | dbx_request_memory_free(pcon, pmeth, 0); 362 | return; 363 | } 364 | 365 | 366 | void mclass::Method(const FunctionCallbackInfo& args) 367 | { 368 | return MethodEx(args, 0); 369 | } 370 | 371 | 372 | void mclass::Method_bx(const FunctionCallbackInfo& args) 373 | { 374 | return MethodEx(args, 1); 375 | } 376 | 377 | 378 | void mclass::MethodEx(const FunctionCallbackInfo& args, int binary) 379 | { 380 | short async; 381 | int rc; 382 | DBXCON *pcon; 383 | DBXMETH *pmeth; 384 | Local str; 385 | DBXCREF cref; 386 | mclass *clx = ObjectWrap::Unwrap(args.This()); 387 | MG_CLASS_CHECK_CLASS(clx); 388 | DBX_DBNAME *c = clx->c; 389 | DBX_GET_ISOLATE; 390 | clx->dbx_count ++; 391 | 392 | pcon = c->pcon; 393 | if (pcon->log_functions) { 394 | c->LogFunction(c, args, (void *) clx, (char *) "mclass::method"); 395 | } 396 | pmeth = dbx_request_memory(pcon, 1, 0); 397 | 398 | pmeth->binary = binary; 399 | cref.class_name = clx->class_name; 400 | cref.class_name_len = clx->class_name_len; 401 | cref.class_name16 = clx->class_name16; 402 | cref.class_name16_len = clx->class_name16_len; 403 | cref.oref = clx->oref; 404 | 405 | DBX_CALLBACK_FUN(pmeth->argc, async); 406 | 407 | if (pmeth->argc >= DBX_MAXARGS) { 408 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "Too many arguments on ClassMethod", 1))); 409 | dbx_request_memory_free(pcon, pmeth, 0); 410 | return; 411 | } 412 | 413 | DBX_DBFUN_START(c, pcon, pmeth); 414 | 415 | cref.optype = 1; 416 | rc = c->ClassReference(c, args, pmeth, &cref, 0, (async || pcon->net_connection)); 417 | 418 | if (pcon->log_transmissions) { 419 | dbx_log_transmission(pcon, pmeth, (char *) "mclass::method"); 420 | } 421 | 422 | if (async) { 423 | DBX_DBNAME::dbx_baton_t *baton = c->dbx_make_baton(c, pmeth); 424 | baton->clx = (void *) clx; 425 | baton->isolate = isolate; 426 | baton->pmeth->p_dbxfun = (int (*) (struct tagDBXMETH * pmeth)) dbx_method; 427 | Local cb = Local::Cast(args[pmeth->argc]); 428 | baton->cb.Reset(isolate, cb); 429 | clx->Ref(); 430 | if (c->dbx_queue_task((void *) c->dbx_process_task, (void *) c->dbx_invoke_callback, baton, 0)) { 431 | char error[DBX_ERROR_SIZE]; 432 | T_STRCPY(error, _dbxso(error), pcon->error); 433 | c->dbx_destroy_baton(baton, pmeth); 434 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, error, 1))); 435 | dbx_request_memory_free(pcon, pmeth, 0); 436 | return; 437 | } 438 | return; 439 | } 440 | 441 | if (pcon->net_connection) { 442 | rc = dbx_method(pmeth); 443 | } 444 | else { 445 | rc = pcon->p_isc_so->p_CacheInvokeMethod(pmeth->cargc - 2); 446 | if (rc == CACHE_SUCCESS) { 447 | rc = isc_pop_value(pmeth, &(pmeth->output_val), DBX_DTYPE_STR); 448 | } 449 | } 450 | 451 | if (rc != CACHE_SUCCESS) { 452 | dbx_error_message(pmeth, rc); 453 | if (pcon->error_mode == 1) { /* v2.2.21 */ 454 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) pcon->error, 1))); 455 | } 456 | } 457 | 458 | DBX_DBFUN_END(c); 459 | DBX_DB_UNLOCK(); 460 | 461 | if (pcon->log_transmissions == 2) { 462 | dbx_log_response(pcon, (char *) pmeth->output_val.svalue.buf_addr, (int) pmeth->output_val.svalue.len_used, (char *) "mclass::method"); 463 | } 464 | 465 | if (pmeth->output_val.type != DBX_DTYPE_OREF) { 466 | if (binary) { 467 | Local bx = node::Buffer::New(isolate, (char *) pmeth->output_val.svalue.buf_addr, (size_t) pmeth->output_val.svalue.len_used).ToLocalChecked(); 468 | args.GetReturnValue().Set(bx); 469 | } 470 | else { 471 | str = pcon->utf16 ? dbx_new_string16n(isolate, pmeth->output_val.cvalue.buf16_addr, pmeth->output_val.cvalue.len_used) : dbx_new_string8n(isolate, pmeth->output_val.svalue.buf_addr, pmeth->output_val.svalue.len_used, pcon->utf8); 472 | args.GetReturnValue().Set(str); 473 | } 474 | dbx_request_memory_free(pcon, pmeth, 0); 475 | return; 476 | } 477 | 478 | DBX_DB_LOCK(0); 479 | mclass *clx1 = mclass::NewInstance(args); 480 | DBX_DB_UNLOCK(); 481 | 482 | clx1->c = c; 483 | clx1->oref = pmeth->output_val.num.oref; 484 | clx1->class_name[0] = '\0'; 485 | clx1->class_name_len = 0; 486 | clx1->class_name16[0] = 0; 487 | clx1->class_name16_len = 0; 488 | 489 | dbx_request_memory_free(pcon, pmeth, 0); 490 | return; 491 | } 492 | 493 | 494 | void mclass::SetProperty(const FunctionCallbackInfo& args) 495 | { 496 | short async; 497 | int rc; 498 | DBXCON *pcon; 499 | DBXMETH *pmeth; 500 | Local str; 501 | DBXCREF cref; 502 | mclass *clx = ObjectWrap::Unwrap(args.This()); 503 | MG_CLASS_CHECK_CLASS(clx); 504 | DBX_DBNAME *c = clx->c; 505 | DBX_GET_ISOLATE; 506 | clx->dbx_count ++; 507 | 508 | pcon = c->pcon; 509 | if (pcon->log_functions) { 510 | c->LogFunction(c, args, (void *) clx, (char *) "mclass::setproperty"); 511 | } 512 | pmeth = dbx_request_memory(pcon, 1, 0); 513 | 514 | cref.class_name = clx->class_name; 515 | cref.class_name_len = clx->class_name_len; 516 | cref.class_name16 = clx->class_name16; 517 | cref.class_name16_len = clx->class_name16_len; 518 | cref.oref = clx->oref; 519 | 520 | DBX_CALLBACK_FUN(pmeth->argc, async); 521 | 522 | if (pmeth->argc >= DBX_MAXARGS) { 523 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "Too many arguments on ClassMethod", 1))); 524 | dbx_request_memory_free(pcon, pmeth, 0); 525 | return; 526 | } 527 | 528 | DBX_DBFUN_START(c, pcon, pmeth); 529 | 530 | cref.optype = 2; 531 | rc = c->ClassReference(c, args, pmeth, &cref, 0, (async || pcon->net_connection)); 532 | 533 | if (pcon->log_transmissions) { 534 | dbx_log_transmission(pcon, pmeth, (char *) "mclass::setproperty"); 535 | } 536 | 537 | if (async) { 538 | DBX_DBNAME::dbx_baton_t *baton = c->dbx_make_baton(c, pmeth); 539 | baton->clx = (void *) clx; 540 | baton->isolate = isolate; 541 | baton->pmeth->p_dbxfun = (int (*) (struct tagDBXMETH * pmeth)) dbx_setproperty; 542 | Local cb = Local::Cast(args[pmeth->argc]); 543 | baton->cb.Reset(isolate, cb); 544 | clx->Ref(); 545 | if (c->dbx_queue_task((void *) c->dbx_process_task, (void *) c->dbx_invoke_callback, baton, 0)) { 546 | char error[DBX_ERROR_SIZE]; 547 | T_STRCPY(error, _dbxso(error), pcon->error); 548 | c->dbx_destroy_baton(baton, pmeth); 549 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, error, 1))); 550 | dbx_request_memory_free(pcon, pmeth, 0); 551 | return; 552 | } 553 | return; 554 | } 555 | 556 | if (pcon->net_connection) { 557 | rc = dbx_setproperty(pmeth); 558 | } 559 | else { 560 | rc = pcon->p_isc_so->p_CacheSetProperty(); 561 | } 562 | 563 | if (rc == CACHE_SUCCESS) { 564 | dbx_create_string(&(pmeth->output_val.svalue), (void *) &rc, DBX_DTYPE_INT); 565 | } 566 | else { 567 | dbx_error_message(pmeth, rc); 568 | if (pcon->error_mode == 1) { /* v2.2.21 */ 569 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) pcon->error, 1))); 570 | } 571 | } 572 | 573 | DBX_DBFUN_END(c); 574 | DBX_DB_UNLOCK(); 575 | 576 | if (pcon->log_transmissions == 2) { 577 | dbx_log_response(pcon, (char *) pmeth->output_val.svalue.buf_addr, (int) pmeth->output_val.svalue.len_used, (char *) "mclass::setproperty"); 578 | } 579 | 580 | str = dbx_new_string8n(isolate, pmeth->output_val.svalue.buf_addr, pmeth->output_val.svalue.len_used, pcon->utf8); 581 | args.GetReturnValue().Set(str); 582 | dbx_request_memory_free(pcon, pmeth, 0); 583 | return; 584 | } 585 | 586 | 587 | void mclass::GetProperty(const FunctionCallbackInfo& args) 588 | { 589 | return GetPropertyEx(args, 0); 590 | } 591 | 592 | 593 | void mclass::GetProperty_bx(const FunctionCallbackInfo& args) 594 | { 595 | return GetPropertyEx(args, 1); 596 | } 597 | 598 | 599 | void mclass::GetPropertyEx(const FunctionCallbackInfo& args, int binary) 600 | { 601 | short async; 602 | int rc; 603 | DBXCON *pcon; 604 | DBXMETH *pmeth; 605 | Local str; 606 | DBXCREF cref; 607 | mclass *clx = ObjectWrap::Unwrap(args.This()); 608 | MG_CLASS_CHECK_CLASS(clx); 609 | DBX_DBNAME *c = clx->c; 610 | DBX_GET_ISOLATE; 611 | clx->dbx_count ++; 612 | 613 | pcon = c->pcon; 614 | if (pcon->log_functions) { 615 | c->LogFunction(c, args, (void *) clx, (char *) "mclass::getproperty"); 616 | } 617 | pmeth = dbx_request_memory(pcon, 1, 0); 618 | 619 | pmeth->binary = binary; 620 | cref.class_name = clx->class_name; 621 | cref.class_name_len = clx->class_name_len; 622 | cref.class_name16 = clx->class_name16; 623 | cref.class_name16_len = clx->class_name16_len; 624 | cref.oref = clx->oref; 625 | 626 | DBX_CALLBACK_FUN(pmeth->argc, async); 627 | 628 | if (pmeth->argc >= DBX_MAXARGS) { 629 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "Too many arguments on ClassMethod", 1))); 630 | dbx_request_memory_free(pcon, pmeth, 0); 631 | return; 632 | } 633 | 634 | DBX_DBFUN_START(c, pcon, pmeth); 635 | 636 | cref.optype = 3; 637 | rc = c->ClassReference(c, args, pmeth, &cref, 0, (async || pcon->net_connection)); 638 | 639 | if (pcon->log_transmissions) { 640 | dbx_log_transmission(pcon, pmeth, (char *) "mclass::getproperty"); 641 | } 642 | 643 | if (async) { 644 | DBX_DBNAME::dbx_baton_t *baton = c->dbx_make_baton(c, pmeth); 645 | baton->clx = (void *) clx; 646 | baton->isolate = isolate; 647 | baton->pmeth->p_dbxfun = (int (*) (struct tagDBXMETH * pmeth)) dbx_getproperty; 648 | Local cb = Local::Cast(args[pmeth->argc]); 649 | baton->cb.Reset(isolate, cb); 650 | clx->Ref(); 651 | if (c->dbx_queue_task((void *) c->dbx_process_task, (void *) c->dbx_invoke_callback, baton, 0)) { 652 | char error[DBX_ERROR_SIZE]; 653 | T_STRCPY(error, _dbxso(error), pcon->error); 654 | c->dbx_destroy_baton(baton, pmeth); 655 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, error, 1))); 656 | dbx_request_memory_free(pcon, pmeth, 0); 657 | return; 658 | } 659 | return; 660 | } 661 | 662 | if (pcon->net_connection) { 663 | rc = dbx_getproperty(pmeth); 664 | } 665 | else { 666 | rc = pcon->p_isc_so->p_CacheGetProperty(); 667 | if (rc == CACHE_SUCCESS) { 668 | rc = isc_pop_value(pmeth, &(pmeth->output_val), DBX_DTYPE_STR); 669 | } 670 | } 671 | 672 | if (rc != CACHE_SUCCESS) { 673 | dbx_error_message(pmeth, rc); 674 | if (pcon->error_mode == 1) { /* v2.2.21 */ 675 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) pcon->error, 1))); 676 | } 677 | } 678 | 679 | DBX_DBFUN_END(c); 680 | DBX_DB_UNLOCK(); 681 | 682 | if (pcon->log_transmissions == 2) { 683 | dbx_log_response(pcon, (char *) pmeth->output_val.svalue.buf_addr, (int) pmeth->output_val.svalue.len_used, (char *) "mclass::getproperty"); 684 | } 685 | 686 | if (pmeth->output_val.type != DBX_DTYPE_OREF) { 687 | if (binary) { 688 | Local bx = node::Buffer::New(isolate, (char *) pmeth->output_val.svalue.buf_addr, (size_t) pmeth->output_val.svalue.len_used).ToLocalChecked(); 689 | args.GetReturnValue().Set(bx); 690 | } 691 | else { 692 | str = pcon->utf16 ? dbx_new_string16n(isolate, pmeth->output_val.cvalue.buf16_addr, pmeth->output_val.cvalue.len_used) : dbx_new_string8n(isolate, pmeth->output_val.svalue.buf_addr, pmeth->output_val.svalue.len_used, pcon->utf8); 693 | args.GetReturnValue().Set(str); 694 | } 695 | dbx_request_memory_free(pcon, pmeth, 0); 696 | return; 697 | } 698 | 699 | /* 2.0.14 */ 700 | 701 | DBX_DB_LOCK(0); 702 | mclass *clx1 = mclass::NewInstance(args); 703 | DBX_DB_UNLOCK(); 704 | 705 | clx1->c = c; 706 | clx1->oref = pmeth->output_val.num.oref; 707 | clx1->class_name[0] = '\0'; 708 | clx1->class_name_len = 0; 709 | clx1->class_name16[0] = 0; 710 | clx1->class_name16_len = 0; 711 | 712 | dbx_request_memory_free(pcon, pmeth, 0); 713 | return; 714 | } 715 | 716 | 717 | void mclass::Reset(const FunctionCallbackInfo& args) 718 | { 719 | short async; 720 | int rc, len; 721 | char class_name[256]; 722 | DBXCON *pcon; 723 | DBXMETH *pmeth; 724 | Local obj; 725 | Local str; 726 | Local cname; 727 | mclass *clx = ObjectWrap::Unwrap(args.This()); 728 | MG_CLASS_CHECK_CLASS(clx); 729 | DBX_DBNAME *c = clx->c; 730 | DBX_GET_ICONTEXT; 731 | clx->dbx_count ++; 732 | 733 | pcon = c->pcon; 734 | if (pcon->log_functions) { 735 | c->LogFunction(c, args, (void *) clx, (char *) "mclass::reset"); 736 | } 737 | pmeth = dbx_request_memory(pcon, 1, 0); 738 | 739 | DBX_CALLBACK_FUN(pmeth->argc, async); 740 | 741 | pmeth->argc = args.Length(); 742 | 743 | if (pmeth->argc < 1) { 744 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "The class:reset method takes at least one argument (the class name)", 1))); 745 | dbx_request_memory_free(pcon, pmeth, 0); 746 | return; 747 | } 748 | 749 | cname = DBX_TO_STRING(args[0]); 750 | len = dbx_string8_length(isolate, cname, 1); 751 | if (len > 250) { /* 2.4.31 */ 752 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "Invalid class name given for the class:reset method (oversize class name)", 1))); 753 | dbx_request_memory_free(pcon, pmeth, 0); 754 | return; 755 | } 756 | dbx_write_char8(isolate, cname, class_name, sizeof(class_name), pcon->utf8); 757 | if (class_name[0] == '\0') { 758 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "The class:reset method takes at least one argument (the class name)", 1))); 759 | dbx_request_memory_free(pcon, pmeth, 0); 760 | return; 761 | } 762 | 763 | DBX_DBFUN_START(c, pcon, pmeth); 764 | 765 | rc = c->ClassReference(c, args, pmeth, NULL, 0, (async || pcon->net_connection)); 766 | 767 | if (pcon->net_connection) { 768 | rc = dbx_classmethod(pmeth); 769 | } 770 | else { 771 | rc = pcon->p_isc_so->p_CacheInvokeClassMethod(pmeth->cargc - 2); 772 | if (rc == CACHE_SUCCESS) { 773 | rc = isc_pop_value(pmeth, &(pmeth->output_val), DBX_DTYPE_STR); 774 | } 775 | } 776 | 777 | if (rc != CACHE_SUCCESS) { 778 | dbx_error_message(pmeth, rc); 779 | if (pcon->error_mode == 1) { /* v2.2.21 */ 780 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) pcon->error, 1))); 781 | } 782 | } 783 | 784 | DBX_DBFUN_END(c); 785 | DBX_DB_UNLOCK(); 786 | 787 | if (pmeth->output_val.type != DBX_DTYPE_OREF) { 788 | str = dbx_new_string8n(isolate, pmeth->output_val.svalue.buf_addr, pmeth->output_val.svalue.len_used, pcon->utf8); 789 | args.GetReturnValue().Set(str); 790 | dbx_request_memory_free(pcon, pmeth, 0); 791 | return; 792 | } 793 | 794 | clx->oref = pmeth->output_val.num.oref; 795 | strcpy(clx->class_name, class_name); 796 | clx->class_name_len = (int) strlen(clx->class_name); 797 | if (pcon->utf16) { 798 | dbx_write_char16(isolate, cname, clx->class_name16); 799 | clx->class_name16_len = dbx_string16_length(isolate, cname); 800 | } 801 | else { 802 | clx->class_name16[0] = 0; 803 | clx->class_name16_len = 0; 804 | } 805 | 806 | dbx_request_memory_free(pcon, pmeth, 0); 807 | return; 808 | } 809 | 810 | 811 | void mclass::Close(const FunctionCallbackInfo& args) 812 | { 813 | 814 | /* 815 | clx->delete_mclass_template(clx); 816 | */ 817 | 818 | return; 819 | } 820 | 821 | 822 | 823 | 824 | -------------------------------------------------------------------------------- /src/mg-cursor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ---------------------------------------------------------------------------- 3 | | mg-dbx.node | 4 | | Author: Chris Munt cmunt@mgateway.com | 5 | | chris.e.munt@gmail.com | 6 | | Copyright (c) 2019-2026 MGateway Ltd | 7 | | Surrey UK. | 8 | | All rights reserved. | 9 | | | 10 | | http://www.mgateway.com | 11 | | | 12 | | Licensed under the Apache License, Version 2.0 (the "License"); you may | 13 | | not use this file except in compliance with the License. | 14 | | You may obtain a copy of the License at | 15 | | | 16 | | http://www.apache.org/licenses/LICENSE-2.0 | 17 | | | 18 | | Unless required by applicable law or agreed to in writing, software | 19 | | distributed under the License is distributed on an "AS IS" BASIS, | 20 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 21 | | See the License for the specific language governing permissions and | 22 | | limitations under the License. | 23 | | | 24 | ---------------------------------------------------------------------------- 25 | */ 26 | 27 | #include "mg-dbx.h" 28 | #include "mg-cursor.h" 29 | 30 | using namespace v8; 31 | using namespace node; 32 | 33 | Persistent mcursor::constructor; 34 | 35 | mcursor::mcursor(int value) : dbx_count(value) 36 | { 37 | } 38 | 39 | 40 | mcursor::~mcursor() 41 | { 42 | delete_mcursor_template(this); 43 | } 44 | 45 | 46 | #if DBX_NODE_VERSION >= 100000 47 | void mcursor::Init(Local exports) 48 | #else 49 | void mcursor::Init(Handle exports) 50 | #endif 51 | { 52 | #if DBX_NODE_VERSION >= 120000 53 | /* v2.4.31 */ 54 | #if DBX_NODE_VERSION >= 250000 55 | Isolate* isolate = Isolate::GetCurrent(); 56 | #else 57 | Isolate* isolate = exports->GetIsolate(); 58 | #endif 59 | Local icontext = isolate->GetCurrentContext(); 60 | 61 | Local tpl = FunctionTemplate::New(isolate, New); 62 | tpl->SetClassName(String::NewFromUtf8(isolate, "mcursor", NewStringType::kNormal).ToLocalChecked()); 63 | tpl->InstanceTemplate()->SetInternalFieldCount(3); /* 2.0.14 */ 64 | #else 65 | Isolate* isolate = Isolate::GetCurrent(); 66 | 67 | /* Prepare constructor template */ 68 | Local tpl = FunctionTemplate::New(isolate, New); 69 | tpl->SetClassName(String::NewFromUtf8(isolate, "mcursor")); 70 | tpl->InstanceTemplate()->SetInternalFieldCount(3); /* v2.3.24 */ 71 | #endif 72 | 73 | /* Prototypes */ 74 | 75 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "execute", Execute); 76 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "cleanup", Cleanup); 77 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "next", Next); 78 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "previous", Previous); 79 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "reset", Reset); 80 | DBX_NODE_SET_PROTOTYPE_METHOD(tpl, "_close", Close); 81 | 82 | #if DBX_NODE_VERSION >= 120000 83 | constructor.Reset(isolate, tpl->GetFunction(icontext).ToLocalChecked()); 84 | exports->Set(icontext, String::NewFromUtf8(isolate, "mcursor", NewStringType::kNormal).ToLocalChecked(), tpl->GetFunction(icontext).ToLocalChecked()).FromJust(); 85 | #else 86 | constructor.Reset(isolate, tpl->GetFunction()); 87 | #endif 88 | 89 | } 90 | 91 | 92 | void mcursor::New(const FunctionCallbackInfo& args) 93 | { 94 | Isolate* isolate = args.GetIsolate(); 95 | #if DBX_NODE_VERSION >= 100000 96 | Local icontext = isolate->GetCurrentContext(); 97 | #endif 98 | HandleScope scope(isolate); 99 | int rc, fc, mn, argc, otype; 100 | DBX_DBNAME *c = NULL; 101 | DBXCON *pcon = NULL; 102 | DBXMETH *pmeth = NULL; 103 | Local obj; 104 | 105 | /* 1.4.10 */ 106 | argc = args.Length(); 107 | if (argc > 0) { 108 | obj = dbx_is_object(args[0], &otype); 109 | if (otype) { 110 | fc = obj->InternalFieldCount(); 111 | if (fc == 3) { 112 | /* v2.4.29 */ 113 | #if DBX_NODE_VERSION >= 220000 114 | mn = obj->GetInternalField(2).As().As()->Int32Value(icontext).FromJust(); 115 | #else 116 | mn = DBX_INT32_VALUE(obj->GetInternalField(2)); 117 | #endif 118 | if (mn == DBX_MAGIC_NUMBER) { 119 | c = ObjectWrap::Unwrap(obj); 120 | } 121 | } 122 | } 123 | } 124 | 125 | if (args.IsConstructCall()) { 126 | /* Invoked as constructor: `new mcursor(...)` */ 127 | int value = args[0]->IsUndefined() ? 0 : DBX_INT32_VALUE(args[0]); 128 | 129 | mcursor * obj = new mcursor(value); 130 | obj->c = NULL; 131 | 132 | if (c) { /* 1.4.10 */ 133 | if (c->pcon == NULL) { 134 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "No Connection to the database", 1))); 135 | return; 136 | } 137 | pcon = c->pcon; 138 | pmeth = dbx_request_memory(pcon, 1, 1); 139 | pmeth->argc = argc; 140 | dbx_cursor_init((void *) obj); 141 | obj->c = c; 142 | 143 | /* 1.4.10 */ 144 | rc = dbx_cursor_reset(args, isolate, pcon, pmeth, (void *) obj, 1, 1); 145 | if (rc < 0) { 146 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "The mcursor::New() method takes at least one argument (the query object)", 1))); 147 | dbx_request_memory_free(pcon, pmeth, 0); 148 | return; 149 | } 150 | dbx_request_memory_free(pcon, pmeth, 0); 151 | } 152 | obj->Wrap(args.This()); 153 | args.This()->SetInternalField(2, DBX_INTEGER_NEW(DBX_MAGIC_NUMBER_MCURSOR)); /* 2.0.14 */ 154 | args.GetReturnValue().Set(args.This()); 155 | } 156 | else { 157 | /* Invoked as plain function `mcursor(...)`, turn into construct call. */ 158 | const int argc = 1; 159 | Local argv[argc] = { args[0] }; 160 | Local cons = Local::New(isolate, constructor); 161 | args.GetReturnValue().Set(cons->NewInstance(isolate->GetCurrentContext(), argc, argv).ToLocalChecked()); 162 | } 163 | } 164 | 165 | 166 | mcursor * mcursor::NewInstance(const FunctionCallbackInfo& args) 167 | { 168 | Isolate* isolate = args.GetIsolate(); 169 | Local icontext = isolate->GetCurrentContext(); 170 | HandleScope scope(isolate); 171 | 172 | #if DBX_NODE_VERSION >= 100000 173 | Local argv[2]; 174 | #else 175 | Handle argv[2]; 176 | #endif 177 | const unsigned argc = 1; 178 | 179 | argv[0] = args[0]; 180 | 181 | Local cons = Local::New(isolate, constructor); 182 | Local instance = cons->NewInstance(icontext, argc, argv).ToLocalChecked(); 183 | mcursor *cx = ObjectWrap::Unwrap(instance); 184 | 185 | args.GetReturnValue().Set(instance); 186 | 187 | return cx; 188 | } 189 | 190 | 191 | int mcursor::async_callback(mcursor *cx) 192 | { 193 | cx->Unref(); 194 | return 0; 195 | } 196 | 197 | 198 | int mcursor::delete_mcursor_template(mcursor *cx) 199 | { 200 | return 0; 201 | } 202 | 203 | 204 | void mcursor::Execute(const FunctionCallbackInfo& args) 205 | { 206 | short async; 207 | int cn; 208 | DBXCON *pcon; 209 | DBXMETH *pmeth; 210 | Local obj, obj1; 211 | Local key; 212 | mcursor *cx = ObjectWrap::Unwrap(args.This()); 213 | MG_CURSOR_CHECK_CLASS(cx); 214 | DBX_DBNAME *c = cx->c; 215 | DBX_GET_ICONTEXT; 216 | cx->dbx_count ++; 217 | 218 | pcon = c->pcon; 219 | if (pcon->log_functions) { 220 | c->LogFunction(c, args, (void *) cx, (char *) "mcursor::execute"); 221 | } 222 | pmeth = dbx_request_memory(pcon, 1, 0); 223 | 224 | pmeth->psql = cx->psql; 225 | 226 | DBX_CALLBACK_FUN(pmeth->argc, async); 227 | 228 | if (pmeth->argc >= DBX_MAXARGS) { 229 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "Too many arguments on Execute", 1))); 230 | dbx_request_memory_free(pcon, pmeth, 0); 231 | return; 232 | } 233 | 234 | DBX_DBFUN_START(c, pcon, pmeth); 235 | 236 | if (async) { 237 | DBX_DBNAME::dbx_baton_t *baton = c->dbx_make_baton(c, pmeth); 238 | baton->cx = (void *) cx; 239 | baton->isolate = isolate; 240 | baton->pmeth->p_dbxfun = (int (*) (struct tagDBXMETH * pmeth)) dbx_sql_execute; 241 | Local cb = Local::Cast(args[pmeth->argc]); 242 | 243 | baton->cb.Reset(isolate, cb); 244 | 245 | cx->Ref(); 246 | 247 | if (c->dbx_queue_task((void *) c->dbx_process_task, (void *) c->dbx_invoke_callback_sql_execute, baton, 0)) { 248 | char error[DBX_ERROR_SIZE]; 249 | 250 | T_STRCPY(error, _dbxso(error), pcon->error); 251 | c->dbx_destroy_baton(baton, pmeth); 252 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, error, 1))); 253 | dbx_request_memory_free(pcon, pmeth, 0); 254 | return; 255 | } 256 | return; 257 | } 258 | 259 | dbx_sql_execute(pmeth); 260 | 261 | DBX_DBFUN_END(c); 262 | 263 | obj = DBX_OBJECT_NEW(); 264 | 265 | key = dbx_new_string8(isolate, (char *) "sqlcode", 0); 266 | DBX_SET(obj, key, DBX_INTEGER_NEW(pmeth->psql->sqlcode)); 267 | key = dbx_new_string8(isolate, (char *) "sqlstate", 0); 268 | DBX_SET(obj, key, dbx_new_string8(isolate, pmeth->psql->sqlstate, 0)); 269 | if (pcon->error[0]) { 270 | key = dbx_new_string8(isolate, (char *) "error", 0); 271 | DBX_SET(obj, key, dbx_new_string8(isolate, pcon->error, 0)); 272 | } 273 | else if (pmeth->psql->no_cols > 0) { /* v2.1.18 */ 274 | 275 | Local a = DBX_ARRAY_NEW(pmeth->psql->no_cols); 276 | key = dbx_new_string8(isolate, (char *) "columns", 0); 277 | DBX_SET(obj, key, a); 278 | 279 | for (cn = 0; cn < pmeth->psql->no_cols; cn ++) { 280 | obj1 = DBX_OBJECT_NEW(); 281 | DBX_SET(a, cn, obj1); 282 | key = dbx_new_string8(isolate, (char *) "name", 0); 283 | DBX_SET(obj1, key, dbx_new_string8(isolate, pmeth->psql->cols[cn]->name.buf_addr, 0)); 284 | if (pmeth->psql->cols[cn]->stype) { 285 | key = dbx_new_string8(isolate, (char *) "type", 0); 286 | DBX_SET(obj1, key, dbx_new_string8(isolate, pmeth->psql->cols[cn]->stype, 0)); 287 | } 288 | } 289 | } 290 | 291 | args.GetReturnValue().Set(obj); 292 | dbx_request_memory_free(pcon, pmeth, 0); 293 | return; 294 | } 295 | 296 | 297 | void mcursor::Cleanup(const FunctionCallbackInfo& args) 298 | { 299 | short async; 300 | DBXCON *pcon; 301 | DBXMETH *pmeth; 302 | Local result; 303 | mcursor *cx = ObjectWrap::Unwrap(args.This()); 304 | MG_CURSOR_CHECK_CLASS(cx); 305 | DBX_DBNAME *c = cx->c; 306 | DBX_GET_ISOLATE; 307 | cx->dbx_count ++; 308 | 309 | pcon = c->pcon; 310 | if (pcon->log_functions) { 311 | c->LogFunction(c, args, (void *) cx, (char *) "mcursor::cleanup"); 312 | } 313 | pmeth = dbx_request_memory(pcon, 1, 0); 314 | 315 | pmeth->psql = cx->psql; 316 | 317 | DBX_CALLBACK_FUN(pmeth->argc, async); 318 | 319 | if (pmeth->argc >= DBX_MAXARGS) { 320 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "Too many arguments on Cleanup", 1))); 321 | dbx_request_memory_free(pcon, pmeth, 0); 322 | return; 323 | } 324 | 325 | DBX_DBFUN_START(c, pcon, pmeth); 326 | 327 | if (async) { 328 | DBX_DBNAME::dbx_baton_t *baton = c->dbx_make_baton(c, pmeth); 329 | baton->cx = (void *) cx; 330 | baton->isolate = isolate; 331 | baton->pmeth->p_dbxfun = (int (*) (struct tagDBXMETH * pmeth)) dbx_sql_cleanup; 332 | Local cb = Local::Cast(args[pmeth->argc]); 333 | 334 | baton->cb.Reset(isolate, cb); 335 | 336 | cx->Ref(); 337 | 338 | if (c->dbx_queue_task((void *) c->dbx_process_task, (void *) c->dbx_invoke_callback, baton, 0)) { 339 | char error[DBX_ERROR_SIZE]; 340 | 341 | T_STRCPY(error, _dbxso(error), pcon->error); 342 | c->dbx_destroy_baton(baton, pmeth); 343 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, error, 1))); 344 | dbx_request_memory_free(pcon, pmeth, 0); 345 | return; 346 | } 347 | return; 348 | } 349 | 350 | dbx_sql_cleanup(pmeth); 351 | 352 | DBX_DBFUN_END(c); 353 | 354 | result = dbx_new_string8n(isolate, pmeth->output_val.svalue.buf_addr, pmeth->output_val.svalue.len_used, pcon->utf8); 355 | args.GetReturnValue().Set(result); 356 | dbx_request_memory_free(pcon, pmeth, 0); 357 | return; 358 | } 359 | 360 | 361 | void mcursor::Next(const FunctionCallbackInfo& args) 362 | { 363 | short async; 364 | int n, eod; 365 | DBXCON *pcon; 366 | DBXMETH *pmeth; 367 | Local obj; 368 | Local key; 369 | DBXQR *pqr; 370 | mcursor *cx = ObjectWrap::Unwrap(args.This()); 371 | MG_CURSOR_CHECK_CLASS(cx); 372 | DBX_DBNAME *c = cx->c; 373 | DBX_GET_ICONTEXT; 374 | cx->dbx_count ++; 375 | 376 | pcon = c->pcon; 377 | if (pcon->log_functions) { 378 | c->LogFunction(c, args, (void *) cx, (char *) "mcursor::next"); 379 | } 380 | pmeth = dbx_request_memory(pcon, 1, 0); 381 | 382 | DBX_CALLBACK_FUN(pmeth->argc, async); 383 | 384 | if (pmeth->argc >= DBX_MAXARGS) { 385 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "Too many arguments on Next", 1))); 386 | dbx_request_memory_free(pcon, pmeth, 0); 387 | return; 388 | } 389 | if (async) { 390 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "Cursor based operations cannot be invoked asynchronously", 1))); 391 | dbx_request_memory_free(pcon, pmeth, 0); 392 | return; 393 | } 394 | 395 | if (cx->context == 1) { 396 | 397 | if (cx->pqr_prev->keyn < 1) { 398 | args.GetReturnValue().Set(DBX_NULL()); 399 | dbx_request_memory_free(pcon, pmeth, 0); 400 | return; 401 | } 402 | 403 | DBX_DBFUN_START(c, pcon, pmeth); 404 | DBX_DB_LOCK(0); 405 | 406 | eod = dbx_global_order(pmeth, cx->pqr_prev, 1, cx->getdata); 407 | 408 | DBX_DBFUN_END(c); 409 | DBX_DB_UNLOCK(); 410 | 411 | if (pcon->utf16 && cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.len_used == 0) { 412 | args.GetReturnValue().Set(DBX_NULL()); 413 | } 414 | else if (!pcon->utf16 && cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].len_used == 0) { 415 | args.GetReturnValue().Set(DBX_NULL()); 416 | } 417 | else if (cx->getdata == 0) { 418 | /* 419 | if (cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].len_used && cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].len_used < 10) { 420 | int n; 421 | char buffer[32]; 422 | strncpy(buffer, cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].buf_addr, cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].len_used); 423 | buffer[cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].len_used] = '\0'; 424 | n = (int) strtol(buffer, NULL, 10); 425 | args.GetReturnValue().Set(DBX_INTEGER_NEW(n)); 426 | return; 427 | } 428 | */ 429 | key = pcon->utf16 ? dbx_new_string16n(isolate, cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.buf16_addr, cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.len_used) : dbx_new_string8n(isolate, cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].buf_addr, cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].len_used, pcon->utf8); 430 | args.GetReturnValue().Set(key); 431 | } 432 | else { 433 | if (cx->format == 1) { 434 | cx->data.len_used = 0; 435 | dbx_escape_output(&(cx->data), (char *) "key=", 4, 0); 436 | pcon->utf16 ? dbx_escape_output16(&(cx->data), cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.buf16_addr, cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.len_used, 1) : dbx_escape_output(&(cx->data), cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].buf_addr, cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].len_used, 1); 437 | dbx_escape_output(&(cx->data), (char *) "&data=", 6, 0); 438 | pcon->utf16 ? dbx_escape_output16(&(cx->data), cx->pqr_prev->data.cvalue.buf16_addr, cx->pqr_prev->data.cvalue.len_used, 1) : dbx_escape_output(&(cx->data), cx->pqr_prev->data.svalue.buf_addr, cx->pqr_prev->data.svalue.len_used, 1); 439 | key = dbx_new_string8n(isolate, (char *) cx->data.buf_addr, cx->data.len_used, 0); 440 | args.GetReturnValue().Set(key); 441 | } 442 | else { 443 | obj = DBX_OBJECT_NEW(); 444 | 445 | key = dbx_new_string8(isolate, (char *) "key", 0); 446 | DBX_SET(obj, key, pcon->utf16 ? dbx_new_string16n(isolate, cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.buf16_addr, cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.len_used) : dbx_new_string8n(isolate, cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].buf_addr, cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].len_used, pcon->utf8)); 447 | key = dbx_new_string8(isolate, (char *) "data", 0); 448 | DBX_SET(obj, key, pcon->utf16 ? dbx_new_string16n(isolate, cx->pqr_prev->data.cvalue.buf16_addr, cx->pqr_prev->data.cvalue.len_used) : dbx_new_string8n(isolate, cx->pqr_prev->data.svalue.buf_addr, cx->pqr_prev->data.svalue.len_used, 0)); 449 | args.GetReturnValue().Set(obj); 450 | } 451 | } 452 | } 453 | else if (cx->context == 2) { 454 | 455 | DBX_DBFUN_START(c, pcon, pmeth); 456 | DBX_DB_LOCK(0); 457 | 458 | eod = dbx_global_query(pmeth, cx->pqr_next, cx->pqr_prev, 1, cx->getdata); 459 | 460 | DBX_DBFUN_END(c); 461 | DBX_DB_UNLOCK(); 462 | 463 | if (cx->format == 1) { 464 | char buffer[32], delim[4]; 465 | 466 | cx->data.len_used = 0; 467 | *delim = '\0'; 468 | for (n = 0; n < cx->pqr_next->keyn; n ++) { 469 | sprintf(buffer, (char *) "%skey%d=", delim, n + 1); 470 | dbx_escape_output(&(cx->data), buffer, (int) strlen(buffer), 0); 471 | pcon->utf16 ? dbx_escape_output16(&(cx->data), cx->pqr_next->keys[n].cvalue.buf16_addr, cx->pqr_next->keys[n].cvalue.len_used, 1) : dbx_escape_output(&(cx->data), cx->pqr_next->ykeys[n].buf_addr, cx->pqr_next->ykeys[n].len_used, 1); 472 | strcpy(delim, (char *) "&"); 473 | } 474 | if (cx->getdata) { 475 | sprintf(buffer, (char *) "%sdata=", delim); 476 | dbx_escape_output(&(cx->data), buffer, (int) strlen(buffer), 0); 477 | pcon->utf16 ? dbx_escape_output16(&(cx->data), cx->pqr_next->data.cvalue.buf16_addr, cx->pqr_next->data.cvalue.len_used, 1) : dbx_escape_output(&(cx->data), cx->pqr_next->data.svalue.buf_addr, cx->pqr_next->data.svalue.len_used, 1); 478 | } 479 | 480 | key = dbx_new_string8n(isolate, (char *) cx->data.buf_addr, cx->data.len_used, 0); 481 | args.GetReturnValue().Set(key); 482 | } 483 | else { 484 | obj = DBX_OBJECT_NEW(); 485 | key = dbx_new_string8(isolate, (char *) "key", 0); 486 | Local a = DBX_ARRAY_NEW(cx->pqr_next->keyn); 487 | DBX_SET(obj, key, a); 488 | 489 | for (n = 0; n < cx->pqr_next->keyn; n ++) { 490 | DBX_SET(a, n, pcon->utf16 ? dbx_new_string16n(isolate, cx->pqr_next->keys[n].cvalue.buf16_addr, cx->pqr_next->keys[n].cvalue.len_used) : dbx_new_string8n(isolate, cx->pqr_next->ykeys[n].buf_addr, cx->pqr_next->ykeys[n].len_used, 0)); 491 | } 492 | if (cx->getdata) { 493 | key = dbx_new_string8(isolate, (char *) "data", 0); 494 | DBX_SET(obj, key, pcon->utf16 ? dbx_new_string16n(isolate, cx->pqr_next->data.cvalue.buf16_addr, cx->pqr_next->data.cvalue.len_used) : dbx_new_string8n(isolate, cx->pqr_next->data.svalue.buf_addr, cx->pqr_next->data.svalue.len_used, 0)); 495 | } 496 | } 497 | 498 | pqr = cx->pqr_next; 499 | cx->pqr_next = cx->pqr_prev; 500 | cx->pqr_prev = pqr; 501 | 502 | if (eod == CACHE_SUCCESS) { 503 | if (cx->format == 1) 504 | args.GetReturnValue().Set(key); 505 | else 506 | args.GetReturnValue().Set(obj); 507 | } 508 | else { 509 | args.GetReturnValue().Set(DBX_NULL()); 510 | } 511 | dbx_request_memory_free(pcon, pmeth, 0); 512 | return; 513 | } 514 | else if (cx->context == 9) { 515 | 516 | /* v2.1.18 */ 517 | DBX_DBFUN_START(c, pcon, pmeth); 518 | DBX_DB_LOCK(0); 519 | 520 | eod = dbx_global_directory(pmeth, cx->pqr_prev, 1, &(cx->counter)); 521 | 522 | DBX_DBFUN_END(c); 523 | DBX_DB_UNLOCK(); 524 | 525 | if (eod) { 526 | args.GetReturnValue().Set(DBX_NULL()); 527 | } 528 | else { 529 | key = pcon->utf16 ? dbx_new_string16n(isolate, cx->pqr_prev->global_name16.cvalue.buf16_addr, cx->pqr_prev->global_name16.cvalue.len_used) : dbx_new_string8n(isolate, cx->pqr_prev->global_name.buf_addr, cx->pqr_prev->global_name.len_used, pcon->utf8); 530 | args.GetReturnValue().Set(key); 531 | } 532 | dbx_request_memory_free(pcon, pmeth, 0); 533 | return; 534 | } 535 | else if (cx->context == 11) { 536 | 537 | pmeth->psql = cx->psql; 538 | 539 | if (!pmeth->psql) { 540 | args.GetReturnValue().Set(DBX_NULL()); 541 | dbx_request_memory_free(pcon, pmeth, 0); 542 | return; 543 | } 544 | 545 | eod = dbx_sql_row(pmeth, pmeth->psql->row_no, 1); 546 | if (eod) { 547 | args.GetReturnValue().Set(DBX_NULL()); 548 | } 549 | else { 550 | int len, dsort, dtype; 551 | 552 | obj = DBX_OBJECT_NEW(); 553 | 554 | for (n = 0; n < pmeth->psql->no_cols; n ++) { 555 | len = (int) dbx_get_block_size((unsigned char *) pmeth->output_val.svalue.buf_addr, pmeth->output_val.offs, &dsort, &dtype); 556 | pmeth->output_val.offs += 5; 557 | 558 | /* printf("\r\n ROW DATA: n=%d; len=%d; offset=%d; sort=%d; type=%d; str=%s;", n, len, pmeth->output_val.offs, dsort, dtype, pmeth->output_val.svalue.buf_addr + pmeth->output_val.offs); */ 559 | 560 | if (dsort == DBX_DSORT_EOD || dsort == DBX_DSORT_ERROR) { 561 | break; 562 | } 563 | 564 | key = dbx_new_string8n(isolate, (char *) pmeth->psql->cols[n]->name.buf_addr, pmeth->psql->cols[n]->name.len_used, 0); 565 | /* v2.4.28 add utf8 flag */ 566 | DBX_SET(obj, key, dbx_new_string8n(isolate, pmeth->output_val.svalue.buf_addr + pmeth->output_val.offs, len, pcon->utf8)); 567 | pmeth->output_val.offs += len; 568 | } 569 | 570 | args.GetReturnValue().Set(obj); 571 | } 572 | dbx_request_memory_free(pcon, pmeth, 0); 573 | return; 574 | } 575 | else { 576 | args.GetReturnValue().Set(DBX_NULL()); 577 | } 578 | dbx_request_memory_free(pcon, pmeth, 0); 579 | return; 580 | } 581 | 582 | 583 | void mcursor::Previous(const FunctionCallbackInfo& args) 584 | { 585 | short async; 586 | int n, eod; 587 | DBXCON *pcon; 588 | DBXMETH *pmeth; 589 | Local obj; 590 | Local key; 591 | DBXQR *pqr; 592 | mcursor *cx = ObjectWrap::Unwrap(args.This()); 593 | MG_CURSOR_CHECK_CLASS(cx); 594 | DBX_DBNAME *c = cx->c; 595 | DBX_GET_ICONTEXT; 596 | cx->dbx_count ++; 597 | 598 | pcon = c->pcon; 599 | if (pcon->log_functions) { 600 | c->LogFunction(c, args, (void *) cx, (char *) "mcursor::previous"); 601 | } 602 | pmeth = dbx_request_memory(pcon, 1, 0); 603 | 604 | DBX_CALLBACK_FUN(pmeth->argc, async); 605 | 606 | if (pmeth->argc >= DBX_MAXARGS) { 607 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "Too many arguments on Previous", 1))); 608 | dbx_request_memory_free(pcon, pmeth, 0); 609 | return; 610 | } 611 | if (async) { 612 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "Cursor based operations cannot be invoked asynchronously", 1))); 613 | dbx_request_memory_free(pcon, pmeth, 0); 614 | return; 615 | } 616 | 617 | if (cx->context == 1) { 618 | 619 | if (cx->pqr_prev->keyn < 1) { 620 | args.GetReturnValue().Set(DBX_NULL()); 621 | dbx_request_memory_free(pcon, pmeth, 0); 622 | return; 623 | } 624 | 625 | DBX_DBFUN_START(c, pcon, pmeth); 626 | DBX_DB_LOCK(0); 627 | 628 | eod = dbx_global_order(pmeth, cx->pqr_prev, -1, cx->getdata); 629 | 630 | DBX_DBFUN_END(c); 631 | DBX_DB_UNLOCK(); 632 | 633 | if (pcon->utf16 && cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.len_used == 0) { 634 | args.GetReturnValue().Set(DBX_NULL()); 635 | } 636 | else if (!pcon->utf16 && cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].len_used == 0) { 637 | args.GetReturnValue().Set(DBX_NULL()); 638 | } 639 | else if (cx->getdata == 0) { 640 | key = pcon->utf16 ? dbx_new_string16n(isolate, cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.buf16_addr, cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.len_used) : dbx_new_string8n(isolate, cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].buf_addr, cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].len_used, pcon->utf8); 641 | args.GetReturnValue().Set(key); 642 | } 643 | else { 644 | if (cx->format == 1) { 645 | cx->data.len_used = 0; 646 | dbx_escape_output(&cx->data, (char *) "key=", 4, 0); 647 | pcon->utf16 ? dbx_escape_output16(&cx->data, cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.buf16_addr, cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.len_used, 1) : dbx_escape_output(&cx->data, cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].buf_addr, cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].len_used, 1); 648 | dbx_escape_output(&cx->data, (char *) "&data=", 6, 0); 649 | pcon->utf16 ? dbx_escape_output16(&cx->data, cx->pqr_prev->data.cvalue.buf16_addr, cx->pqr_prev->data.cvalue.len_used, 1) : dbx_escape_output(&cx->data, cx->pqr_prev->data.svalue.buf_addr, cx->pqr_prev->data.svalue.len_used, 1); 650 | key = dbx_new_string8n(isolate, (char *) cx->data.buf_addr, cx->data.len_used, 0); 651 | args.GetReturnValue().Set(key); 652 | } 653 | else { 654 | obj = DBX_OBJECT_NEW(); 655 | 656 | key = dbx_new_string8(isolate, (char *) "key", 0); 657 | DBX_SET(obj, key, pcon->utf16 ? dbx_new_string16n(isolate, cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.buf16_addr, cx->pqr_prev->keys[cx->pqr_prev->keyn - 1].cvalue.len_used) : dbx_new_string8n(isolate, cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].buf_addr, cx->pqr_prev->ykeys[cx->pqr_prev->keyn - 1].len_used, pcon->utf8)); 658 | key = dbx_new_string8(isolate, (char *) "data", 0); 659 | DBX_SET(obj, key, pcon->utf16 ? dbx_new_string16n(isolate, cx->pqr_prev->data.cvalue.buf16_addr, cx->pqr_prev->data.cvalue.len_used) : dbx_new_string8n(isolate, cx->pqr_prev->data.svalue.buf_addr, cx->pqr_prev->data.svalue.len_used, 0)); 660 | args.GetReturnValue().Set(obj); 661 | } 662 | } 663 | } 664 | else if (cx->context == 2) { 665 | 666 | DBX_DBFUN_START(c, pcon, pmeth); 667 | DBX_DB_LOCK(0); 668 | 669 | eod = dbx_global_query(pmeth, cx->pqr_next, cx->pqr_prev, -1, cx->getdata); 670 | 671 | DBX_DBFUN_END(c); 672 | DBX_DB_UNLOCK(); 673 | 674 | if (cx->format == 1) { 675 | char buffer[32], delim[4]; 676 | 677 | cx->data.len_used = 0; 678 | *delim = '\0'; 679 | for (n = 0; n < cx->pqr_next->keyn; n ++) { 680 | sprintf(buffer, (char *) "%skey%d=", delim, n + 1); 681 | dbx_escape_output(&(cx->data), buffer, (int) strlen(buffer), 0); 682 | pcon->utf16 ? dbx_escape_output16(&(cx->data), cx->pqr_next->keys[n].cvalue.buf16_addr, cx->pqr_next->keys[n].cvalue.len_used, 1) : dbx_escape_output(&(cx->data), cx->pqr_next->ykeys[n].buf_addr, cx->pqr_next->ykeys[n].len_used, 1); 683 | strcpy(delim, (char *) "&"); 684 | } 685 | if (cx->getdata) { 686 | sprintf(buffer, (char *) "%sdata=", delim); 687 | dbx_escape_output(&(cx->data), buffer, (int) strlen(buffer), 0); 688 | pcon->utf16 ? dbx_escape_output16(&(cx->data), cx->pqr_next->data.cvalue.buf16_addr, cx->pqr_next->data.cvalue.len_used, 1) : dbx_escape_output(&(cx->data), cx->pqr_next->data.svalue.buf_addr, cx->pqr_next->data.svalue.len_used, 1); 689 | } 690 | 691 | key = dbx_new_string8n(isolate, (char *) cx->data.buf_addr, cx->data.len_used, 0); 692 | args.GetReturnValue().Set(key); 693 | } 694 | else { 695 | obj = DBX_OBJECT_NEW(); 696 | /* 697 | key = dbx_new_string8(isolate, (char *) "global", 0); 698 | DBX_SET(obj, key, pcon->utf16 ? dbx_new_string16n(isolate, cx->pqr_next->global_name16.cvalue.buf16_addr, cx->pqr_next->global_name16.cvalue.len_used) : dbx_new_string8(isolate, cx->pqr_next->global_name.buf_addr, 0)); 699 | */ 700 | key = dbx_new_string8(isolate, (char *) "key", 0); 701 | Local a = DBX_ARRAY_NEW(cx->pqr_next->keyn); 702 | DBX_SET(obj, key, a); 703 | for (n = 0; n < cx->pqr_next->keyn; n ++) { 704 | DBX_SET(a, n, pcon->utf16 ? dbx_new_string16n(isolate, cx->pqr_next->keys[n].cvalue.buf16_addr, cx->pqr_next->keys[n].cvalue.len_used) : dbx_new_string8n(isolate, cx->pqr_next->ykeys[n].buf_addr, cx->pqr_next->ykeys[n].len_used, 0)); 705 | } 706 | if (cx->getdata) { 707 | key = dbx_new_string8(isolate, (char *) "data", 0); 708 | DBX_SET(obj, key, pcon->utf16 ? dbx_new_string16n(isolate, cx->pqr_next->data.cvalue.buf16_addr, cx->pqr_next->data.cvalue.len_used) : dbx_new_string8n(isolate, cx->pqr_next->data.svalue.buf_addr, cx->pqr_next->data.svalue.len_used, 0)); 709 | } 710 | } 711 | 712 | pqr = cx->pqr_next; 713 | cx->pqr_next = cx->pqr_prev; 714 | cx->pqr_prev = pqr; 715 | 716 | if (eod == CACHE_SUCCESS) { 717 | if (cx->format == 1) 718 | args.GetReturnValue().Set(key); 719 | else 720 | args.GetReturnValue().Set(obj); 721 | } 722 | else { 723 | args.GetReturnValue().Set(DBX_NULL()); 724 | } 725 | dbx_request_memory_free(pcon, pmeth, 0); 726 | return; 727 | } 728 | else if (cx->context == 9) { 729 | 730 | /* v2.1.18 */ 731 | DBX_DBFUN_START(c, pcon, pmeth); 732 | DBX_DB_LOCK(0); 733 | 734 | eod = dbx_global_directory(pmeth, cx->pqr_prev, -1, &(cx->counter)); 735 | 736 | DBX_DBFUN_END(c); 737 | DBX_DB_UNLOCK(); 738 | 739 | if (eod) { 740 | args.GetReturnValue().Set(DBX_NULL()); 741 | } 742 | else { 743 | key = pcon->utf16 ? dbx_new_string16n(isolate, cx->pqr_prev->global_name16.cvalue.buf16_addr, cx->pqr_prev->global_name16.cvalue.len_used) : dbx_new_string8n(isolate, cx->pqr_prev->global_name.buf_addr, cx->pqr_prev->global_name.len_used, pcon->utf8); 744 | args.GetReturnValue().Set(key); 745 | } 746 | dbx_request_memory_free(pcon, pmeth, 0); 747 | return; 748 | } 749 | else if (cx->context == 11) { 750 | 751 | pmeth->psql = cx->psql; 752 | 753 | if (!pmeth->psql) { 754 | args.GetReturnValue().Set(DBX_NULL()); 755 | dbx_request_memory_free(pcon, pmeth, 0); 756 | return; 757 | } 758 | 759 | eod = dbx_sql_row(pmeth, pmeth->psql->row_no, -1); 760 | if (eod) { 761 | args.GetReturnValue().Set(DBX_NULL()); 762 | } 763 | else { 764 | int len, dsort, dtype; 765 | 766 | obj = DBX_OBJECT_NEW(); 767 | 768 | for (n = 0; n < pmeth->psql->no_cols; n ++) { 769 | len = (int) dbx_get_block_size((unsigned char *) pmeth->output_val.svalue.buf_addr, pmeth->output_val.offs, &dsort, &dtype); 770 | pmeth->output_val.offs += 5; 771 | 772 | /* printf("\r\n ROW DATA: n=%d; len=%d; offset=%d; sort=%d; type=%d; str=%s;", n, len, pmeth->output_val.offs, dsort, dtype, pmeth->output_val.svalue.buf_addr + pmeth->output_val.offs); */ 773 | 774 | if (dsort == DBX_DSORT_EOD || dsort == DBX_DSORT_ERROR) { 775 | break; 776 | } 777 | 778 | key = dbx_new_string8n(isolate, (char *) pmeth->psql->cols[n]->name.buf_addr, pmeth->psql->cols[n]->name.len_used, 0); 779 | DBX_SET(obj, key, dbx_new_string8n(isolate, pmeth->output_val.svalue.buf_addr + pmeth->output_val.offs, len, 0)); 780 | pmeth->output_val.offs += len; 781 | } 782 | args.GetReturnValue().Set(obj); 783 | } 784 | dbx_request_memory_free(pcon, pmeth, 0); 785 | return; 786 | } 787 | else { 788 | args.GetReturnValue().Set(DBX_NULL()); 789 | } 790 | dbx_request_memory_free(pcon, pmeth, 0); 791 | return; 792 | } 793 | 794 | 795 | void mcursor::Reset(const FunctionCallbackInfo& args) 796 | { 797 | int rc; 798 | DBXCON *pcon; 799 | DBXMETH *pmeth; 800 | mcursor *cx = ObjectWrap::Unwrap(args.This()); 801 | MG_CURSOR_CHECK_CLASS(cx); 802 | DBX_DBNAME *c = cx->c; 803 | DBX_GET_ISOLATE; 804 | cx->dbx_count ++; 805 | 806 | pcon = c->pcon; 807 | if (pcon->log_functions) { 808 | c->LogFunction(c, args, (void *) cx, (char *) "mcursor::reset"); 809 | } 810 | pmeth = dbx_request_memory(pcon, 1, 1); 811 | 812 | pmeth->argc = args.Length(); 813 | 814 | if (pmeth->argc < 1) { 815 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "The mglobalquery.reset() method takes at least one argument (the global reference to start with)", 1))); 816 | dbx_request_memory_free(pcon, pmeth, 0); 817 | return; 818 | } 819 | 820 | /* 1.4.10 */ 821 | rc = dbx_cursor_reset(args, isolate, pcon, pmeth, (void *) cx, 0, 0); 822 | if (rc < 0) { 823 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "The mglobalquery.reset() method takes at least one argument (the global reference to start with)", 1))); 824 | dbx_request_memory_free(pcon, pmeth, 0); 825 | return; 826 | } 827 | 828 | dbx_request_memory_free(pcon, pmeth, 0); 829 | return; 830 | } 831 | 832 | 833 | void mcursor::Close(const FunctionCallbackInfo& args) 834 | { 835 | int cn; 836 | DBXCON *pcon; 837 | DBXMETH *pmeth; 838 | mcursor *cx = ObjectWrap::Unwrap(args.This()); 839 | MG_CURSOR_CHECK_CLASS(cx); 840 | DBX_DBNAME *c = cx->c; 841 | DBX_GET_ISOLATE; 842 | cx->dbx_count ++; 843 | 844 | pcon = c->pcon; 845 | if (pcon->log_functions) { 846 | c->LogFunction(c, args, (void *) cx, (char *) "mcursor::close"); 847 | } 848 | pmeth = dbx_request_memory(pcon, 1, 0); 849 | 850 | pmeth->argc = args.Length(); 851 | 852 | if (pmeth->argc >= DBX_MAXARGS) { 853 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "Too many arguments", 1))); 854 | dbx_request_memory_free(pcon, pmeth, 0); 855 | return; 856 | } 857 | if (pmeth->argc > 0) { 858 | isolate->ThrowException(Exception::Error(dbx_new_string8(isolate, (char *) "Closing a cursor template does not take any arguments", 1))); 859 | dbx_request_memory_free(pcon, pmeth, 0); 860 | return; 861 | } 862 | 863 | if (cx->pqr_next) { 864 | if (cx->pqr_next->data.svalue.buf_addr) { 865 | dbx_free((void *) cx->pqr_next->data.svalue.buf_addr, 0); 866 | cx->pqr_next->data.svalue.buf_addr = NULL; 867 | cx->pqr_next->data.svalue.len_alloc = 0; 868 | cx->pqr_next->data.svalue.len_used = 0; 869 | } 870 | dbx_free((void *) cx->pqr_next, 0); 871 | cx->pqr_next = NULL; 872 | } 873 | 874 | if (cx->pqr_prev) { 875 | if (cx->pqr_prev->data.svalue.buf_addr) { 876 | dbx_free((void *) cx->pqr_prev->data.svalue.buf_addr, 0); 877 | cx->pqr_prev->data.svalue.buf_addr = NULL; 878 | cx->pqr_prev->data.svalue.len_alloc = 0; 879 | cx->pqr_prev->data.svalue.len_used = 0; 880 | } 881 | dbx_free((void *) cx->pqr_prev, 0); 882 | cx->pqr_prev = NULL; 883 | } 884 | 885 | if (cx->psql) { 886 | for (cn = 0; cn < cx->psql->no_cols; cn ++) { 887 | if (cx->psql->cols[cn]) { 888 | dbx_free((void *) cx->psql->cols[cn], 0); 889 | cx->psql->cols[cn] = NULL; 890 | } 891 | } 892 | dbx_free((void *) cx->psql, 0); 893 | cx->psql = NULL; 894 | } 895 | 896 | /* 897 | cx->delete_mcursor_template(cx); 898 | */ 899 | dbx_request_memory_free(pcon, pmeth, 0); 900 | return; 901 | } 902 | 903 | int dbx_escape_output(DBXSTR *pdata, char *item, int item_len, short context) 904 | { 905 | int n; 906 | 907 | if (context == 0) { 908 | for (n = 0; n < item_len; n ++) { 909 | pdata->buf_addr[pdata->len_used ++] = item[n]; 910 | } 911 | return pdata->len_used; 912 | } 913 | 914 | for (n = 0; n < item_len; n ++) { 915 | if (item[n] == '&') { 916 | pdata->buf_addr[pdata->len_used ++] = '%'; 917 | pdata->buf_addr[pdata->len_used ++] = '2'; 918 | pdata->buf_addr[pdata->len_used ++] = '6'; 919 | } 920 | else if (item[n] == '=') { 921 | pdata->buf_addr[pdata->len_used ++] = '%'; 922 | pdata->buf_addr[pdata->len_used ++] = '3'; 923 | pdata->buf_addr[pdata->len_used ++] = 'D'; 924 | } 925 | else { 926 | pdata->buf_addr[pdata->len_used ++] = item[n]; 927 | } 928 | } 929 | return pdata->len_used; 930 | } 931 | 932 | 933 | int dbx_escape_output16(DBXSTR *pdata, unsigned short *item, int item_len, short context) 934 | { 935 | int n; 936 | 937 | /* TODO escape Unicode characters */ 938 | if (context == 0) { 939 | for (n = 0; n < item_len; n ++) { 940 | pdata->buf_addr[pdata->len_used ++] = (char) item[n]; 941 | } 942 | return pdata->len_used; 943 | } 944 | 945 | for (n = 0; n < item_len; n ++) { 946 | if (item[n] == 38) { /* & */ 947 | pdata->buf_addr[pdata->len_used ++] = '%'; 948 | pdata->buf_addr[pdata->len_used ++] = '2'; 949 | pdata->buf_addr[pdata->len_used ++] = '6'; 950 | } 951 | else if (item[n] == 61) { /* = */ 952 | pdata->buf_addr[pdata->len_used ++] = '%'; 953 | pdata->buf_addr[pdata->len_used ++] = '3'; 954 | pdata->buf_addr[pdata->len_used ++] = 'D'; 955 | } 956 | else { 957 | pdata->buf_addr[pdata->len_used ++] = (char) item[n]; 958 | } 959 | } 960 | return pdata->len_used; 961 | } 962 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mg-dbx 2 | 3 | High speed Synchronous and Asynchronous access to InterSystems Cache/IRIS and YottaDB from Node.js. 4 | 5 | Chris Munt 6 | 9 December 2025, MGateway Ltd [http://www.mgateway.com](http://www.mgateway.com) 7 | 8 | * Verified to work with Node.js v8 to v24. 9 | * Two connectivity models to the InterSystems or YottaDB database are provided: High performance via the local database API or network based. 10 | * [Release Notes](#RelNotes) can be found at the end of this document. 11 | 12 | Contents 13 | 14 | * [Pre-requisites](#PreReq") 15 | * [Installing mg-dbx](#Install) 16 | * [Connecting to the database](#Connect) 17 | * [Invocation of database commands](#DBCommands) 18 | * [Invocation of database functions](#DBFunctions) 19 | * [Cursor based data retrieval](#Cursors) 20 | * [Transaction Processing](#TProcessing) 21 | * [Direct access to InterSystems classes (IRIS and Cache)](#DBClasses) 22 | * [Direct access to SQL: MGSQL and InterSystems SQL (IRIS and Cache)](#DBSQL) 23 | * [Working with binary data](#Binary) 24 | * [Using Node.js/V8 worker threads](#Threads) 25 | * [The Event Log](#EventLog) 26 | * [License](#License) 27 | 28 | ## Pre-requisites 29 | 30 | **mg-dbx** is a Node.js addon written in C++. It is distributed as C++ source code and the NPM installation procedure will expect a C++ compiler to be present on the target system. 31 | 32 | Linux systems can use the freely available GNU C++ compiler (g++) which can be installed as follows. 33 | 34 | Ubuntu: 35 | 36 | apt-get install g++ 37 | 38 | Red Hat and CentOS: 39 | 40 | yum install gcc-c++ 41 | 42 | Apple OS X can use the freely available **Xcode** development environment. 43 | 44 | There are two options for Windows, both of which are free: 45 | 46 | * Microsoft Visual Studio Community: [https://www.visualstudio.com/vs/community/](https://www.visualstudio.com/vs/community/) 47 | * MinGW: [http://www.mingw.org/](http://www.mingw.org/) 48 | 49 | If the Windows machine is not set up for systems development, building native Addon modules for this platform from C++ source can be quite arduous. There is some helpful advice available at: 50 | 51 | * [Compiling native Addon modules for Windows](https://github.com/Microsoft/nodejs-guidelines/blob/master/windows-environment.md#compiling-native-addon-modules) 52 | 53 | Alternatively there are built Windows x64 binaries available from: 54 | 55 | * [https://github.com/chrisemunt/mg-dbx/blob/master/bin/winx64](https://github.com/chrisemunt/mg-dbx/blob/master/bin/winx64) 56 | 57 | ## Installing mg-dbx 58 | 59 | Assuming that Node.js is already installed and a C++ compiler is available to the installation process: 60 | 61 | npm install mg-dbx 62 | 63 | This command will create the **mg-dbx** addon (*mg-dbx.node*). 64 | 65 | ### Enabling the API for InterSystems IRIS and Cache 66 | 67 | Before connecting to a local (relative to Node.js) InterSystems database via its API, check that the InterSystems ***'Callin API'*** is enabled. From the InterSystems Database Management Portal select ***System Administration***; then ***Security***; then ***Services***. Look for the row containing ***%Service_Callin*** and check that it is ***Enabled***. Edit the row and mark this service as ***Enabled*** if necessary. 68 | 69 | ### Installing the M support routines (also known as the DB Superserver) 70 | 71 | The M support routines are required for: 72 | 73 | * Network based access to databases. 74 | * Direct access to SQL (either via the API or via the network). 75 | * The Merge command under YottaDB (either via the API or via the network). 76 | 77 | If none of the above apply you do not need to install these routines - proceed to [Connecting to the database](#Connect). 78 | 79 | Two M routines need to be installed (%zmgsi and %zmgsis). These can be found in the *Service Integration Gateway* (**mgsi**) GitHub source code repository ([https://github.com/chrisemunt/mgsi](https://github.com/chrisemunt/mgsi)). Note that it is not necessary to install the whole *Service Integration Gateway*, just the two M routines held in that repository. 80 | 81 | #### Installation for InterSystems Cache/IRIS 82 | 83 | Log in to the %SYS Namespace and install the **zmgsi** routines held in **/isc/zmgsi\_isc.ro**. 84 | 85 | do $system.OBJ.Load("/isc/zmgsi_isc.ro","ck") 86 | 87 | Change to your development UCI and check the installation: 88 | 89 | do ^%zmgsi 90 | 91 | MGateway Ltd - Service Integration Gateway 92 | Version: 4.5; Revision 31 (18 November 2023) 93 | 94 | 95 | #### Installation for YottaDB 96 | 97 | The instructions given here assume a standard 'out of the box' installation of **YottaDB** (version 1.38) deployed in the following location: 98 | 99 | /usr/local/lib/yottadb/r138 100 | 101 | The primary default location for routines: 102 | 103 | /root/.yottadb/r1.38_x86_64/r 104 | 105 | Copy all the routines (i.e. all files with an 'm' extension) held in the GitHub **/yottadb** directory to: 106 | 107 | /root/.yottadb/r1.38_x86_64/r 108 | 109 | Change directory to the following location and start a **YottaDB** command shell: 110 | 111 | cd /usr/local/lib/yottadb/r138 112 | ./ydb 113 | 114 | Link all the **zmgsi** routines and check the installation: 115 | 116 | do ylink^%zmgsi 117 | 118 | do ^%zmgsi 119 | 120 | MGateway Ltd - Service Integration Gateway 121 | Version: 4.5; Revision 31 (18 November 2023) 122 | 123 | Note that the version of **zmgsi** is successfully displayed. 124 | 125 | Finally, add the following lines to the interface file (**zmgsi.ci** in the example used in the db.open() method). 126 | 127 | sqlemg: ydb_string_t * sqlemg^%zmgsis(I:ydb_string_t*, I:ydb_string_t *, I:ydb_string_t *) 128 | sqlrow: ydb_string_t * sqlrow^%zmgsis(I:ydb_string_t*, I:ydb_string_t *, I:ydb_string_t *) 129 | sqldel: ydb_string_t * sqldel^%zmgsis(I:ydb_string_t*, I:ydb_string_t *) 130 | ifc_zmgsis: ydb_string_t * ifc^%zmgsis(I:ydb_string_t*, I:ydb_string_t *, I:ydb_string_t*) 131 | 132 | A copy of this file can be downloaded from the **/unix** directory of the **mgsi** GitHub repository [here](https://github.com/chrisemunt/mgsi) 133 | 134 | ### Starting the DB Superserver (for network based connectivity only) 135 | 136 | The default TCP server port for **zmgsi** is **7041**. If you wish to use an alternative port then modify the following instructions accordingly. 137 | 138 | * For InterSystems DB servers the concurrent TCP service should be started in the **%SYS** Namespace. 139 | 140 | Start the DB Superserver using the following command: 141 | 142 | do start^%zmgsi(0) 143 | 144 | To use a server TCP port other than 7041, specify it in the start-up command (as opposed to using zero to indicate the default port of 7041). 145 | 146 | * For YottaDB, as an alternative to starting the DB Superserver from the command prompt, Superserver processes can be started via the **xinetd** daemon. Instructions for configuring this option can be found in the **mgsi** repository [here](https://github.com/chrisemunt/mgsi) 147 | 148 | 149 | ## Connecting to the database 150 | 151 | Most **mg-dbx** methods are capable of operating either synchronously or asynchronously. For an operation to complete asynchronously, simply supply a suitable callback as the last argument in the call. 152 | 153 | The first step is to add **mg-dbx** to your Node.js script 154 | 155 | var dbx = require('mg-dbx').dbx; 156 | 157 | And optionally (as required): 158 | 159 | var mglobal = require('mg-dbx').mglobal; 160 | var mcursor = require('mg-dbx').mcursor; 161 | var mclass = require('mg-dbx').mclass; 162 | 163 | ### Create a Server Object 164 | 165 | var db = new dbx(); 166 | 167 | 168 | ### Open a connection to the database 169 | 170 | In the following examples, modify all paths (and any user names and passwords) to match those of your own installation. 171 | 172 | #### InterSystems Cache 173 | 174 | ##### API based connectivity 175 | 176 | Assuming Cache is installed under **/opt/cache20181/** 177 | 178 | var open = db.open({ 179 | type: "Cache", 180 | path:"/opt/cache20181/mgr", 181 | username: "_SYSTEM", 182 | password: "SYS", 183 | namespace: "USER" 184 | }); 185 | 186 | ##### Network based connectivity 187 | 188 | Assuming Cache is accessed via **localhost** listening on TCP port **7041** 189 | 190 | var open = db.open({ 191 | type: "Cache", 192 | host: "localhost", 193 | tcp_port: 7041, 194 | username: "_SYSTEM", 195 | password: "SYS", 196 | namespace: "USER" 197 | }); 198 | 199 | 200 | #### InterSystems IRIS 201 | 202 | ##### API based connectivity 203 | 204 | Assuming IRIS is installed under **/opt/IRIS20181/** 205 | 206 | var open = db.open({ 207 | type: "IRIS", 208 | path:"/opt/IRIS20181/mgr", 209 | username: "_SYSTEM", 210 | password: "SYS", 211 | namespace: "USER" 212 | }); 213 | 214 | ##### Network based connectivity 215 | 216 | Assuming IRIS is accessed via **localhost** listening on TCP port **7041** 217 | 218 | var open = db.open({ 219 | type: "IRIS", 220 | host: "localhost", 221 | tcp_port: 7041, 222 | username: "_SYSTEM", 223 | password: "SYS", 224 | namespace: "USER" 225 | }); 226 | 227 | #### YottaDB 228 | 229 | ##### API based connectivity 230 | 231 | Assuming an 'out of the box' YottaDB installation under **/usr/local/lib/yottadb/r138**. 232 | 233 | var envvars = ""; 234 | envvars = envvars + "ydb_dir=/root/.yottadb\n" 235 | envvars = envvars + "ydb_rel=r1.38_x86_64\n" 236 | envvars = envvars + "ydb_gbldir=/root/.yottadb/r1.38_x86_64/g/yottadb.gld\n" 237 | envvars = envvars + "ydb_routines=/root/.yottadb/r1.38_x86_64/o*(/root/.yottadb/r1.38_x86_64/r /root/.yottadb/r) /usr/local/lib/yottadb/r138/libyottadbutil.so\n" 238 | envvars = envvars + "ydb_ci=/usr/local/lib/yottadb/r138/zmgsi.ci\n" 239 | envvars = envvars + "\n" 240 | 241 | var open = db.open({ 242 | type: "YottaDB", 243 | path: "/usr/local/lib/yottadb/r138", 244 | env_vars: envvars 245 | }); 246 | 247 | ##### Network based connectivity 248 | 249 | Assuming YottaDB is accessed via **localhost** listening on TCP port **7041** 250 | 251 | var open = db.open({ 252 | type: "YottaDB", 253 | host: "localhost", 254 | tcp_port: 7041, 255 | }); 256 | 257 | 258 | #### Additional (optional) properties for the open() method 259 | 260 | * **multithreaded**: A boolean value to be set to 'true' or 'false' (default: **multithreaded: true**). Set this property to 'true' if the application uses multithreaded techniques in JavaScript (e.g. V8 worker threads). 261 | 262 | * **timeout**: The timeout (in seconds) to be applied to database operations invoked via network based connections. The default value is 10 seconds. 263 | 264 | * **dberror\_exceptions**: A boolean value to be set to 'true' or 'false' (default: **dberror_exceptions: false**). Set this property to 'true' to instruct **mg\-dbx** to throw Node.js exceptions if synchronous invocation of database operations result in an error condition. If this property is not set, any error condition resulting from the previous database operation can be retrieved using the **db.geterrormessage()** method. 265 | 266 | 267 | ### Return the version of mg-dbx 268 | 269 | var result = db.version(); 270 | 271 | Example: 272 | 273 | console.log("\nmg-dbx Version: " + db.version()); 274 | 275 | 276 | ### Returning (and optionally changing) the current directory (or Namespace) 277 | 278 | current_namespace = db.namespace([]); 279 | 280 | Example 1 (Get the current Namespace): 281 | 282 | var nspace = db.namespace(); 283 | 284 | * Note this will return the current Namespace for InterSystems databases and the value of the current global directory for YottaDB (i.e. $ZG). 285 | 286 | Example 2 (Change the current Namespace): 287 | 288 | var new_nspace = db.namespace("SAMPLES"); 289 | 290 | * If the operation is successful this method will echo back the new Namespace name. If not successful, the method will return the name of the current (unchanged) Namespace. 291 | 292 | 293 | ### Returning (and optionally changing) the current character set 294 | 295 | UTF-8 is the default character encoding for **mg-dbx**. The other option is the 8-bit ASCII character set (characters of the range ASCII 0 to ASCII 255). Native Unicode (as UTF-16) is supported for InterSystems DB Servers. The ASCII character set is a better option when exchanging single-byte binary data with the database. 296 | 297 | current_charset = db.charset([]); 298 | 299 | Example 1 (Get the current character set): 300 | 301 | var charset = db.charset(); 302 | 303 | Example 2 (Change the current character set): 304 | 305 | var new_charset = db.charset('ascii'); 306 | 307 | Example 3 (Native Unicode support for InterSystems DB Servers): 308 | 309 | var new_charset = db.charset('utf-16'); 310 | 311 | * If the operation is successful this method will echo back the new character set name. If not successful, the method will return the name of the current (unchanged) character set. 312 | * Currently supported character sets and encoding schemes: 'ascii', 'utf-8' and 'utf-16' for InterSystems DB Servers. 313 | 314 | 315 | ### Setting (or resetting) the timeout for the connection 316 | 317 | new_timeout = db.settimeout(); 318 | 319 | Specify a new timeout value (in seconds) for the connection. If the operation is successful this method will return the new value for the timeout. 320 | 321 | Example (Set the timeout to 30 seconds): 322 | 323 | var new_timeout = db.settimeout(30); 324 | 325 | 326 | ### Get the error message associated with the previous database operation 327 | 328 | error_message = db.geterrormessage(); 329 | 330 | This method will return the error message (as a string) associated with the previous database operation. An empty string will be returned if the previous operation completed successfully. 331 | 332 | 333 | ### Close database connection 334 | 335 | db.close(); 336 | 337 | 338 | ## Invocation of database commands 339 | 340 | ### Register a global name (and fixed key) 341 | 342 | 343 | global = new mglobal(db, [, ]); 344 | Or: 345 | 346 | global = db.mglobal([, ]); 347 | 348 | Example (using a global named "Person"): 349 | 350 | var person = db.mglobal("Person"); 351 | 352 | ### Set a record 353 | 354 | Synchronous: 355 | 356 | var result = .set(, ); 357 | 358 | Asynchronous: 359 | 360 | .set(, , callback(, )); 361 | 362 | Example: 363 | 364 | person.set(1, "John Smith"); 365 | 366 | ### Get a record 367 | 368 | Synchronous: 369 | 370 | var result = .get(); 371 | 372 | Asynchronous: 373 | 374 | .get(, callback(, )); 375 | 376 | Example: 377 | 378 | var name = person.get(1); 379 | 380 | * Note: use **get\_bx** to receive the result as a Node.js Buffer. 381 | 382 | ### Delete a record 383 | 384 | Synchronous: 385 | 386 | var result = .delete(); 387 | 388 | Asynchronous: 389 | 390 | .delete(, callback(, )); 391 | 392 | Example: 393 | 394 | var name = person.delete(1); 395 | 396 | 397 | ### Check whether a record is defined 398 | 399 | Synchronous: 400 | 401 | var result = .defined(); 402 | 403 | Asynchronous: 404 | 405 | .defined(, callback(, )); 406 | 407 | Example: 408 | 409 | var name = person.defined(1); 410 | 411 | 412 | ### Parse a set of records (in order) 413 | 414 | Synchronous: 415 | 416 | var result = .next(); 417 | 418 | Asynchronous: 419 | 420 | .next(, callback(, )); 421 | 422 | Example: 423 | 424 | var key = ""; 425 | while ((key = person.next(key)) != "") { 426 | console.log("\nPerson: " + key + ' : ' + person.get(key)); 427 | } 428 | 429 | 430 | ### Parse a set of records (in reverse order) 431 | 432 | Synchronous: 433 | 434 | var result = .previous(); 435 | 436 | Asynchronous: 437 | 438 | .previous(, callback(, )); 439 | 440 | Example: 441 | 442 | var key = ""; 443 | while ((key = person.previous(key)) != "") { 444 | console.log("\nPerson: " + key + ' : ' + person.get(key)); 445 | } 446 | 447 | 448 | ### Increment the value of a global node 449 | 450 | Synchronous: 451 | 452 | var result = .increment(, ); 453 | 454 | Asynchronous: 455 | 456 | .increment(, , callback(, )); 457 | 458 | Example (increment the value of the "counter" node by 1.5 and return the new value): 459 | 460 | var result = person.increment("counter", 1.5); 461 | 462 | 463 | ### Lock a global node 464 | 465 | Synchronous: 466 | 467 | var result = .lock(, ); 468 | 469 | Asynchronous: 470 | 471 | .lock(, , callback(, )); 472 | 473 | Example (lock global node '1' with a timeout of 30 seconds): 474 | 475 | var result = person.lock(1, 30); 476 | 477 | * Note: Specify the timeout value as '-1' for no timeout (i.e. wait until the global node becomes available to lock). 478 | 479 | 480 | ### Unlock a (previously locked) global node 481 | 482 | Synchronous: 483 | 484 | var result = .unlock(); 485 | 486 | Asynchronous: 487 | 488 | .unlock(, callback(, )); 489 | 490 | Example (unlock global node '1'): 491 | 492 | var result = person.unlock(1); 493 | 494 | 495 | ### Merge (or copy) part of one global to another 496 | 497 | * Note: In order to use the 'Merge' facility with YottaDB the M support routines should be installed (**%zmgsi** and **%zmgsis**). 498 | 499 | Synchronous (merge from global2 to global1): 500 | 501 | var result = .merge([,] [, ]); 502 | 503 | Asynchronous (merge from global2 to global1): 504 | 505 | .defined([,] [, ], callback(, )); 506 | 507 | Example 1 (merge ^MyGlobal2 to ^MyGlobal1): 508 | 509 | global1 = new mglobal(db, 'MyGlobal1'); 510 | global2 = new mglobal(db, 'MyGlobal2'); 511 | global1.merge(global2); 512 | 513 | Example 2 (merge ^MyGlobal2(0) to ^MyGlobal1(1)): 514 | 515 | global1 = new mglobal(db, 'MyGlobal1', 1); 516 | global2 = new mglobal(db, 'MyGlobal2', 0); 517 | global1.merge(global2); 518 | 519 | Alternatively: 520 | 521 | global1 = new mglobal(db, 'MyGlobal1'); 522 | global2 = new mglobal(db, 'MyGlobal2'); 523 | global1.merge(1, global2, 0); 524 | 525 | ### Reset a global name (and fixed key) 526 | 527 | .reset([, ]); 528 | 529 | Example: 530 | 531 | // Process orders for customer #1 532 | customer_orders = db.mglobal("Customer", 1, "orders") 533 | do_work ... 534 | 535 | // Process orders for customer #2 536 | customer_orders.reset("Customer", 2, "orders"); 537 | do_work ... 538 | 539 | 540 | ## Cursor based data retrieval 541 | 542 | This facility provides high-performance techniques for traversing records held in database globals. 543 | 544 | ### Specifying the query 545 | 546 | The first task is to specify the 'query' for the global traverse. 547 | 548 | query = new mcursor(db, {global: , key: []}[, {}]); 549 | Or: 550 | 551 | query = db.mglobalquery({global: , key: []}[, {}]); 552 | 553 | The 'options' object can contain the following properties: 554 | 555 | * **multilevel**: A boolean value (default: **multilevel: false**). Set to 'true' to return all descendant nodes from the specified 'seed_key'. 556 | 557 | * **getdata**: A boolean value (default: **getdata: false**). Set to 'true' to return any data values associated with each global node returned. 558 | 559 | * **format**: Format for output (default: not specified). If the output consists of multiple data elements, the return value (by default) is a JavaScript object made up of a 'key' array and an associated 'data' value. Set to "url" to return such data as a single URL escaped string including all key values ('key[1->n]') and any associated 'data' value. 560 | 561 | Example (return all keys and names from the 'Person' global): 562 | 563 | query = db.mglobalquery({global: "Person", key: [""]}, {multilevel: false, getdata: true}); 564 | 565 | ### Traversing the dataset 566 | 567 | In key order: 568 | 569 | result = query.next(); 570 | 571 | In reverse key order: 572 | 573 | result = query.previous(); 574 | 575 | In all cases these methods will return 'null' when the end of the dataset is reached. 576 | 577 | Example 1 (return all key values from the 'Person' global - returns a simple variable): 578 | 579 | query = db.mglobalquery({global: "Person", key: [""]}); 580 | while ((result = query.next()) !== null) { 581 | console.log("result: " + result); 582 | } 583 | 584 | Example 2 (return all key values and names from the 'Person' global - returns an object): 585 | 586 | query = db.mglobalquery({global: "Person", key: [""]}, {multilevel: false, getdata: true}); 587 | while ((result = query.next()) !== null) { 588 | console.log("result: " + JSON.stringify(result, null, '\t')); 589 | } 590 | 591 | 592 | Example 3 (return all key values and names from the 'Person' global - returns a string): 593 | 594 | query = db.mglobalquery({global: "Person", key: [""]}, {multilevel: false, getdata: true, format: "url"}); 595 | while ((result = query.next()) !== null) { 596 | console.log("result: " + result); 597 | } 598 | 599 | Example 4 (return all key values and names from the 'Person' global, including any descendant nodes): 600 | 601 | query = db.mglobalquery({global: "Person", key: [""]}, {multilevel: true, getdata: true}); 602 | while ((result = query.next()) !== null) { 603 | console.log("result: " + JSON.stringify(result, null, '\t')); 604 | } 605 | 606 | * M programmers will recognise this last example as the M **$Query()** command. 607 | 608 | 609 | ### Traversing the global directory (return a list of global names) 610 | 611 | query = db.mglobalquery({global: }, {globaldirectory: true}); 612 | 613 | Example (return all global names held in the current directory) 614 | 615 | query = db.mglobalquery({global: ""}, {globaldirectory: true}); 616 | while ((result = query.next()) !== null) { 617 | console.log("result: " + result); 618 | } 619 | 620 | 621 | ## Invocation of database functions 622 | 623 | Synchronous: 624 | 625 | result = db.function(, ); 626 | 627 | Asynchronous: 628 | 629 | db.function(, , callback(, )); 630 | 631 | Example: 632 | 633 | M routine called 'math': 634 | 635 | add(a, b) ; Add two numbers together 636 | quit (a+b) 637 | 638 | JavaScript invocation: 639 | 640 | result = db.function("add^math", 2, 3); 641 | 642 | 643 | * Note: use **function\_bx** to receive the result as a Node.js Buffer. 644 | 645 | 646 | ## Transaction Processing 647 | 648 | M DB Servers implement Transaction Processing by means of the methods described in this section. When implementing transactions, care should be taken with JavaScript operations that are invoked asynchronously. All the Transaction Processing methods describe here can only be invoked synchronously. 649 | 650 | 651 | ### Start a Transaction 652 | 653 | result = db.tstart(); 654 | 655 | * At this time, this method does not take any arguments. 656 | * On successful completion this method will return zero, or an error code on failure. 657 | 658 | Example: 659 | 660 | result = db.tstart(); 661 | 662 | 663 | ### Determine the Transaction Level 664 | 665 | result = db.tlevel(); 666 | 667 | * At this time, this method does not take any arguments. 668 | * Transactions can be nested and this method will return the level of nesting. If no Transaction is active this method will return zero. Otherwise a positive integer will be returned to represent the current depth of Transaction nesting. 669 | 670 | Example: 671 | 672 | tlevel = db.tlevel(); 673 | 674 | 675 | ### Commit a Transaction 676 | 677 | result = db.tcommit(); 678 | 679 | * At this time, this method does not take any arguments. 680 | * On successful completion this method will return zero, or an error code on failure. 681 | 682 | Example: 683 | 684 | result = db.tcommit(); 685 | 686 | 687 | ### Rollback a Transaction 688 | 689 | result = db.trollback(); 690 | 691 | * At this time, this method does not take any arguments. 692 | * On successful completion this method will return zero, or an error code on failure. 693 | 694 | Example: 695 | 696 | result = db.trollback(); 697 | 698 | 699 | ## Direct access to InterSystems classes (IRIS and Cache) 700 | 701 | ### Invocation of a ClassMethod 702 | 703 | Synchronous: 704 | 705 | result = new mclass(db, , , ); 706 | Or: 707 | 708 | result = db.classmethod(, , ); 709 | 710 | Asynchronous: 711 | 712 | db.classmethod(, , , callback(, )); 713 | 714 | Example (Encode a date to internal storage format): 715 | 716 | result = db.classmethod("%Library.Date", "DisplayToLogical", "10/10/2019"); 717 | 718 | * Note: use **classmethod\_bx** to receive the result as a Node.js Buffer. 719 | 720 | 721 | ### Creating and manipulating instances of objects 722 | 723 | The following simple class will be used to illustrate this facility. 724 | 725 | Class User.Person Extends %Persistent 726 | { 727 | Property Number As %Integer; 728 | Property Name As %String; 729 | Property DateOfBirth As %Date; 730 | Method Age(AtDate As %Integer) As %Integer 731 | { 732 | Quit (AtDate - ..DateOfBirth) \ 365.25 733 | } 734 | } 735 | 736 | ### Create an entry for a new Person 737 | 738 | person = db.classmethod("User.Person", "%New"); 739 | 740 | Add Data: 741 | 742 | result = person.setproperty("Number", 1); 743 | result = person.setproperty("Name", "John Smith"); 744 | result = person.setproperty("DateOfBirth", "12/8/1995"); 745 | 746 | Save the object record: 747 | 748 | result = person.method("%Save"); 749 | 750 | ### Retrieve an entry for an existing Person 751 | 752 | Retrieve data for object %Id of 1. 753 | 754 | person = db.classmethod("User.Person", "%OpenId", 1); 755 | 756 | Return properties: 757 | 758 | var number = person.getproperty("Number"); 759 | var name = person.getproperty("Name"); 760 | var dob = person.getproperty("DateOfBirth"); 761 | 762 | Calculate person's age at a particular date: 763 | 764 | today = db.classmethod("%Library.Date", "DisplayToLogical", "10/10/2019"); 765 | var age = person.method("Age", today); 766 | 767 | * Note: use **classmethod\_bx**, **method\_bx** and **getproperty\_bx** to receive data as a Node.js Buffer. 768 | 769 | ### Reusing an object container 770 | 771 | Once created, it is possible to reuse containers holding previously instantiated objects using the **reset()** method. Using this technique helps to reduce memory usage in the Node.js environment. 772 | 773 | Example 1 Reset a container to hold a new instance: 774 | 775 | person.reset("User.Person", "%New"); 776 | 777 | Example 2 Reset a container to hold an existing instance (object %Id of 2): 778 | 779 | person.reset("User.Person", "%OpenId", 2); 780 | 781 | 782 | ## Direct access to SQL: MGSQL and InterSystems SQL (IRIS and Cache) 783 | 784 | **mg-dbx** provides direct access to the Open Source MGSQL engine ([https://github.com/chrisemunt/mgsql](https://github.com/chrisemunt/mgsql)) and InterSystems SQL (IRIS and Cache). 785 | 786 | * Note: In order to use this facility the M support routines should be installed (**%zmgsi** and **%zmgsis**). 787 | 788 | ### Specifying the SQL query 789 | 790 | The first task is to specify the SQL query. 791 | 792 | query = new mcursor(db, {sql: [, type: ]}); 793 | Or: 794 | 795 | query = db.sql({sql: [, type: ]}); 796 | 797 | Example 1 (using MGSQL): 798 | 799 | query = db.sql({sql: "select * from person"}); 800 | 801 | 802 | Example 2 (using InterSystems SQL): 803 | 804 | query = db.sql({sql: "select * from SQLUser.person", type: "Cache"}); 805 | 806 | 807 | ### Execute an SQL query 808 | 809 | Synchronous: 810 | 811 | var result = .execute(); 812 | 813 | Asynchronous: 814 | 815 | .execute(callback(, )); 816 | 817 | 818 | The result of query execution is an object containing the return code and state and any associated error message. The familiar ODBC return and status codes are used. 819 | 820 | Example 1 (successful execution): 821 | 822 | { 823 | "sqlcode": 0, 824 | "sqlstate": "00000", 825 | "columns": [ 826 | { 827 | "name": "Number", 828 | "type": "INTEGER" 829 | }, 830 | "name": "Name", 831 | "type": "VARCHAR" 832 | }, 833 | "name": "DateOfBirth", 834 | "type": "DATE" 835 | } 836 | ] 837 | } 838 | 839 | 840 | Example 2 (unsuccessful execution): 841 | 842 | { 843 | "sqlcode": -1, 844 | "sqlstate": "HY000", 845 | "error": "no such table 'person'" 846 | } 847 | 848 | 849 | ### Traversing the returned dataset (SQL 'select' queries) 850 | 851 | In result-set order: 852 | 853 | result = query.next(); 854 | 855 | In reverse result-set order: 856 | 857 | result = query.previous(); 858 | 859 | In all cases these methods will return 'null' when the end of the dataset is reached. 860 | 861 | Example: 862 | 863 | while ((row = query.next()) !== null) { 864 | console.log("row: " + JSON.stringify(result, null, '\t')); 865 | } 866 | 867 | The output for each iteration is a row of the generated SQL result-set. For example: 868 | 869 | { 870 | "number": 1, 871 | "name": "John Smith", 872 | } 873 | 874 | ### SQL cleanup 875 | 876 | For 'select' queries that generate a result-set it is good practice to invoke the 'cleanup' method at the end to delete the result-set held in the database. 877 | 878 | Synchronous: 879 | 880 | var result = .cleanup(); 881 | 882 | Asynchronous: 883 | 884 | .cleanup(callback(, )); 885 | 886 | ### Reset an SQL container with a new SQL Query 887 | 888 | Synchronous: 889 | 890 | .reset({sql: [, type: ]); 891 | 892 | Asynchronous: 893 | 894 | .reset({sql: [, type: ], callback(, )); 895 | 896 | 897 | ## Working with binary data 898 | 899 | In **mg-dbx** the default character encoding scheme is UTF-8. When transmitting binary data between the database and Node.js there are two options. 900 | 901 | * Switch to using the 8-bit ASCII character set. 902 | * Receive the incoming data into Node.js Buffers. 903 | 904 | On the input (to the database) side all **mg-dbx** function arguments can be presented as Node.js Buffers and **mg-dbx** will automatically detect that an argument is a Buffer and process it accordingly. 905 | 906 | On the output side the following functions can be used to return the output as a Node.js Buffer. 907 | 908 | * dbx::function\_bx 909 | * dbx::classmethod\_bx 910 | 911 | * mglobal::get\_bx 912 | 913 | * mclass::classmethod\_bx 914 | * mclass::method\_bx 915 | * mclass::getproperty\_bx 916 | 917 | These functions work the same way as their non '_bx' suffixed counterparts. The only difference is that they will return data as a Node.js Buffer as opposed to a type of String. 918 | 919 | The following two examples illustrate the two schemes for receiving binary data from the database. 920 | 921 | Example 1: Receive binary data from a DB function as a Node.js 8-bit character stream 922 | 923 | .charset('ascii'); 924 | var stream_str8 = .function(, ); 925 | .charset('utf-8'); // reset character encoding 926 | 927 | Example 2: Receive binary data from a DB function as a Node.js Buffer 928 | 929 | var stream_buffer = .function_bx(, ); 930 | 931 | 932 | ## Using Node.js/V8 worker threads 933 | 934 | **mg-dbx** functionality can now be used with Node.js/V8 worker threads. This enhancement is available with Node.js v12 (and later). 935 | 936 | * Note: be sure to include the property **multithreaded: true** in the **open** method when opening database connections to be used in multi-threaded applications. 937 | 938 | Use the following constructs for instantiating **mg-dbx** objects in multi-threaded applications: 939 | 940 | // Use: 941 | var = new mglobal(, ); 942 | // Instead of: 943 | var = .mglobal(); 944 | 945 | // Use: 946 | var = new mcursor(, ); 947 | // Instead of: 948 | var = .mglobalquery() 949 | 950 | // Use: 951 | var = new mclass(, ); 952 | // Instead of: 953 | var = .classmethod(); 954 | 955 | // Use: 956 | var = new mcursor(, ); 957 | // Instead of: 958 | var = .sql(); 959 | 960 | 961 | The following scheme illustrates how **mg-dbx** should be used in threaded Node.js applications. 962 | 963 | const { Worker, isMainThread, parentPort, threadId } = require('worker_threads'); 964 | 965 | if (isMainThread) { 966 | // start the threads 967 | const worker1 = new Worker(__filename); 968 | const worker2 = new Worker(__filename); 969 | 970 | // process messages received from threads 971 | worker1.on('message', (message) => { 972 | console.log(message); 973 | }); 974 | worker2.on('message', (message) => { 975 | console.log(message); 976 | }); 977 | } else { 978 | var dbx = require('mg-dbx').dbx; 979 | // And as required ... 980 | var mglobal = require('mg-dbx').mglobal; 981 | var mcursor = require('mg-dbx').mcursor; 982 | var mclass = require('mg-dbx').mclass; 983 | 984 | var db = new dbx(); 985 | db.open(); 986 | 987 | var global = new mglobal(db, ); 988 | 989 | // do some work 990 | 991 | var result = db.close(); 992 | // tell the parent that we're done 993 | parentPort.postMessage("threadId=" + threadId + " Done"); 994 | } 995 | 996 | ## The Event Log 997 | 998 | **mg\-dbx** provides an Event Log facility for recording errors in a physical file and, as an aid to debugging, recording the **mg\-dbx** functions called by the application. This Log facility can also be used by Node.js applications. 999 | 1000 | To use this facility, the Event Log file must be specified using the following function: 1001 | 1002 | 1003 | db.setloglevel(, , ); 1004 | 1005 | Where: 1006 | 1007 | * **log\_file**: The name (and path to) the log file you wish to use. The default is c:/temp/mg-dbx.log (or /tmp/mg-dbx.log under UNIX). 1008 | * **log\_level**: A set of characters to include one or more of the following: 1009 | * **e** - Log error conditions. 1010 | * **f** - Log all **mg\-dbx** function calls (function name and arguments). 1011 | * **t** - Log the request data buffers to be transmitted from **mg\-dbx** to the DB Server. 1012 | * **r** - Log the request data buffers to be transmitted from **mg\-dbx\-bdb** to the DB Server and the corresponding response data. 1013 | * **log\_filter**: A comma-separated list of functions that you wish the log directive to be active for. This should be left empty to activate the log for all functions. 1014 | 1015 | Examples: 1016 | 1017 | db.setloglevel("c:/temp/mg-dbx.log", "e", ""); 1018 | db.setloglevel("/tmp/mg-dbx.log", "ft", "dbx::set,mglobal::set,mcursor::execute"); 1019 | 1020 | Node.js applications can write their own messages to the Event Log using the following function: 1021 | 1022 | db.logmessage(, ); 1023 | 1024 | Logging can be switched off by calling the **setloglevel** function without specifying a log level. For example: 1025 | 1026 | db.setloglevel("c:/temp/mg-dbx.log"); 1027 | 1028 | ## <a name="License"></a> License 1029 | 1030 | Copyright (c) 2018-2026 MGateway Ltd, 1031 | Surrey UK. 1032 | All rights reserved. 1033 | 1034 | http://www.mgateway.com 1035 | Email: cmunt@mgateway.com 1036 | 1037 | 1038 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at 1039 | 1040 | http://www.apache.org/licenses/LICENSE-2.0 1041 | 1042 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 1043 | 1044 | ## <a name="RelNotes"></a>Release Notes 1045 | 1046 | ### v1.0.3 (28 June 2019) 1047 | 1048 | * Initial Release 1049 | 1050 | ### v1.0.4 (7 September 2019) 1051 | 1052 | * Allow a global to be registered with a fixed leading key (i.e. leading fixed subscripts). 1053 | * Introduce a method to reset a global name (and any associated fixed keys). 1054 | 1055 | ### v1.1.5 (4 October 2019) 1056 | 1057 | * Introduce global 'increment()' and 'lock(); methods. 1058 | * Introduce cursor based data retrieval. 1059 | * Introduce outline support for multithreading in JavaScript - **currently not stable!**. 1060 | 1061 | ### v1.2.6 (10 October 2019) 1062 | 1063 | * Introduce support for direct access to InterSystems IRIS/Cache classes. 1064 | * Extend cursor based data retrieval to include an option for generating a global directory listing. 1065 | * Introduce a method to report and (optionally change) the current working global directory (or Namespace). 1066 | * Correct a fault that led to the timeout occasionally not being honoured in the **lock()** method. 1067 | * Correct a fault that led to Node.js exceptions not being processed correctly. 1068 | 1069 | ### v1.3.7 (1 November 2019) 1070 | 1071 | * Introduce support for direct access to InterSystems SQL and MGSQL. 1072 | * Correct a fault in the InterSystems Cache/IRIS API to globals that resulted in failures - notably in cases where there was a mix of string and numeric data in the global records. 1073 | 1074 | ### v1.3.8 (14 November 2019) 1075 | 1076 | * Correct a fault in the Global Increment method. 1077 | * Correct a fault that resulted in query.next() and query.previous() loops not terminating properly (with null) under YottaDB. This fault affected YottaDB releases after 1.30 1078 | * Modify the version() method so that it returns the version of YottaDB rather than the version of the underlying GT.M engine. 1079 | 1080 | ### v1.3.9 (26 February 2020) 1081 | 1082 | * Verify that **mg-dbx** will build and work with Node.js v13.x.x. 1083 | * Suppress a number of benign 'cast-function-type' compiler warnings when building on the Raspberry Pi. 1084 | 1085 | ### v1.3.9a (21 April 2020) 1086 | 1087 | * Verify that **mg-dbx** will build and work with Node.js v14.x.x. 1088 | 1089 | ### v1.4.10 (6 May 2020) 1090 | 1091 | * Introduce support for Node.js/V8 worker threads (for Node.js v12.x.x. and later). 1092 | * See the section on 'Using Node.js/V8 worker threads'. 1093 | * Introduce support for the M Merge command. 1094 | * Correct a fault in the mcursor 'Reset' method. 1095 | 1096 | ### v1.4.11 (14 May 2020) 1097 | 1098 | * Introduce a scheme for transmitting binary data between Node.js and the database. 1099 | * Correct a fault that led to some calls failing with incorrect data types after calls to the **mglobal::increment** method. 1100 | * **mg-dbx** will now pass arguments to YottaDB functions as **ydb\_string\_t** types and not **ydb\_char\_t**. Modify your YottaDB function interface file accordingly. See the section on 'Installing the M support routines'. 1101 | 1102 | ### v2.0.12 (25 May 2020) 1103 | 1104 | * Introduce the option to connect to a database over the network. 1105 | * Remove the 32K limit on the volume of data that can be sent to the database via the **mg-dbx** methods. 1106 | * Correct a fault that led to the failure of asynchronous calls to the **dbx::function** and **mglobal::previous** methods. 1107 | 1108 | ### v2.0.13 (8 June 2020) 1109 | 1110 | * Correct a fault in the processing of InterSystems Object References (orefs). 1111 | * This fault only affected applications using API-based connectivity to the database (as opposed to network-based connectivity). 1112 | * The fault could result in Node.js throwing 'Heap Corruption' errors after creating an instance of an InterSystems Object. 1113 | 1114 | ### v2.0.14 (17 June 2020) 1115 | 1116 | * Extend the processing of InterSystems Object References (orefs) to cater for instances of an object embedded as a property in other objects. For example, consider two classes: Patient and Doctor where an instance of a Doctor may be embedded in a Patient record (On the Server: "Property MyDoctor As Doctor"). And on the Node.js side... 1117 | 1118 | var patient = db.classmethod("User.Patient", "%OpenId", patient_id); 1119 | var doctor = patient.getproperty("MyDoctor"); 1120 | var doctor_name = doctor.getproperty("Name"); 1121 | 1122 | * Correct a fault in the processing of output values returned from YottaDB functions that led to output string values not being terminated correctly. The result being unexpected characters appended to function outputs. 1123 | 1124 | ### v2.0.15 (22 June 2020) 1125 | 1126 | * Correct a fault that could lead to fatal error conditions when creating new JS objects in multithreaded Node.js applications (i.e. when using Node.js/V8 worker threads). 1127 | 1128 | ### v2.0.16 (8 July 2020) 1129 | 1130 | * Correct a fault that could lead to **mg-dbx** incorrectly reporting _'Database not open'_ errors when connecting to YottaDB via its API in multithreaded Node.js applications. 1131 | 1132 | ### v2.1.17 (1 August 2020) 1133 | 1134 | * Introduce a log facility to record error conditions and run-time information to assist with debugging. 1135 | * Change the default for the **multihtreaded** property to be **true**. This can be set to **false** (in the **open()** method) if you are sure that your application does not use Node.js/V8 threading and does not call **mg\-dbx** functionality asynchronously. If in doubt, it is safer to leave this property set to **true**. 1136 | * A number of faults related to the use of **mg\-dbx** functionality in Node.js/v8 worker threads have been corrected. In particular, it was noticed that callback functions were not being fired correctly for some asynchronous invocations of **mg\-dbx** methods. 1137 | 1138 | ### v2.1.18 (12 August 2020) 1139 | 1140 | * Correct a fault that could lead to unpredictable behaviour and failures if more than one V8 worker thread concurrently requested a global directory listing. 1141 | * For example: query = new cursor(db, {global: ""}, {globaldirectory: true}); 1142 | * For SQL SELECT queries, return the column names and their associated data types. 1143 | * This metadata is presented as a **columns** array within the object returned from the SQL Execute method. 1144 | * The **columns** array is created in SELECT order. 1145 | * Attempt to capture Windows OS exceptions in the event log. 1146 | * The default event log is c:\temp\mg-dbx.log under Windows and /tmp/mg-dbx.log under UNIX. 1147 | 1148 | ### v2.1.19 (15 August 2020) 1149 | 1150 | * Update the internal UNIX library names for InterSystems IRIS and Cache. 1151 | * For information, the Cache library was renamed from libcache to libisccache and the IRIS library from libirisdb to libiscirisdb 1152 | * This change does not affect Windows platforms. 1153 | 1154 | ### v2.1.19a (13 November 2020) 1155 | 1156 | * Verify that **mg-dbx** will build and work with Node.js v15.x.x. 1157 | * Note that files that were previously held under the **/m**, **/yottadb**, and **/unix** directories are now available from the **mgsi** GitHub repository. These files are common to a number of my Open Source projects. 1158 | * [https://github.com/chrisemunt/mgsi](https://github.com/chrisemunt/mgsi) 1159 | 1160 | ### v2.1.20 (9 December 2020) 1161 | 1162 | * Correct a fault that occasionally led to failures in network-based connectivity between **mg\-dbx** and DB Servers. 1163 | 1164 | ### v2.2.21 (6 January 2021) 1165 | 1166 | * Allow a DB Server response timeout to be set for network based connectivity. 1167 | * Specify the **timeout** property in the open() method. 1168 | * Use the **db.settimeout()** method to set or reset the timeout value. 1169 | * Introduce an option to throw Node.js exceptions if synchronous calls to database operations result in an error condition (for example an M "SUBSCRIPT" or "SYNTAX" error). 1170 | * Specify the **dberror_exceptions** property in the **open()** method (default is **false**). 1171 | * Introduce a method to return any error message associated with the previous database operation. 1172 | * **var errormessage = db.geterrormessage()** 1173 | 1174 | ### v2.2.22 (18 January 2021) 1175 | 1176 | * Extend the logging of request transmission data to include the corresponding response data. 1177 | * Include 'r' in the log level. For example: db.setloglevel("MyLog.log", "eftr", ""); 1178 | * Correct a fault that occasionally led to failures when sending long strings (greater than 32K) to the DB Server. 1179 | * For example global.set('key1', 'key2', [string 2MB in length]); 1180 | * Correct a fault that occasionally led to failures when returning long strings to Node.js from the DB Server. 1181 | * This fault only affected network based connectivity to the DB Server. 1182 | 1183 | ### v2.3.23 (12 February 2021) 1184 | 1185 | * Introduce support for M Transaction Processing: tstart, $tlevel, tcommit, trollback. 1186 | 1187 | ### v2.3.24 (23 February 2021) 1188 | 1189 | * Correct a fault that resulted in a crash when loading the **mg-dbx** module in Node.js v10. 1190 | * This change only affects **mg-dbx** for Node.js v10. 1191 | 1192 | ### v2.3.25 (11 March 2021) 1193 | 1194 | * Introduce support for YottaDB Transaction Processing over API based connectivity. 1195 | * This functionality was previously only available over network-based connectivity to YottaDB. 1196 | 1197 | ### v2.3.25a (22 April 2021) 1198 | 1199 | * Verify that **mg-dbx** will build and work with Node.js v16.x.x. 1200 | 1201 | ### v2.4.26 (2 September 2021) 1202 | 1203 | * Introduce native Unicode support for InterSystems DB Servers - as character set/encoding UTF-16. 1204 | * db.charset('utf-16') 1205 | * For network-based connectivity, DB Superserver version 4.4.25 (or later) should be used. 1206 | * This update should be regarded as a beta-release. It is recommended that only those who need native support for Unicode on InterSystems DB Servers should upgrade to this release at this time. 1207 | 1208 | ### v2.4.27 (20 October 2021) 1209 | 1210 | * Verify that **mg-dbx** will build and work with Node.js v17.x.x. 1211 | 1212 | ### v2.4.27a (25 April 2022) 1213 | 1214 | * Verify that **mg-dbx** will build and work with Node.js v18.x.x. 1215 | 1216 | ### v2.4.27b (4 November 2022) 1217 | 1218 | * Verify that **mg-dbx** will build and work with Node.js v19.x.x. 1219 | 1220 | ### v2.4.27c (3 May 2023) 1221 | 1222 | * Verify that **mg-dbx** will build and work with Node.js v20.x.x. 1223 | 1224 | ### v2.4.27d (22 June 2023) 1225 | 1226 | * Documentation update. 1227 | 1228 | ### v2.4.28 (7 November 2023) 1229 | 1230 | * Correct a fault affecting the return of Unicode data to Node.js through SQL. 1231 | 1232 | ### v2.4.29 (21 May 2024) 1233 | 1234 | * Verify that **mg-dbx** will build and work with Node.js v22.x.x. 1235 | * Correct a fault in the InterSystems get and change namespace operations under network connections (db.namespace()). 1236 | 1237 | ### v2.4.30 (29 May 2025) 1238 | 1239 | * Verify that **mg-dbx** will build and work with Node.js v24.x.x. 1240 | 1241 | ### v2.4.31 (9 December 2025) 1242 | 1243 | * Correct a potential memory access violation in the **dbx.setloglevel()** method. 1244 | * Correct a potential memory access violation in the **mclass.reset()** method. 1245 | 1246 | -------------------------------------------------------------------------------- /src/mg-net.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ---------------------------------------------------------------------------- 3 | | mg-dbx.node | 4 | | Author: Chris Munt cmunt@mgateway.com | 5 | | chris.e.munt@gmail.com | 6 | | Copyright (c) 2019-2026 MGateway Ltd | 7 | | Surrey UK. | 8 | | All rights reserved. | 9 | | | 10 | | http://www.mgateway.com | 11 | | | 12 | | Licensed under the Apache License, Version 2.0 (the "License"); you may | 13 | | not use this file except in compliance with the License. | 14 | | You may obtain a copy of the License at | 15 | | | 16 | | http://www.apache.org/licenses/LICENSE-2.0 | 17 | | | 18 | | Unless required by applicable law or agreed to in writing, software | 19 | | distributed under the License is distributed on an "AS IS" BASIS, | 20 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 21 | | See the License for the specific language governing permissions and | 22 | | limitations under the License. | 23 | | | 24 | ---------------------------------------------------------------------------- 25 | */ 26 | 27 | #include "mg-dbx.h" 28 | #include "mg-net.h" 29 | 30 | #if !defined(_WIN32) 31 | extern int errno; 32 | #endif 33 | 34 | static NETXSOCK netx_so = {0, 0, 0, 0, 0, 0, 0, {'\0'}}; 35 | 36 | 37 | int netx_load_winsock(DBXCON *pcon, int context) 38 | { 39 | #if defined(_WIN32) 40 | int result, mem_locked; 41 | char buffer[1024]; 42 | 43 | result = 0; 44 | mem_locked = 0; 45 | *buffer = '\0'; 46 | netx_so.version_requested = 0; 47 | 48 | if (netx_so.load_attempted) { 49 | return result; 50 | } 51 | 52 | if (netx_so.load_attempted) { 53 | goto netx_load_winsock_no_so; 54 | } 55 | 56 | netx_so.sock = 0; 57 | 58 | /* Try to Load the Winsock 2 library */ 59 | 60 | netx_so.winsock = 2; 61 | strcpy(netx_so.libnam, "WS2_32.DLL"); 62 | 63 | netx_so.plibrary = dbx_dso_load(netx_so.libnam); 64 | 65 | if (!netx_so.plibrary) { 66 | netx_so.winsock = 1; 67 | strcpy(netx_so.libnam, "WSOCK32.DLL"); 68 | netx_so.plibrary = dbx_dso_load(netx_so.libnam); 69 | 70 | if (!netx_so.plibrary) { 71 | goto netx_load_winsock_no_so; 72 | } 73 | } 74 | 75 | netx_so.p_WSASocket = (MG_LPFN_WSASOCKET) dbx_dso_sym(netx_so.plibrary, (char *) "WSASocketA"); 76 | netx_so.p_WSAGetLastError = (MG_LPFN_WSAGETLASTERROR) dbx_dso_sym(netx_so.plibrary, (char *) "WSAGetLastError"); 77 | netx_so.p_WSAStartup = (MG_LPFN_WSASTARTUP) dbx_dso_sym(netx_so.plibrary, (char *) "WSAStartup"); 78 | netx_so.p_WSACleanup = (MG_LPFN_WSACLEANUP) dbx_dso_sym(netx_so.plibrary, (char *) "WSACleanup"); 79 | netx_so.p_WSAFDIsSet = (MG_LPFN_WSAFDISSET) dbx_dso_sym(netx_so.plibrary, (char *) "__WSAFDIsSet"); 80 | netx_so.p_WSARecv = (MG_LPFN_WSARECV) dbx_dso_sym(netx_so.plibrary, (char *) "WSARecv"); 81 | netx_so.p_WSASend = (MG_LPFN_WSASEND) dbx_dso_sym(netx_so.plibrary, (char *) "WSASend"); 82 | 83 | #if defined(NETX_IPV6) 84 | netx_so.p_WSAStringToAddress = (MG_LPFN_WSASTRINGTOADDRESS) dbx_dso_sym(netx_so.plibrary, (char *) "WSAStringToAddressA"); 85 | netx_so.p_WSAAddressToString = (MG_LPFN_WSAADDRESSTOSTRING) dbx_dso_sym(netx_so.plibrary, (char *) "WSAAddressToStringA"); 86 | netx_so.p_getaddrinfo = (MG_LPFN_GETADDRINFO) dbx_dso_sym(netx_so.plibrary, (char *) "getaddrinfo"); 87 | netx_so.p_freeaddrinfo = (MG_LPFN_FREEADDRINFO) dbx_dso_sym(netx_so.plibrary, (char *) "freeaddrinfo"); 88 | netx_so.p_getnameinfo = (MG_LPFN_GETNAMEINFO) dbx_dso_sym(netx_so.plibrary, (char *) "getnameinfo"); 89 | netx_so.p_getpeername = (MG_LPFN_GETPEERNAME) dbx_dso_sym(netx_so.plibrary, (char *) "getpeername"); 90 | netx_so.p_inet_ntop = (MG_LPFN_INET_NTOP) dbx_dso_sym(netx_so.plibrary, (char *) "InetNtop"); 91 | netx_so.p_inet_pton = (MG_LPFN_INET_PTON) dbx_dso_sym(netx_so.plibrary, (char *) "InetPton"); 92 | #else 93 | netx_so.p_WSAStringToAddress = NULL; 94 | netx_so.p_WSAAddressToString = NULL; 95 | netx_so.p_getaddrinfo = NULL; 96 | netx_so.p_freeaddrinfo = NULL; 97 | netx_so.p_getnameinfo = NULL; 98 | netx_so.p_getpeername = NULL; 99 | netx_so.p_inet_ntop = NULL; 100 | netx_so.p_inet_pton = NULL; 101 | #endif 102 | 103 | netx_so.p_closesocket = (MG_LPFN_CLOSESOCKET) dbx_dso_sym(netx_so.plibrary, (char *) "closesocket"); 104 | netx_so.p_gethostname = (MG_LPFN_GETHOSTNAME) dbx_dso_sym(netx_so.plibrary, (char *) "gethostname"); 105 | netx_so.p_gethostbyname = (MG_LPFN_GETHOSTBYNAME) dbx_dso_sym(netx_so.plibrary, (char *) "gethostbyname"); 106 | netx_so.p_getservbyname = (MG_LPFN_GETSERVBYNAME) dbx_dso_sym(netx_so.plibrary, (char *) "getservbyname"); 107 | netx_so.p_gethostbyaddr = (MG_LPFN_GETHOSTBYADDR) dbx_dso_sym(netx_so.plibrary, (char *) "gethostbyaddr"); 108 | netx_so.p_htons = (MG_LPFN_HTONS) dbx_dso_sym(netx_so.plibrary, (char *) "htons"); 109 | netx_so.p_htonl = (MG_LPFN_HTONL) dbx_dso_sym(netx_so.plibrary, (char *) "htonl"); 110 | netx_so.p_ntohl = (MG_LPFN_NTOHL) dbx_dso_sym(netx_so.plibrary, (char *) "ntohl"); 111 | netx_so.p_ntohs = (MG_LPFN_NTOHS) dbx_dso_sym(netx_so.plibrary, (char *) "ntohs"); 112 | netx_so.p_connect = (MG_LPFN_CONNECT) dbx_dso_sym(netx_so.plibrary, (char *) "connect"); 113 | netx_so.p_inet_addr = (MG_LPFN_INET_ADDR) dbx_dso_sym(netx_so.plibrary, (char *) "inet_addr"); 114 | netx_so.p_inet_ntoa = (MG_LPFN_INET_NTOA) dbx_dso_sym(netx_so.plibrary, (char *) "inet_ntoa"); 115 | 116 | netx_so.p_socket = (MG_LPFN_SOCKET) dbx_dso_sym(netx_so.plibrary, (char *) "socket"); 117 | netx_so.p_setsockopt = (MG_LPFN_SETSOCKOPT) dbx_dso_sym(netx_so.plibrary, (char *) "setsockopt"); 118 | netx_so.p_getsockopt = (MG_LPFN_GETSOCKOPT) dbx_dso_sym(netx_so.plibrary, (char *) "getsockopt"); 119 | netx_so.p_getsockname = (MG_LPFN_GETSOCKNAME) dbx_dso_sym(netx_so.plibrary, (char *) "getsockname"); 120 | 121 | netx_so.p_select = (MG_LPFN_SELECT) dbx_dso_sym(netx_so.plibrary, (char *) "select"); 122 | netx_so.p_recv = (MG_LPFN_RECV) dbx_dso_sym(netx_so.plibrary, (char *) "recv"); 123 | netx_so.p_send = (MG_LPFN_SEND) dbx_dso_sym(netx_so.plibrary, (char *) "send"); 124 | netx_so.p_shutdown = (MG_LPFN_SHUTDOWN) dbx_dso_sym(netx_so.plibrary, (char *) "shutdown"); 125 | netx_so.p_bind = (MG_LPFN_BIND) dbx_dso_sym(netx_so.plibrary, (char *) "bind"); 126 | netx_so.p_listen = (MG_LPFN_LISTEN) dbx_dso_sym(netx_so.plibrary, (char *) "listen"); 127 | netx_so.p_accept = (MG_LPFN_ACCEPT) dbx_dso_sym(netx_so.plibrary, (char *) "accept"); 128 | 129 | if ( (netx_so.p_WSASocket == NULL && netx_so.winsock == 2) 130 | || netx_so.p_WSAGetLastError == NULL 131 | || netx_so.p_WSAStartup == NULL 132 | || netx_so.p_WSACleanup == NULL 133 | || netx_so.p_WSAFDIsSet == NULL 134 | || (netx_so.p_WSARecv == NULL && netx_so.winsock == 2) 135 | || (netx_so.p_WSASend == NULL && netx_so.winsock == 2) 136 | 137 | #if defined(NETX_IPV6) 138 | || (netx_so.p_WSAStringToAddress == NULL && netx_so.winsock == 2) 139 | || (netx_so.p_WSAAddressToString == NULL && netx_so.winsock == 2) 140 | || netx_so.p_getpeername == NULL 141 | #endif 142 | 143 | || netx_so.p_closesocket == NULL 144 | || netx_so.p_gethostname == NULL 145 | || netx_so.p_gethostbyname == NULL 146 | || netx_so.p_getservbyname == NULL 147 | || netx_so.p_gethostbyaddr == NULL 148 | || netx_so.p_htons == NULL 149 | || netx_so.p_htonl == NULL 150 | || netx_so.p_ntohl == NULL 151 | || netx_so.p_ntohs == NULL 152 | || netx_so.p_connect == NULL 153 | || netx_so.p_inet_addr == NULL 154 | || netx_so.p_inet_ntoa == NULL 155 | || netx_so.p_socket == NULL 156 | || netx_so.p_setsockopt == NULL 157 | || netx_so.p_getsockopt == NULL 158 | || netx_so.p_getsockname == NULL 159 | || netx_so.p_select == NULL 160 | || netx_so.p_recv == NULL 161 | || netx_so.p_send == NULL 162 | || netx_so.p_shutdown == NULL 163 | || netx_so.p_bind == NULL 164 | || netx_so.p_listen == NULL 165 | || netx_so.p_accept == NULL 166 | ) { 167 | 168 | sprintf(buffer, "Cannot use Winsock library (WSASocket=%p; WSAGetLastError=%p; WSAStartup=%p; WSACleanup=%p; WSAFDIsSet=%p; WSARecv=%p; WSASend=%p; WSAStringToAddress=%p; WSAAddressToString=%p; closesocket=%p; gethostname=%p; gethostbyname=%p; getservbyname=%p; gethostbyaddr=%p; getaddrinfo=%p; freeaddrinfo=%p; getnameinfo=%p; getpeername=%p; htons=%p; htonl=%p; ntohl=%p; ntohs=%p; connect=%p; inet_addr=%p; inet_ntoa=%p; socket=%p; setsockopt=%p; getsockopt=%p; getsockname=%p; select=%p; recv=%p; p_send=%p; shutdown=%p; bind=%p; listen=%p; accept=%p;)", 169 | netx_so.p_WSASocket, 170 | netx_so.p_WSAGetLastError, 171 | netx_so.p_WSAStartup, 172 | netx_so.p_WSACleanup, 173 | netx_so.p_WSAFDIsSet, 174 | netx_so.p_WSARecv, 175 | netx_so.p_WSASend, 176 | 177 | netx_so.p_WSAStringToAddress, 178 | netx_so.p_WSAAddressToString, 179 | 180 | netx_so.p_closesocket, 181 | netx_so.p_gethostname, 182 | netx_so.p_gethostbyname, 183 | netx_so.p_getservbyname, 184 | netx_so.p_gethostbyaddr, 185 | 186 | netx_so.p_getaddrinfo, 187 | netx_so.p_freeaddrinfo, 188 | netx_so.p_getnameinfo, 189 | netx_so.p_getpeername, 190 | 191 | netx_so.p_htons, 192 | netx_so.p_htonl, 193 | netx_so.p_ntohl, 194 | netx_so.p_ntohs, 195 | netx_so.p_connect, 196 | netx_so.p_inet_addr, 197 | netx_so.p_inet_ntoa, 198 | netx_so.p_socket, 199 | netx_so.p_setsockopt, 200 | netx_so.p_getsockopt, 201 | netx_so.p_getsockname, 202 | netx_so.p_select, 203 | netx_so.p_recv, 204 | netx_so.p_send, 205 | netx_so.p_shutdown, 206 | netx_so.p_bind, 207 | netx_so.p_listen, 208 | netx_so.p_accept 209 | ); 210 | dbx_dso_unload((DBXPLIB) netx_so.plibrary); 211 | } 212 | else { 213 | netx_so.sock = 1; 214 | } 215 | 216 | if (netx_so.sock) 217 | result = 0; 218 | else 219 | result = -1; 220 | 221 | netx_so.load_attempted = 1; 222 | 223 | if (netx_so.p_getaddrinfo == NULL || netx_so.p_freeaddrinfo == NULL || netx_so.p_getnameinfo == NULL) 224 | netx_so.ipv6 = 0; 225 | 226 | netx_load_winsock_no_so: 227 | 228 | if (result == 0) { 229 | 230 | if (netx_so.winsock == 2) 231 | netx_so.version_requested = MAKEWORD(2, 2); 232 | else 233 | netx_so.version_requested = MAKEWORD(1, 1); 234 | 235 | netx_so.wsastartup = NETX_WSASTARTUP(netx_so.version_requested, &(netx_so.wsadata)); 236 | 237 | if (netx_so.wsastartup != 0 && netx_so.winsock == 2) { 238 | netx_so.version_requested = MAKEWORD(2, 0); 239 | netx_so.wsastartup = NETX_WSASTARTUP(netx_so.version_requested, &(netx_so.wsadata)); 240 | if (netx_so.wsastartup != 0) { 241 | netx_so.winsock = 1; 242 | netx_so.version_requested = MAKEWORD(1, 1); 243 | netx_so.wsastartup = NETX_WSASTARTUP(netx_so.version_requested, &(netx_so.wsadata)); 244 | } 245 | } 246 | if (netx_so.wsastartup == 0) { 247 | if ((netx_so.winsock == 2 && LOBYTE(netx_so.wsadata.wVersion) != 2) 248 | || (netx_so.winsock == 1 && (LOBYTE(netx_so.wsadata.wVersion) != 1 || HIBYTE(netx_so.wsadata.wVersion) != 1))) { 249 | 250 | sprintf(pcon->error, "Initialization Error: Wrong version of Winsock library (%s) (%d.%d)", netx_so.libnam, LOBYTE(netx_so.wsadata.wVersion), HIBYTE(netx_so.wsadata.wVersion)); 251 | NETX_WSACLEANUP(); 252 | netx_so.wsastartup = -1; 253 | } 254 | else { 255 | if (strlen(netx_so.libnam)) 256 | sprintf(pcon->info, "Initialization: Windows Sockets library loaded (%s) Version: %d.%d", netx_so.libnam, LOBYTE(netx_so.wsadata.wVersion), HIBYTE(netx_so.wsadata.wVersion)); 257 | else 258 | sprintf(pcon->info, "Initialization: Windows Sockets library Version: %d.%d", LOBYTE(netx_so.wsadata.wVersion), HIBYTE(netx_so.wsadata.wVersion)); 259 | netx_so.winsock_ready = 1; 260 | } 261 | } 262 | else { 263 | strcpy(pcon->error, "Initialization Error: Unusable Winsock library"); 264 | } 265 | } 266 | 267 | return result; 268 | 269 | #else 270 | 271 | return 1; 272 | 273 | #endif /* #if defined(_WIN32) */ 274 | 275 | } 276 | 277 | 278 | int netx_tcp_connect(DBXCON *pcon, int context) 279 | { 280 | short physical_ip, ipv6, connected, getaddrinfo_ok; 281 | int n, errorno; 282 | unsigned long inetaddr; 283 | DWORD spin_count; 284 | char net_host[64]; 285 | struct sockaddr_in srv_addr, cli_addr; 286 | struct hostent *hp; 287 | struct in_addr **pptr; 288 | 289 | pcon->open = 0; 290 | pcon->error_code = 0; 291 | connected = 0; 292 | getaddrinfo_ok = 0; 293 | spin_count = 0; 294 | 295 | ipv6 = 1; 296 | #if !defined(NETX_IPV6) 297 | ipv6 = 0; 298 | #endif 299 | 300 | strcpy(net_host, (char *) pcon->net_host); 301 | 302 | #if defined(_WIN32) 303 | 304 | if (!netx_so.load_attempted) { 305 | n = netx_load_winsock(pcon, 0); 306 | if (n != 0) { 307 | return CACHE_NOCON; 308 | } 309 | } 310 | if (!netx_so.winsock_ready) { 311 | strcpy(pcon->error, (char *) "DLL Load Error: Unusable Winsock Library"); 312 | return CACHE_NOCON; 313 | } 314 | 315 | n = netx_so.wsastartup; 316 | if (n != 0) { 317 | strcpy(pcon->error, (char *) "DLL Load Error: Unusable Winsock Library"); 318 | return n; 319 | } 320 | 321 | #endif /* #if defined(_WIN32) */ 322 | 323 | #if defined(NETX_IPV6) 324 | 325 | if (ipv6) { 326 | short mode; 327 | struct addrinfo hints, *res; 328 | struct addrinfo *ai; 329 | char port_str[32]; 330 | 331 | res = NULL; 332 | sprintf(port_str, "%d", pcon->tcp_port); 333 | connected = 0; 334 | pcon->error_code = 0; 335 | 336 | for (mode = 0; mode < 3; mode ++) { 337 | 338 | if (res) { 339 | NETX_FREEADDRINFO(res); 340 | res = NULL; 341 | } 342 | 343 | memset(&hints, 0, sizeof hints); 344 | hints.ai_family = AF_UNSPEC; /* Use IPv4 or IPv6 */ 345 | hints.ai_socktype = SOCK_STREAM; 346 | /* hints.ai_flags = AI_PASSIVE; */ 347 | if (mode == 0) 348 | hints.ai_flags = AI_NUMERICHOST | AI_CANONNAME; 349 | else if (mode == 1) 350 | hints.ai_flags = AI_CANONNAME; 351 | else if (mode == 2) { 352 | /* Apparently an error can occur with AF_UNSPEC (See RJW1564) */ 353 | /* This iteration will return IPV6 addresses if any */ 354 | hints.ai_flags = AI_CANONNAME; 355 | hints.ai_family = AF_INET6; 356 | } 357 | else 358 | break; 359 | 360 | n = NETX_GETADDRINFO(net_host, port_str, &hints, &res); 361 | 362 | if (n != 0) { 363 | continue; 364 | } 365 | 366 | getaddrinfo_ok = 1; 367 | spin_count = 0; 368 | for (ai = res; ai != NULL; ai = ai->ai_next) { 369 | 370 | spin_count ++; 371 | 372 | if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) { 373 | continue; 374 | } 375 | 376 | /* Open a socket with the correct address family for this address. */ 377 | pcon->cli_socket = NETX_SOCKET(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 378 | 379 | /* NETX_BIND(pcon->cli_socket, ai->ai_addr, (int) (ai->ai_addrlen)); */ 380 | /* NETX_CONNECT(pcon->cli_socket, ai->ai_addr, (int) (ai->ai_addrlen)); */ 381 | 382 | if (netx_so.nagle_algorithm == 0) { 383 | 384 | int flag = 1; 385 | int result; 386 | 387 | result = NETX_SETSOCKOPT(pcon->cli_socket, IPPROTO_TCP, TCP_NODELAY, (const char *) &flag, sizeof(int)); 388 | 389 | if (result < 0) { 390 | strcpy(pcon->error, "Connection Error: Unable to disable the Nagle Algorithm"); 391 | } 392 | 393 | } 394 | 395 | pcon->error_code = 0; 396 | n = netx_tcp_connect_ex(pcon, (xLPSOCKADDR) ai->ai_addr, (socklen_netx) (ai->ai_addrlen), pcon->timeout); 397 | if (n == -2) { 398 | pcon->error_code = n; 399 | n = -737; 400 | continue; 401 | } 402 | if (SOCK_ERROR(n)) { 403 | errorno = (int) netx_get_last_error(0); 404 | pcon->error_code = errorno; 405 | netx_tcp_disconnect(pcon, 0); 406 | continue; 407 | } 408 | else { 409 | connected = 1; 410 | break; 411 | } 412 | } 413 | if (connected) 414 | break; 415 | } 416 | 417 | if (pcon->error_code) { 418 | char message[256]; 419 | netx_get_error_message(pcon->error_code, message, 250, 0); 420 | sprintf(pcon->error, "Connection Error: Cannot Connect to Server (%s:%d): Error Code: %d (%s)", (char *) pcon->net_host, pcon->tcp_port, pcon->error_code, message); 421 | n = -5; 422 | } 423 | 424 | if (res) { 425 | NETX_FREEADDRINFO(res); 426 | res = NULL; 427 | } 428 | } 429 | #endif 430 | 431 | if (ipv6) { 432 | if (connected) { 433 | return 0; 434 | } 435 | else { 436 | if (getaddrinfo_ok) { 437 | netx_tcp_disconnect(pcon, 0); 438 | return -5; 439 | } 440 | else { 441 | char message[256]; 442 | 443 | errorno = (int) netx_get_last_error(0); 444 | netx_get_error_message(errorno, message, 250, 0); 445 | sprintf(pcon->error, "Connection Error: Cannot identify Server: Error Code: %d (%s)", errorno, message); 446 | netx_tcp_disconnect(pcon, 0); 447 | return -5; 448 | } 449 | } 450 | } 451 | 452 | ipv6 = 0; 453 | inetaddr = NETX_INET_ADDR(net_host); 454 | 455 | physical_ip = 0; 456 | if (isdigit(net_host[0])) { 457 | char *p; 458 | 459 | if ((p = strstr(net_host, "."))) { 460 | if (isdigit(*(++ p))) { 461 | if ((p = strstr(p, "."))) { 462 | if (isdigit(*(++ p))) { 463 | if ((p = strstr(p, "."))) { 464 | if (isdigit(*(++ p))) { 465 | physical_ip = 1; 466 | } 467 | } 468 | } 469 | } 470 | } 471 | } 472 | } 473 | 474 | if (inetaddr == INADDR_NONE || !physical_ip) { 475 | 476 | hp = NETX_GETHOSTBYNAME((const char *) net_host); 477 | 478 | if (hp == NULL) { 479 | n = -2; 480 | strcpy(pcon->error, "Connection Error: Invalid Host"); 481 | return n; 482 | } 483 | 484 | pptr = (struct in_addr **) hp->h_addr_list; 485 | connected = 0; 486 | 487 | spin_count = 0; 488 | 489 | for (; *pptr != NULL; pptr ++) { 490 | 491 | spin_count ++; 492 | 493 | pcon->cli_socket = NETX_SOCKET(AF_INET, SOCK_STREAM, 0); 494 | 495 | if (INVALID_SOCK(pcon->cli_socket)) { 496 | char message[256]; 497 | 498 | n = -2; 499 | errorno = (int) netx_get_last_error(0); 500 | netx_get_error_message(errorno, message, 250, 0); 501 | sprintf(pcon->error, "Connection Error: Invalid Socket: Context=1: Error Code: %d (%s)", errorno, message); 502 | break; 503 | } 504 | 505 | #if !defined(_WIN32) 506 | BZERO((char *) &cli_addr, sizeof(cli_addr)); 507 | BZERO((char *) &srv_addr, sizeof(srv_addr)); 508 | #endif 509 | 510 | cli_addr.sin_family = AF_INET; 511 | srv_addr.sin_port = NETX_HTONS((unsigned short) pcon->tcp_port); 512 | 513 | cli_addr.sin_addr.s_addr = NETX_HTONL(INADDR_ANY); 514 | cli_addr.sin_port = NETX_HTONS(0); 515 | 516 | n = NETX_BIND(pcon->cli_socket, (xLPSOCKADDR) &cli_addr, sizeof(cli_addr)); 517 | 518 | if (SOCK_ERROR(n)) { 519 | char message[256]; 520 | 521 | n = -3; 522 | errorno = (int) netx_get_last_error(0); 523 | netx_get_error_message(errorno, message, 250, 0); 524 | sprintf(pcon->error, "Connection Error: Cannot bind to Socket: Error Code: %d (%s)", errorno, message); 525 | 526 | break; 527 | } 528 | 529 | if (netx_so.nagle_algorithm == 0) { 530 | 531 | int flag = 1; 532 | int result; 533 | 534 | result = NETX_SETSOCKOPT(pcon->cli_socket, IPPROTO_TCP, TCP_NODELAY, (const char *) &flag, sizeof(int)); 535 | if (result < 0) { 536 | strcpy(pcon->error, "Connection Error: Unable to disable the Nagle Algorithm"); 537 | } 538 | } 539 | 540 | srv_addr.sin_family = AF_INET; 541 | srv_addr.sin_port = NETX_HTONS((unsigned short) pcon->tcp_port); 542 | 543 | NETX_MEMCPY(&srv_addr.sin_addr, *pptr, sizeof(struct in_addr)); 544 | 545 | n = netx_tcp_connect_ex(pcon, (xLPSOCKADDR) &srv_addr, sizeof(srv_addr), pcon->timeout); 546 | 547 | if (n == -2) { 548 | pcon->error_code = n; 549 | n = -737; 550 | 551 | continue; 552 | } 553 | 554 | if (SOCK_ERROR(n)) { 555 | char message[256]; 556 | 557 | errorno = (int) netx_get_last_error(0); 558 | netx_get_error_message(errorno, message, 250, 0); 559 | 560 | pcon->error_code = errorno; 561 | sprintf(pcon->error, "Connection Error: Cannot Connect to Server (%s:%d): Error Code: %d (%s)", (char *) pcon->net_host, pcon->tcp_port, errorno, message); 562 | n = -5; 563 | netx_tcp_disconnect(pcon, 0); 564 | continue; 565 | } 566 | else { 567 | connected = 1; 568 | break; 569 | } 570 | } 571 | if (!connected) { 572 | 573 | netx_tcp_disconnect(pcon, 0); 574 | 575 | strcpy(pcon->error, "Connection Error: Failed to find the Server via a DNS Lookup"); 576 | 577 | return n; 578 | } 579 | } 580 | else { 581 | 582 | pcon->cli_socket = NETX_SOCKET(AF_INET, SOCK_STREAM, 0); 583 | 584 | if (INVALID_SOCK(pcon->cli_socket)) { 585 | char message[256]; 586 | 587 | n = -2; 588 | errorno = (int) netx_get_last_error(0); 589 | netx_get_error_message(errorno, message, 250, 0); 590 | sprintf(pcon->error, "Connection Error: Invalid Socket: Context=2: Error Code: %d (%s)", errorno, message); 591 | 592 | return n; 593 | } 594 | 595 | #if !defined(_WIN32) 596 | BZERO((char *) &cli_addr, sizeof(cli_addr)); 597 | BZERO((char *) &srv_addr, sizeof(srv_addr)); 598 | #endif 599 | 600 | cli_addr.sin_family = AF_INET; 601 | cli_addr.sin_addr.s_addr = NETX_HTONL(INADDR_ANY); 602 | cli_addr.sin_port = NETX_HTONS(0); 603 | 604 | n = NETX_BIND(pcon->cli_socket, (xLPSOCKADDR) &cli_addr, sizeof(cli_addr)); 605 | 606 | if (SOCK_ERROR(n)) { 607 | char message[256]; 608 | 609 | n = -3; 610 | 611 | errorno = (int) netx_get_last_error(0); 612 | netx_get_error_message(errorno, message, 250, 0); 613 | 614 | sprintf(pcon->error, "Connection Error: Cannot bind to Socket: Error Code: %d (%s)", errorno, message); 615 | 616 | netx_tcp_disconnect(pcon, 0); 617 | 618 | return n; 619 | } 620 | 621 | if (netx_so.nagle_algorithm == 0) { 622 | 623 | int flag = 1; 624 | int result; 625 | 626 | result = NETX_SETSOCKOPT(pcon->cli_socket, IPPROTO_TCP, TCP_NODELAY, (const char *) &flag, sizeof(int)); 627 | 628 | if (result < 0) { 629 | strcpy(pcon->error, "Connection Error: Unable to disable the Nagle Algorithm"); 630 | 631 | } 632 | } 633 | 634 | srv_addr.sin_port = NETX_HTONS((unsigned short) pcon->tcp_port); 635 | srv_addr.sin_family = AF_INET; 636 | srv_addr.sin_addr.s_addr = NETX_INET_ADDR(net_host); 637 | 638 | n = netx_tcp_connect_ex(pcon, (xLPSOCKADDR) &srv_addr, sizeof(srv_addr), pcon->timeout); 639 | if (n == -2) { 640 | pcon->error_code = n; 641 | n = -737; 642 | 643 | netx_tcp_disconnect(pcon, 0); 644 | 645 | return n; 646 | } 647 | 648 | if (SOCK_ERROR(n)) { 649 | char message[256]; 650 | 651 | errorno = (int) netx_get_last_error(0); 652 | netx_get_error_message(errorno, message, 250, 0); 653 | pcon->error_code = errorno; 654 | sprintf(pcon->error, "Connection Error: Cannot Connect to Server (%s:%d): Error Code: %d (%s)", (char *) pcon->net_host, pcon->tcp_port, errorno, message); 655 | n = -5; 656 | netx_tcp_disconnect(pcon, 0); 657 | return n; 658 | } 659 | } 660 | 661 | return 0; 662 | } 663 | 664 | 665 | int netx_tcp_handshake(DBXCON *pcon, int context) 666 | { 667 | int len; 668 | char buffer[256]; 669 | 670 | sprintf(buffer, "dbx1~%s\n", pcon->nspace); 671 | len = (int) strlen(buffer); 672 | 673 | netx_tcp_write(pcon, (unsigned char *) buffer, len); 674 | len = netx_tcp_read(pcon, (unsigned char *) buffer, 5, DBX_DEFAULT_TIMEOUT, 1); 675 | 676 | len = dbx_get_size((unsigned char *) buffer); 677 | 678 | netx_tcp_read(pcon, (unsigned char *) buffer, len, DBX_DEFAULT_TIMEOUT, 1); 679 | if (pcon->dbtype != DBX_DBTYPE_YOTTADB) { 680 | isc_parse_zv(buffer, pcon->p_zv); 681 | T_SPRINTF(pcon->p_zv->version, _dbxso(pcon->p_zv->version), "%d.%d build %d", pcon->p_zv->majorversion, pcon->p_zv->minorversion, pcon->p_zv->dbx_build); 682 | } 683 | else { 684 | ydb_parse_zv(buffer, pcon->p_zv); 685 | if (pcon->p_zv->dbx_build) 686 | sprintf(pcon->p_zv->version, "%d.%d.b%d", pcon->p_zv->majorversion, pcon->p_zv->minorversion, pcon->p_zv->dbx_build); 687 | else 688 | sprintf(pcon->p_zv->version, "%d.%d", pcon->p_zv->majorversion, pcon->p_zv->minorversion); 689 | } 690 | 691 | return 0; 692 | } 693 | 694 | int netx_tcp_command(DBXMETH *pmeth, int command, int context) 695 | { 696 | int len, rc; 697 | unsigned int netbuf_used; 698 | unsigned char *netbuf; 699 | char *p; 700 | DBXCON *pcon = pmeth->pcon; 701 | 702 | rc = CACHE_SUCCESS; 703 | pcon->error[0] = '\0'; 704 | 705 | dbx_add_block_size(pmeth->ibuffer, pmeth->ibuffer_used, 0, DBX_DSORT_EOD, DBX_DTYPE_STR8); 706 | pmeth->ibuffer_used += 5; 707 | 708 | netbuf = (pmeth->ibuffer - DBX_IBUFFER_OFFSET); 709 | netbuf_used = (pmeth->ibuffer_used + DBX_IBUFFER_OFFSET); 710 | dbx_add_block_size(netbuf, 0, netbuf_used, 0, command); 711 | 712 | /* v2.4.26 */ 713 | if (pcon->utf8 == 2) 714 | netbuf[9] = 255; 715 | else 716 | netbuf[9] = 0; 717 | 718 | 719 | /* 720 | { 721 | char buffer[256]; 722 | sprintf(buffer, "netx_tcp_command SEND cmnd=%d; size=%d; netbuf_used=%d;", command, pmeth->ibuffer_used, netbuf_used); 723 | dbx_log_buffer(pcon, (char *) netbuf, (int) netbuf_used, buffer, 0); 724 | } 725 | */ 726 | 727 | rc = netx_tcp_write(pcon, (unsigned char *) netbuf, netbuf_used); 728 | if (rc < 0) { /* v2.2.21 */ 729 | netx_tcp_disconnect(pcon, 0); 730 | return rc; 731 | } 732 | 733 | rc = netx_tcp_read(pcon, (unsigned char *) pmeth->output_val.svalue.buf_addr, 5, pcon->timeout, 1); 734 | pmeth->output_val.svalue.buf_addr[5] = '\0'; 735 | 736 | if (rc < 0) { /* v2.2.21 */ 737 | netx_tcp_disconnect(pcon, 0); 738 | return rc; 739 | } 740 | 741 | len = dbx_get_block_size((unsigned char *) pmeth->output_val.svalue.buf_addr, 0, &(pmeth->output_val.sort), &(pmeth->output_val.type)); 742 | 743 | /* v2.2.22 */ 744 | if (len >= (int) pmeth->output_val.svalue.len_alloc) { 745 | p = (char *) dbx_malloc(sizeof(char) * (len + 2), 301); 746 | 747 | if (p) { 748 | if (pmeth->output_val.svalue.buf_addr) { 749 | dbx_free((void *) pmeth->output_val.svalue.buf_addr, 301); 750 | } 751 | pmeth->output_val.svalue.buf_addr = (char *) p; 752 | pmeth->output_val.svalue.len_alloc = len; 753 | } 754 | } 755 | if (len > 0) { 756 | rc = netx_tcp_read(pcon, (unsigned char *) pmeth->output_val.svalue.buf_addr, len, pcon->timeout, 1); 757 | if (rc < 0) { /* v2.2.21 */ 758 | netx_tcp_disconnect(pcon, 0); 759 | return rc; 760 | } 761 | } 762 | 763 | if (pmeth->output_val.type == DBX_DTYPE_OREF) { 764 | pmeth->output_val.svalue.buf_addr[len] = '\0'; 765 | pmeth->output_val.num.oref = (int) strtol(pmeth->output_val.svalue.buf_addr, NULL, 10); 766 | pmeth->output_val.num.int32 = pmeth->output_val.num.oref; 767 | } 768 | 769 | rc = CACHE_SUCCESS; 770 | if (pmeth->output_val.sort == DBX_DSORT_ERROR) { 771 | rc = CACHE_FAILURE; 772 | if (len > 0) { 773 | strncpy(pcon->error, pmeth->output_val.svalue.buf_addr, len); 774 | pcon->error[len] = '\0'; 775 | len = 0; 776 | } 777 | } 778 | /* 779 | { 780 | char buffer[256]; 781 | sprintf(buffer, "netx_tcp_command RECV cmnd=%d; len=%d; sort=%d; type=%d; oref=%d; rc=%d; error=%s;", command, len, pmeth->output_val.sort, pmeth->output_val.type, pmeth->output_val.num.oref, rc, pcon->error); 782 | dbx_log_buffer(pcon, pmeth->output_val.svalue.buf_addr, len, buffer, 0); 783 | } 784 | */ 785 | pmeth->output_val.svalue.len_used = len; 786 | 787 | return rc; 788 | } 789 | 790 | 791 | int netx_tcp_connect_ex(DBXCON *pcon, xLPSOCKADDR p_srv_addr, socklen_netx srv_addr_len, int timeout) 792 | { 793 | #if defined(_WIN32) 794 | int n; 795 | #else 796 | int flags, n, error; 797 | socklen_netx len; 798 | fd_set rset, wset; 799 | struct timeval tval; 800 | #endif 801 | 802 | #if defined(SOLARIS) && BIT64PLAT 803 | timeout = 0; 804 | #endif 805 | 806 | /* It seems that BIT64PLAT is set to 0 for 64-bit Solaris: So, to be safe .... */ 807 | 808 | #if defined(SOLARIS) 809 | timeout = 0; 810 | #endif 811 | 812 | if (timeout != 0) { 813 | 814 | #if defined(_WIN32) 815 | 816 | n = NETX_CONNECT(pcon->cli_socket, (xLPSOCKADDR) p_srv_addr, (socklen_netx) srv_addr_len); 817 | 818 | return n; 819 | 820 | #else 821 | flags = fcntl(pcon->cli_socket, F_GETFL, 0); 822 | n = fcntl(pcon->cli_socket, F_SETFL, flags | O_NONBLOCK); 823 | 824 | error = 0; 825 | 826 | n = NETX_CONNECT(pcon->cli_socket, (xLPSOCKADDR) p_srv_addr, (socklen_netx) srv_addr_len); 827 | 828 | if (n < 0) { 829 | 830 | if (errno != EINPROGRESS) { 831 | 832 | #if defined(SOLARIS) 833 | 834 | if (errno != 2 && errno != 146) { 835 | sprintf((char *) pcon->error, "Diagnostic: Solaris: Initial Connection Error errno=%d; EINPROGRESS=%d", errno, EINPROGRESS); 836 | return -1; 837 | } 838 | #else 839 | return -1; 840 | #endif 841 | 842 | } 843 | } 844 | 845 | if (n != 0) { 846 | 847 | FD_ZERO(&rset); 848 | FD_SET(pcon->cli_socket, &rset); 849 | 850 | wset = rset; 851 | tval.tv_sec = timeout; 852 | tval.tv_usec = timeout; 853 | 854 | n = NETX_SELECT((int) (pcon->cli_socket + 1), &rset, &wset, NULL, &tval); 855 | 856 | if (n == 0) { 857 | close(pcon->cli_socket); 858 | errno = ETIMEDOUT; 859 | 860 | return (-2); 861 | } 862 | if (NETX_FD_ISSET(pcon->cli_socket, &rset) || NETX_FD_ISSET(pcon->cli_socket, &wset)) { 863 | 864 | len = sizeof(error); 865 | if (NETX_GETSOCKOPT(pcon->cli_socket, SOL_SOCKET, SO_ERROR, (void *) &error, &len) < 0) { 866 | 867 | sprintf((char *) pcon->error, "Diagnostic: Solaris: Pending Error %d", errno); 868 | 869 | return (-1); /* Solaris pending error */ 870 | } 871 | } 872 | else { 873 | ; 874 | } 875 | } 876 | 877 | fcntl(pcon->cli_socket, F_SETFL, flags); /* Restore file status flags */ 878 | 879 | if (error) { 880 | 881 | close(pcon->cli_socket); 882 | errno = error; 883 | return (-1); 884 | } 885 | 886 | return 1; 887 | 888 | #endif 889 | 890 | } 891 | else { 892 | 893 | n = NETX_CONNECT(pcon->cli_socket, (xLPSOCKADDR) p_srv_addr, (socklen_netx) srv_addr_len); 894 | 895 | return n; 896 | } 897 | 898 | } 899 | 900 | 901 | int netx_tcp_disconnect(DBXCON *pcon, int context) 902 | { 903 | 904 | if (!pcon) { 905 | return 0; 906 | } 907 | 908 | if (pcon->cli_socket != (SOCKET) 0) { 909 | 910 | #if defined(_WIN32) 911 | NETX_CLOSESOCKET(pcon->cli_socket); 912 | /* 913 | NETX_WSACLEANUP(); 914 | */ 915 | 916 | #else 917 | close(pcon->cli_socket); 918 | #endif 919 | 920 | } 921 | 922 | pcon->open = 0; 923 | 924 | return 0; 925 | 926 | } 927 | 928 | 929 | int netx_tcp_write(DBXCON *pcon, unsigned char *data, int size) 930 | { 931 | int n = 0, errorno = 0, char_sent = 0; 932 | int total; 933 | char errormessage[512]; 934 | 935 | *errormessage = '\0'; 936 | 937 | if (pcon->open == 0) { 938 | strcpy(pcon->error, "TCP Write Error: Socket is Closed"); 939 | return -1; 940 | } 941 | 942 | total = 0; 943 | for (;;) { 944 | n = NETX_SEND(pcon->cli_socket, (xLPSENDBUF) (data + total), size - total, 0); 945 | 946 | if (SOCK_ERROR(n)) { 947 | 948 | errorno = (int) netx_get_last_error(0); 949 | 950 | if (NOT_BLOCKING(errorno) && errorno != 0) { 951 | 952 | char message[256]; 953 | 954 | netx_get_error_message(errorno, message, 250, 0); 955 | sprintf(pcon->error, "TCP Write Error: Cannot Write Data: Error Code: %d (%s)", errorno, message); 956 | 957 | char_sent = -1; 958 | break; 959 | } 960 | } 961 | else { 962 | 963 | total += n; 964 | if (total == size) { 965 | break; 966 | } 967 | } 968 | } 969 | 970 | if (char_sent < 0) 971 | return char_sent; 972 | else 973 | return size; 974 | 975 | } 976 | 977 | 978 | 979 | int netx_tcp_read(DBXCON *pcon, unsigned char *data, int size, int timeout, int context) 980 | { 981 | int result, n; 982 | int len; 983 | fd_set rset, eset; 984 | struct timeval tval; 985 | unsigned long spin_count; 986 | 987 | 988 | if (!pcon) { 989 | return NETX_READ_ERROR; 990 | } 991 | 992 | result = 0; 993 | 994 | tval.tv_sec = timeout; 995 | tval.tv_usec = 0; 996 | 997 | spin_count = 0; 998 | len = 0; 999 | for (;;) { 1000 | spin_count ++; 1001 | 1002 | FD_ZERO(&rset); 1003 | FD_ZERO(&eset); 1004 | FD_SET(pcon->cli_socket, &rset); 1005 | FD_SET(pcon->cli_socket, &eset); 1006 | 1007 | n = NETX_SELECT((int) (pcon->cli_socket + 1), &rset, NULL, &eset, &tval); 1008 | 1009 | if (n == 0) { 1010 | sprintf(pcon->error, "TCP Read Error: Server did not respond within the timeout period (%d seconds)", timeout); 1011 | result = NETX_READ_TIMEOUT; 1012 | break; 1013 | } 1014 | 1015 | if (n < 0 || !NETX_FD_ISSET(pcon->cli_socket, &rset)) { 1016 | strcpy(pcon->error, "TCP Read Error: Server closed the connection without having returned any data"); 1017 | result = NETX_READ_ERROR; 1018 | break; 1019 | } 1020 | 1021 | n = NETX_RECV(pcon->cli_socket, (char *) data + len, size - len, 0); 1022 | 1023 | if (n < 1) { 1024 | if (n == 0) { 1025 | result = NETX_READ_EOF; 1026 | pcon->open = 0; 1027 | pcon->eof = 1; 1028 | } 1029 | else { 1030 | result = NETX_READ_ERROR; 1031 | len = 0; 1032 | pcon->open = 0; 1033 | } 1034 | break; 1035 | } 1036 | 1037 | len += n; 1038 | if (context) { /* Must read length requested */ 1039 | if (len == size) { 1040 | break; 1041 | } 1042 | } 1043 | else { 1044 | break; 1045 | } 1046 | } 1047 | 1048 | if (len) { 1049 | result = len; 1050 | } 1051 | return result; 1052 | } 1053 | 1054 | 1055 | 1056 | int netx_get_last_error(int context) 1057 | { 1058 | int error_code; 1059 | 1060 | #if defined(_WIN32) 1061 | if (context) 1062 | error_code = (int) GetLastError(); 1063 | else 1064 | error_code = (int) NETX_WSAGETLASTERROR(); 1065 | #else 1066 | error_code = (int) errno; 1067 | #endif 1068 | 1069 | return error_code; 1070 | } 1071 | 1072 | 1073 | int netx_get_error_message(int error_code, char *message, int size, int context) 1074 | { 1075 | *message = '\0'; 1076 | 1077 | #if defined(_WIN32) 1078 | 1079 | if (context == 0) { 1080 | short ok; 1081 | int len; 1082 | char *p; 1083 | LPVOID lpMsgBuf; 1084 | 1085 | ok = 0; 1086 | lpMsgBuf = NULL; 1087 | len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 1088 | NULL, 1089 | error_code, 1090 | /* MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), */ 1091 | MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 1092 | (LPTSTR) &lpMsgBuf, 1093 | 0, 1094 | NULL 1095 | ); 1096 | if (len && lpMsgBuf) { 1097 | strncpy(message, (const char *) lpMsgBuf, size); 1098 | p = strstr(message, "\r\n"); 1099 | if (p) 1100 | *p = '\0'; 1101 | ok = 1; 1102 | } 1103 | if (lpMsgBuf) 1104 | LocalFree(lpMsgBuf); 1105 | 1106 | if (!ok) { 1107 | switch (error_code) { 1108 | case EXCEPTION_ACCESS_VIOLATION: 1109 | strncpy(message, "The thread attempted to read from or write to a virtual address for which it does not have the appropriate access.", size); 1110 | break; 1111 | case EXCEPTION_BREAKPOINT: 1112 | strncpy(message, "A breakpoint was encountered.", size); 1113 | break; 1114 | case EXCEPTION_DATATYPE_MISALIGNMENT: 1115 | strncpy(message, "The thread attempted to read or write data that is misaligned on hardware that does not provide alignment. For example, 16-bit values must be aligned on 2-byte boundaries, 32-bit values on 4-byte boundaries, and so on.", size); 1116 | break; 1117 | case EXCEPTION_SINGLE_STEP: 1118 | strncpy(message, "A trace trap or other single-instruction mechanism signaled that one instruction has been executed.", size); 1119 | break; 1120 | case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: 1121 | strncpy(message, "The thread attempted to access an array element that is out of bounds, and the underlying hardware supports bounds checking.", size); 1122 | break; 1123 | case EXCEPTION_FLT_DENORMAL_OPERAND: 1124 | strncpy(message, "One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value.", size); 1125 | break; 1126 | case EXCEPTION_FLT_DIVIDE_BY_ZERO: 1127 | strncpy(message, "The thread attempted to divide a floating-point value by a floating-point divisor of zero.", size); 1128 | break; 1129 | case EXCEPTION_FLT_INEXACT_RESULT: 1130 | strncpy(message, "The result of a floating-point operation cannot be represented exactly as a decimal fraction.", size); 1131 | break; 1132 | case EXCEPTION_FLT_INVALID_OPERATION: 1133 | strncpy(message, "This exception represents any floating-point exception not included in this list.", size); 1134 | break; 1135 | case EXCEPTION_FLT_OVERFLOW: 1136 | strncpy(message, "The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.", size); 1137 | break; 1138 | case EXCEPTION_FLT_STACK_CHECK: 1139 | strncpy(message, "The stack overflowed or underflowed as the result of a floating-point operation.", size); 1140 | break; 1141 | case EXCEPTION_FLT_UNDERFLOW: 1142 | strncpy(message, "The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.", size); 1143 | break; 1144 | case EXCEPTION_INT_DIVIDE_BY_ZERO: 1145 | strncpy(message, "The thread attempted to divide an integer value by an integer divisor of zero.", size); 1146 | break; 1147 | case EXCEPTION_INT_OVERFLOW: 1148 | strncpy(message, "The result of an integer operation caused a carry out of the most significant bit of the result.", size); 1149 | break; 1150 | case EXCEPTION_PRIV_INSTRUCTION: 1151 | strncpy(message, "The thread attempted to execute an instruction whose operation is not allowed in the current machine mode.", size); 1152 | break; 1153 | case EXCEPTION_NONCONTINUABLE_EXCEPTION: 1154 | strncpy(message, "The thread attempted to continue execution after a noncontinuable exception occurred.", size); 1155 | break; 1156 | default: 1157 | strncpy(message, "Unrecognised system or hardware error.", size); 1158 | break; 1159 | } 1160 | } 1161 | } 1162 | 1163 | #else 1164 | 1165 | if (context == 0) { 1166 | #if defined(_GNU_SOURCE) 1167 | char *p; 1168 | #endif 1169 | strcpy(message, ""); 1170 | #if defined(LINUX) || defined(AIX) || defined(OSF1) || defined(MACOSX) 1171 | #if defined(_GNU_SOURCE) 1172 | p = strerror_r(error_code, message, (size_t) size); 1173 | if (p && p != message) { 1174 | strncpy(message, p, size - 1); 1175 | message[size - 1] = '\0'; 1176 | } 1177 | #else 1178 | strerror_r(error_code, message, (size_t) size); 1179 | #endif 1180 | size = (int) strlen(message); 1181 | #else 1182 | netx_get_std_error_message(error_code, message, size, context); 1183 | size = (int) strlen(message); 1184 | #endif 1185 | } 1186 | 1187 | #endif 1188 | 1189 | message[size - 1] = '\0'; 1190 | 1191 | return (int) strlen(message); 1192 | } 1193 | 1194 | 1195 | int netx_get_std_error_message(int error_code, char *message, int size, int context) 1196 | { 1197 | 1198 | strcpy(message, ""); 1199 | 1200 | #if !defined(_WIN32) 1201 | switch (error_code) { 1202 | case E2BIG: 1203 | strncpy(message, "Argument list too long.", size); 1204 | break; 1205 | case EACCES: 1206 | strncpy(message, "Permission denied.", size); 1207 | break; 1208 | case EADDRINUSE: 1209 | strncpy(message, "Address in use.", size); 1210 | break; 1211 | case EADDRNOTAVAIL: 1212 | strncpy(message, "Address not available.", size); 1213 | break; 1214 | case EAFNOSUPPORT: 1215 | strncpy(message, "Address family not supported.", size); 1216 | break; 1217 | case EAGAIN: 1218 | strncpy(message, "Resource unavailable, try again.", size); 1219 | break; 1220 | case EALREADY: 1221 | strncpy(message, "Connection already in progress.", size); 1222 | break; 1223 | case EBADF: 1224 | strncpy(message, "Bad file descriptor.", size); 1225 | break; 1226 | #if !defined(MACOSX) && !defined(FREEBSD) 1227 | case EBADMSG: 1228 | strncpy(message, "Bad message.", size); 1229 | break; 1230 | #endif 1231 | case EBUSY: 1232 | strncpy(message, "Device or resource busy.", size); 1233 | break; 1234 | case ECANCELED: 1235 | strncpy(message, "Operation canceled.", size); 1236 | break; 1237 | case ECHILD: 1238 | strncpy(message, "No child processes.", size); 1239 | break; 1240 | case ECONNABORTED: 1241 | strncpy(message, "Connection aborted.", size); 1242 | break; 1243 | case ECONNREFUSED: 1244 | strncpy(message, "Connection refused.", size); 1245 | break; 1246 | case ECONNRESET: 1247 | strncpy(message, "Connection reset.", size); 1248 | break; 1249 | case EDEADLK: 1250 | strncpy(message, "Resource deadlock would occur.", size); 1251 | break; 1252 | case EDESTADDRREQ: 1253 | strncpy(message, "Destination address required.", size); 1254 | break; 1255 | case EDOM: 1256 | strncpy(message, "Mathematics argument out of domain of function.", size); 1257 | break; 1258 | case EDQUOT: 1259 | strncpy(message, "Reserved.", size); 1260 | break; 1261 | case EEXIST: 1262 | strncpy(message, "File exists.", size); 1263 | break; 1264 | case EFAULT: 1265 | strncpy(message, "Bad address.", size); 1266 | break; 1267 | case EFBIG: 1268 | strncpy(message, "File too large.", size); 1269 | break; 1270 | case EHOSTUNREACH: 1271 | strncpy(message, "Host is unreachable.", size); 1272 | break; 1273 | case EIDRM: 1274 | strncpy(message, "Identifier removed.", size); 1275 | break; 1276 | case EILSEQ: 1277 | strncpy(message, "Illegal byte sequence.", size); 1278 | break; 1279 | case EINPROGRESS: 1280 | strncpy(message, "Operation in progress.", size); 1281 | break; 1282 | case EINTR: 1283 | strncpy(message, "Interrupted function.", size); 1284 | break; 1285 | case EINVAL: 1286 | strncpy(message, "Invalid argument.", size); 1287 | break; 1288 | case EIO: 1289 | strncpy(message, "I/O error.", size); 1290 | break; 1291 | case EISCONN: 1292 | strncpy(message, "Socket is connected.", size); 1293 | break; 1294 | case EISDIR: 1295 | strncpy(message, "Is a directory.", size); 1296 | break; 1297 | case ELOOP: 1298 | strncpy(message, "Too many levels of symbolic links.", size); 1299 | break; 1300 | case EMFILE: 1301 | strncpy(message, "Too many open files.", size); 1302 | break; 1303 | case EMLINK: 1304 | strncpy(message, "Too many links.", size); 1305 | break; 1306 | case EMSGSIZE: 1307 | strncpy(message, "Message too large.", size); 1308 | break; 1309 | #if !defined(MACOSX) && !defined(OSF1) && !defined(FREEBSD) 1310 | case EMULTIHOP: 1311 | strncpy(message, "Reserved.", size); 1312 | break; 1313 | #endif 1314 | case ENAMETOOLONG: 1315 | strncpy(message, "Filename too long.", size); 1316 | break; 1317 | case ENETDOWN: 1318 | strncpy(message, "Network is down.", size); 1319 | break; 1320 | case ENETRESET: 1321 | strncpy(message, "Connection aborted by network.", size); 1322 | break; 1323 | case ENETUNREACH: 1324 | strncpy(message, "Network unreachable.", size); 1325 | break; 1326 | case ENFILE: 1327 | strncpy(message, "Too many files open in system.", size); 1328 | break; 1329 | case ENOBUFS: 1330 | strncpy(message, "No buffer space available.", size); 1331 | break; 1332 | #if !defined(MACOSX) && !defined(FREEBSD) 1333 | case ENODATA: 1334 | strncpy(message, "[XSR] [Option Start] No message is available on the STREAM head read queue. [Option End]", size); 1335 | break; 1336 | #endif 1337 | case ENODEV: 1338 | strncpy(message, "No such device.", size); 1339 | break; 1340 | case ENOENT: 1341 | strncpy(message, "No such file or directory.", size); 1342 | break; 1343 | case ENOEXEC: 1344 | strncpy(message, "Executable file format error.", size); 1345 | break; 1346 | case ENOLCK: 1347 | strncpy(message, "No locks available.", size); 1348 | break; 1349 | #if !defined(MACOSX) && !defined(OSF1) && !defined(FREEBSD) 1350 | case ENOLINK: 1351 | strncpy(message, "Reserved.", size); 1352 | break; 1353 | #endif 1354 | case ENOMEM: 1355 | strncpy(message, "Not enough space.", size); 1356 | break; 1357 | case ENOMSG: 1358 | strncpy(message, "No message of the desired type.", size); 1359 | break; 1360 | case ENOPROTOOPT: 1361 | strncpy(message, "Protocol not available.", size); 1362 | break; 1363 | case ENOSPC: 1364 | strncpy(message, "No space left on device.", size); 1365 | break; 1366 | #if !defined(MACOSX) && !defined(FREEBSD) 1367 | case ENOSR: 1368 | strncpy(message, "[XSR] [Option Start] No STREAM resources. [Option End]", size); 1369 | break; 1370 | #endif 1371 | #if !defined(MACOSX) && !defined(FREEBSD) 1372 | case ENOSTR: 1373 | strncpy(message, "[XSR] [Option Start] Not a STREAM. [Option End]", size); 1374 | break; 1375 | #endif 1376 | case ENOSYS: 1377 | strncpy(message, "Function not supported.", size); 1378 | break; 1379 | case ENOTCONN: 1380 | strncpy(message, "The socket is not connected.", size); 1381 | break; 1382 | case ENOTDIR: 1383 | strncpy(message, "Not a directory.", size); 1384 | break; 1385 | #if !defined(AIX) && !defined(AIX5) 1386 | case ENOTEMPTY: 1387 | strncpy(message, "Directory not empty.", size); 1388 | break; 1389 | #endif 1390 | case ENOTSOCK: 1391 | strncpy(message, "Not a socket.", size); 1392 | break; 1393 | case ENOTSUP: 1394 | strncpy(message, "Not supported.", size); 1395 | break; 1396 | case ENOTTY: 1397 | strncpy(message, "Inappropriate I/O control operation.", size); 1398 | break; 1399 | case ENXIO: 1400 | strncpy(message, "No such device or address.", size); 1401 | break; 1402 | #if !defined(LINUX) && !defined(MACOSX) && !defined(FREEBSD) 1403 | case EOPNOTSUPP: 1404 | strncpy(message, "Operation not supported on socket.", size); 1405 | break; 1406 | #endif 1407 | #if !defined(OSF1) 1408 | case EOVERFLOW: 1409 | strncpy(message, "Value too large to be stored in data type.", size); 1410 | break; 1411 | #endif 1412 | case EPERM: 1413 | strncpy(message, "Operation not permitted.", size); 1414 | break; 1415 | case EPIPE: 1416 | strncpy(message, "Broken pipe.", size); 1417 | break; 1418 | #if !defined(MACOSX) && !defined(FREEBSD) 1419 | case EPROTO: 1420 | strncpy(message, "Protocol error.", size); 1421 | break; 1422 | #endif 1423 | case EPROTONOSUPPORT: 1424 | strncpy(message, "Protocol not supported.", size); 1425 | break; 1426 | case EPROTOTYPE: 1427 | strncpy(message, "Protocol wrong type for socket.", size); 1428 | break; 1429 | case ERANGE: 1430 | strncpy(message, "Result too large.", size); 1431 | break; 1432 | case EROFS: 1433 | strncpy(message, "Read-only file system.", size); 1434 | break; 1435 | case ESPIPE: 1436 | strncpy(message, "Invalid seek.", size); 1437 | break; 1438 | case ESRCH: 1439 | strncpy(message, "No such process.", size); 1440 | break; 1441 | case ESTALE: 1442 | strncpy(message, "Reserved.", size); 1443 | break; 1444 | #if !defined(MACOSX) && !defined(FREEBSD) 1445 | case ETIME: 1446 | strncpy(message, "[XSR] [Option Start] Stream ioctl() timeout. [Option End]", size); 1447 | break; 1448 | #endif 1449 | case ETIMEDOUT: 1450 | strncpy(message, "Connection timed out.", size); 1451 | break; 1452 | case ETXTBSY: 1453 | strncpy(message, "Text file busy.", size); 1454 | break; 1455 | #if !defined(LINUX) && !defined(AIX) && !defined(AIX5) && !defined(MACOSX) && !defined(OSF1) && !defined(SOLARIS) && !defined(FREEBSD) 1456 | case EWOULDBLOCK: 1457 | strncpy(message, "Operation would block.", size); 1458 | break; 1459 | #endif 1460 | case EXDEV: 1461 | strncpy(message, "Cross-device link.", size); 1462 | break; 1463 | default: 1464 | strcpy(message, ""); 1465 | break; 1466 | } 1467 | #endif 1468 | 1469 | return (int) strlen(message); 1470 | } 1471 | 1472 | 1473 | 1474 | 1475 | --------------------------------------------------------------------------------