@mimersql/node-mimer 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,161 @@
1
+ // Copyright (c) 2026 Mimer Information Technology
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in all
11
+ // copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ // SOFTWARE.
20
+ //
21
+ // See license for more details.
22
+
23
+ #include "resultset.h"
24
+ #include "connection.h"
25
+ #include "helpers.h"
26
+
27
+ Napi::FunctionReference MimerResultSetWrapper::constructor_;
28
+
29
+ Napi::Object MimerResultSetWrapper::Init(Napi::Env env, Napi::Object exports) {
30
+ Napi::Function func = DefineClass(env, "ResultSet", {
31
+ InstanceMethod("fetchNext", &MimerResultSetWrapper::FetchNext),
32
+ InstanceMethod("getFields", &MimerResultSetWrapper::GetFields),
33
+ InstanceMethod("close", &MimerResultSetWrapper::Close),
34
+ InstanceMethod("isClosed", &MimerResultSetWrapper::IsClosed)
35
+ });
36
+
37
+ constructor_ = Napi::Persistent(func);
38
+ constructor_.SuppressDestruct();
39
+
40
+ exports.Set("ResultSet", func);
41
+ return exports;
42
+ }
43
+
44
+ /**
45
+ * Create a new ResultSet from C++.
46
+ * Passes the MimerStatement handle and columnCount as External values.
47
+ */
48
+ Napi::Object MimerResultSetWrapper::NewInstance(Napi::Env env,
49
+ MimerStatement stmt,
50
+ int columnCount) {
51
+ Napi::External<MimerStatement> extStmt =
52
+ Napi::External<MimerStatement>::New(env, new MimerStatement(stmt));
53
+ Napi::Number colCount = Napi::Number::New(env, columnCount);
54
+ return constructor_.New({extStmt, colCount});
55
+ }
56
+
57
+ /**
58
+ * Constructor — receives External<MimerStatement> and columnCount.
59
+ */
60
+ MimerResultSetWrapper::MimerResultSetWrapper(const Napi::CallbackInfo& info)
61
+ : Napi::ObjectWrap<MimerResultSetWrapper>(info),
62
+ stmt_(MIMERNULLHANDLE), columnCount_(0),
63
+ closed_(false), exhausted_(false), parentConnection_(nullptr) {
64
+ Napi::Env env = info.Env();
65
+
66
+ if (info.Length() < 2 || !info[0].IsExternal() || !info[1].IsNumber()) {
67
+ Napi::TypeError::New(env,
68
+ "ResultSet cannot be constructed directly; use connection.executeQuery()")
69
+ .ThrowAsJavaScriptException();
70
+ return;
71
+ }
72
+
73
+ MimerStatement* stmtPtr = info[0].As<Napi::External<MimerStatement>>().Data();
74
+ stmt_ = *stmtPtr;
75
+ delete stmtPtr;
76
+
77
+ columnCount_ = info[1].As<Napi::Number>().Int32Value();
78
+
79
+ // Cache column metadata once
80
+ CacheColumnMetadata(stmt_, columnCount_, colNames_, colTypes_);
81
+ }
82
+
83
+ MimerResultSetWrapper::~MimerResultSetWrapper() {
84
+ CloseInternal();
85
+ }
86
+
87
+ void MimerResultSetWrapper::SetParentConnection(MimerConnection* conn) {
88
+ parentConnection_ = conn;
89
+ }
90
+
91
+ /**
92
+ * Called by MimerConnection::Close() — close handles without unregistering.
93
+ */
94
+ void MimerResultSetWrapper::Invalidate() {
95
+ if (!closed_ && stmt_ != MIMERNULLHANDLE) {
96
+ MimerCloseCursor(stmt_);
97
+ MimerEndStatement(&stmt_);
98
+ }
99
+ closed_ = true;
100
+ parentConnection_ = nullptr;
101
+ }
102
+
103
+ /**
104
+ * Close handles AND unregister from parent connection.
105
+ */
106
+ void MimerResultSetWrapper::CloseInternal() {
107
+ if (!closed_ && stmt_ != MIMERNULLHANDLE) {
108
+ MimerCloseCursor(stmt_);
109
+ MimerEndStatement(&stmt_);
110
+ }
111
+ closed_ = true;
112
+ if (parentConnection_) {
113
+ parentConnection_->UnregisterResultSet(this);
114
+ parentConnection_ = nullptr;
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Fetch the next row. Returns a JS object, or null when exhausted / closed.
120
+ */
121
+ Napi::Value MimerResultSetWrapper::FetchNext(const Napi::CallbackInfo& info) {
122
+ Napi::Env env = info.Env();
123
+
124
+ if (closed_ || exhausted_) {
125
+ return env.Null();
126
+ }
127
+
128
+ int rc = MimerFetch(stmt_);
129
+ if (rc == MIMER_SUCCESS) {
130
+ return FetchSingleRow(env, stmt_, columnCount_, colNames_, colTypes_);
131
+ }
132
+
133
+ // No more rows (or error) — mark exhausted
134
+ exhausted_ = true;
135
+ return env.Null();
136
+ }
137
+
138
+ /**
139
+ * Return column metadata array (same format as fields in query results).
140
+ */
141
+ Napi::Value MimerResultSetWrapper::GetFields(const Napi::CallbackInfo& info) {
142
+ Napi::Env env = info.Env();
143
+
144
+ if (closed_) {
145
+ return Napi::Array::New(env, 0);
146
+ }
147
+
148
+ return BuildFieldsArray(env, stmt_, columnCount_);
149
+ }
150
+
151
+ /**
152
+ * Explicitly close the cursor and release the statement handle.
153
+ */
154
+ Napi::Value MimerResultSetWrapper::Close(const Napi::CallbackInfo& info) {
155
+ CloseInternal();
156
+ return Napi::Boolean::New(info.Env(), true);
157
+ }
158
+
159
+ Napi::Value MimerResultSetWrapper::IsClosed(const Napi::CallbackInfo& info) {
160
+ return Napi::Boolean::New(info.Env(), closed_);
161
+ }
@@ -0,0 +1,74 @@
1
+ // Copyright (c) 2026 Mimer Information Technology
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in all
11
+ // copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ // SOFTWARE.
20
+ //
21
+ // See license for more details.
22
+
23
+ #ifndef MIMER_RESULTSET_H
24
+ #define MIMER_RESULTSET_H
25
+
26
+ #include <napi.h>
27
+ #include <mimerapi.h>
28
+ #include <vector>
29
+ #include <string>
30
+
31
+ class MimerConnection; // forward declaration
32
+
33
+ /**
34
+ * MimerResultSetWrapper wraps an open Mimer cursor for row-at-a-time
35
+ * fetching. Owns the MimerStatement handle (cursor already opened by
36
+ * MimerConnection::ExecuteQuery).
37
+ *
38
+ * Lifecycle follows the same pattern as MimerStmtWrapper:
39
+ * - Invalidate() — called by connection close (closes handles, no unregister)
40
+ * - CloseInternal() — closes handles AND unregisters from parent
41
+ * - Destructor calls CloseInternal()
42
+ */
43
+ class MimerResultSetWrapper : public Napi::ObjectWrap<MimerResultSetWrapper> {
44
+ public:
45
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
46
+ static Napi::Object NewInstance(Napi::Env env, MimerStatement stmt,
47
+ int columnCount);
48
+ MimerResultSetWrapper(const Napi::CallbackInfo& info);
49
+ ~MimerResultSetWrapper();
50
+
51
+ void SetParentConnection(MimerConnection* conn);
52
+ void Invalidate();
53
+
54
+ private:
55
+ MimerStatement stmt_;
56
+ int columnCount_;
57
+ std::vector<std::string> colNames_;
58
+ std::vector<int> colTypes_;
59
+ bool closed_;
60
+ bool exhausted_;
61
+ MimerConnection* parentConnection_;
62
+
63
+ // JS-exposed methods
64
+ Napi::Value FetchNext(const Napi::CallbackInfo& info);
65
+ Napi::Value GetFields(const Napi::CallbackInfo& info);
66
+ Napi::Value Close(const Napi::CallbackInfo& info);
67
+ Napi::Value IsClosed(const Napi::CallbackInfo& info);
68
+
69
+ void CloseInternal();
70
+
71
+ static Napi::FunctionReference constructor_;
72
+ };
73
+
74
+ #endif // MIMER_RESULTSET_H
@@ -0,0 +1,201 @@
1
+ // Copyright (c) 2026 Mimer Information Technology
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in all
11
+ // copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ // SOFTWARE.
20
+ //
21
+ // See license for more details.
22
+
23
+ #include "statement.h"
24
+ #include "connection.h"
25
+ #include "helpers.h"
26
+ #include <sstream>
27
+
28
+ Napi::FunctionReference MimerStmtWrapper::constructor_;
29
+
30
+ /**
31
+ * Initialize the Statement class and export it
32
+ */
33
+ Napi::Object MimerStmtWrapper::Init(Napi::Env env, Napi::Object exports) {
34
+ Napi::Function func = DefineClass(env, "Statement", {
35
+ InstanceMethod("execute", &MimerStmtWrapper::Execute),
36
+ InstanceMethod("close", &MimerStmtWrapper::Close)
37
+ });
38
+
39
+ constructor_ = Napi::Persistent(func);
40
+ constructor_.SuppressDestruct();
41
+
42
+ exports.Set("Statement", func);
43
+ return exports;
44
+ }
45
+
46
+ /**
47
+ * Create a new MimerStmtWrapper instance from C++.
48
+ * Called by MimerConnection::Prepare().
49
+ * We pass session and sql as External values through the constructor.
50
+ */
51
+ Napi::Object MimerStmtWrapper::NewInstance(Napi::Env env, MimerSession session,
52
+ Napi::String sql) {
53
+ // Pass session pointer and SQL string to the JS constructor
54
+ Napi::External<MimerSession> extSession =
55
+ Napi::External<MimerSession>::New(env, new MimerSession(session));
56
+ return constructor_.New({extSession, sql});
57
+ }
58
+
59
+ /**
60
+ * Constructor - prepares the SQL statement.
61
+ * When called from NewInstance: info[0] = External<MimerSession>, info[1] = String (SQL)
62
+ */
63
+ MimerStmtWrapper::MimerStmtWrapper(const Napi::CallbackInfo& info)
64
+ : Napi::ObjectWrap<MimerStmtWrapper>(info),
65
+ stmt_(MIMERNULLHANDLE), columnCount_(0), closed_(false),
66
+ parentConnection_(nullptr) {
67
+ Napi::Env env = info.Env();
68
+
69
+ if (info.Length() < 2 || !info[0].IsExternal() || !info[1].IsString()) {
70
+ Napi::TypeError::New(env, "Statement cannot be constructed directly; use connection.prepare()")
71
+ .ThrowAsJavaScriptException();
72
+ return;
73
+ }
74
+
75
+ MimerSession* sessionPtr = info[0].As<Napi::External<MimerSession>>().Data();
76
+ std::string sql = info[1].As<Napi::String>().Utf8Value();
77
+
78
+ int rc = MimerBeginStatement8(*sessionPtr, sql.c_str(), MIMER_FORWARD_ONLY, &stmt_);
79
+
80
+ // Clean up the allocated session pointer copy
81
+ delete sessionPtr;
82
+
83
+ if (rc < 0) {
84
+ ThrowMimerError(env, rc, "MimerBeginStatement8");
85
+ return;
86
+ }
87
+
88
+ columnCount_ = MimerColumnCount(stmt_);
89
+ }
90
+
91
+ /**
92
+ * Destructor - clean up statement handle if not already closed
93
+ */
94
+ MimerStmtWrapper::~MimerStmtWrapper() {
95
+ if (!closed_ && stmt_ != MIMERNULLHANDLE) {
96
+ MimerEndStatement(&stmt_);
97
+ // Unregister from parent if it still exists
98
+ if (parentConnection_) {
99
+ parentConnection_->UnregisterStatement(this);
100
+ parentConnection_ = nullptr;
101
+ }
102
+ }
103
+ }
104
+
105
+ void MimerStmtWrapper::SetParentConnection(MimerConnection* conn) {
106
+ parentConnection_ = conn;
107
+ }
108
+
109
+ /**
110
+ * Called by MimerConnection::Close() to invalidate this statement.
111
+ * Closes the Mimer handle but does NOT unregister from the connection
112
+ * (the connection is clearing its own set).
113
+ */
114
+ void MimerStmtWrapper::Invalidate() {
115
+ if (!closed_ && stmt_ != MIMERNULLHANDLE) {
116
+ MimerEndStatement(&stmt_);
117
+ }
118
+ closed_ = true;
119
+ parentConnection_ = nullptr;
120
+ }
121
+
122
+ /**
123
+ * Internal close: release the Mimer handle and unregister from the
124
+ * parent connection.
125
+ */
126
+ void MimerStmtWrapper::CloseInternal() {
127
+ if (!closed_ && stmt_ != MIMERNULLHANDLE) {
128
+ MimerEndStatement(&stmt_);
129
+ }
130
+ closed_ = true;
131
+ if (parentConnection_) {
132
+ parentConnection_->UnregisterStatement(this);
133
+ parentConnection_ = nullptr;
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Execute the prepared statement with optional parameters.
139
+ * Arguments: params (optional array)
140
+ * Returns: result object with rows and metadata
141
+ */
142
+ Napi::Value MimerStmtWrapper::Execute(const Napi::CallbackInfo& info) {
143
+ Napi::Env env = info.Env();
144
+
145
+ if (closed_) {
146
+ Napi::Error::New(env, "Statement is closed")
147
+ .ThrowAsJavaScriptException();
148
+ return env.Undefined();
149
+ }
150
+
151
+ // Bind parameters if provided
152
+ if (info.Length() >= 1 && info[0].IsArray()
153
+ && info[0].As<Napi::Array>().Length() > 0) {
154
+ Napi::Array params = info[0].As<Napi::Array>();
155
+ BindParameters(env, stmt_, params);
156
+ if (env.IsExceptionPending()) {
157
+ return env.Undefined();
158
+ }
159
+ }
160
+
161
+ bool hasResultSet = (columnCount_ > 0);
162
+ Napi::Object result = Napi::Object::New(env);
163
+ int rc;
164
+
165
+ if (hasResultSet) {
166
+ // Build column metadata before fetching rows
167
+ result.Set("fields", BuildFieldsArray(env, stmt_, columnCount_));
168
+
169
+ rc = MimerOpenCursor(stmt_);
170
+ if (rc < 0) {
171
+ ThrowMimerError(env, rc, "MimerOpenCursor");
172
+ return env.Undefined();
173
+ }
174
+
175
+ Napi::Array rows = FetchResults(env, stmt_, columnCount_);
176
+
177
+ // Close cursor but keep statement alive for reuse
178
+ MimerCloseCursor(stmt_);
179
+
180
+ result.Set("rows", rows);
181
+ result.Set("rowCount", Napi::Number::New(env, rows.Length()));
182
+ } else {
183
+ rc = MimerExecute(stmt_);
184
+ if (rc < 0) {
185
+ ThrowMimerError(env, rc, "MimerExecute");
186
+ return env.Undefined();
187
+ }
188
+ result.Set("rowCount", Napi::Number::New(env, rc));
189
+ }
190
+
191
+ return result;
192
+ }
193
+
194
+ /**
195
+ * Close the prepared statement and release its handle.
196
+ */
197
+ Napi::Value MimerStmtWrapper::Close(const Napi::CallbackInfo& info) {
198
+ Napi::Env env = info.Env();
199
+ CloseInternal();
200
+ return Napi::Boolean::New(env, true);
201
+ }
@@ -0,0 +1,74 @@
1
+ // Copyright (c) 2026 Mimer Information Technology
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in all
11
+ // copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ // SOFTWARE.
20
+ //
21
+ // See license for more details.
22
+
23
+ #ifndef MIMER_STATEMENT_H
24
+ #define MIMER_STATEMENT_H
25
+
26
+ #include <napi.h>
27
+ #include <mimerapi.h>
28
+
29
+ class MimerConnection; // forward declaration
30
+
31
+ /**
32
+ * MimerStmtWrapper wraps a Mimer prepared statement for reuse.
33
+ * Note: Cannot use 'MimerStatement' as class name since it's
34
+ * typedef'd in mimerapi.h.
35
+ *
36
+ * The statement handle persists across multiple execute() calls.
37
+ * After each execution with a cursor, we close the cursor but keep
38
+ * the prepared statement alive for reuse.
39
+ *
40
+ * Tracks its parent connection so it can be invalidated if the
41
+ * connection is closed while this statement is still open.
42
+ */
43
+ class MimerStmtWrapper : public Napi::ObjectWrap<MimerStmtWrapper> {
44
+ public:
45
+ static Napi::Object Init(Napi::Env env, Napi::Object exports);
46
+ static Napi::Object NewInstance(Napi::Env env, MimerSession session,
47
+ Napi::String sql);
48
+ MimerStmtWrapper(const Napi::CallbackInfo& info);
49
+ ~MimerStmtWrapper();
50
+
51
+ // Called by MimerConnection to set the parent after construction
52
+ void SetParentConnection(MimerConnection* conn);
53
+
54
+ // Called by MimerConnection::Close() to invalidate this statement
55
+ // without the statement trying to unregister from the connection
56
+ void Invalidate();
57
+
58
+ private:
59
+ MimerStatement stmt_;
60
+ int columnCount_;
61
+ bool closed_;
62
+ MimerConnection* parentConnection_;
63
+
64
+ // Methods exposed to JavaScript
65
+ Napi::Value Execute(const Napi::CallbackInfo& info);
66
+ Napi::Value Close(const Napi::CallbackInfo& info);
67
+
68
+ // Internal close logic shared by Close() and destructor
69
+ void CloseInternal();
70
+
71
+ static Napi::FunctionReference constructor_;
72
+ };
73
+
74
+ #endif // MIMER_STATEMENT_H
@@ -1,154 +0,0 @@
1
- // Copyright (c) 2026 Mimer Information Technology
2
- //
3
- // Permission is hereby granted, free of charge, to any person obtaining a copy
4
- // of this software and associated documentation files (the "Software"), to deal
5
- // in the Software without restriction, including without limitation the rights
6
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- // copies of the Software, and to permit persons to whom the Software is
8
- // furnished to do so, subject to the following conditions:
9
- //
10
- // The above copyright notice and this permission notice shall be included in all
11
- // copies or substantial portions of the Software.
12
- //
13
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
- // SOFTWARE.
20
- //
21
- // See license for more details.
22
-
23
- 'use strict';
24
-
25
- const fs = require('fs');
26
- const path = require('path');
27
-
28
- /**
29
- * Find the Mimer SQL shared library path for the current platform.
30
- * Returns the path (or library name for ld search) to load with koffi.load().
31
- */
32
- function findMimerLibrary() {
33
- switch (process.platform) {
34
- case 'linux':
35
- return 'libmimerapi.so';
36
-
37
- case 'darwin':
38
- return '/usr/local/lib/libmimerapi.dylib';
39
-
40
- case 'win32':
41
- return findMimerWindows();
42
-
43
- default:
44
- throw new Error(`Unsupported platform: ${process.platform}`);
45
- }
46
- }
47
-
48
- /**
49
- * Find mimapi64.dll on Windows.
50
- * Search order: MIMER_HOME → Registry → Program Files scan → defaults.
51
- */
52
- function findMimerWindows() {
53
- // Helper to check if a directory contains the Mimer DLL
54
- function hasDll(dir) {
55
- if (!dir) return false;
56
- const dllPath = path.join(dir, 'lib', 'mimapi64.dll');
57
- return fs.existsSync(dllPath) ? dllPath : null;
58
- }
59
-
60
- // Try 1: MIMER_HOME environment variable
61
- if (process.env.MIMER_HOME) {
62
- const dll = hasDll(process.env.MIMER_HOME);
63
- if (dll) return dll;
64
- }
65
-
66
- // Try 2: Windows Registry
67
- try {
68
- const { execSync } = require('child_process');
69
- const regEnum = 'reg query "HKLM\\SOFTWARE\\Mimer\\Mimer SQL" 2>nul';
70
- const output = execSync(regEnum, { encoding: 'utf8' });
71
-
72
- const registryVersions = [];
73
- for (const line of output.split('\n')) {
74
- const versionMatch = line.match(/Mimer SQL\\(\d+\.\d+)\s*$/);
75
- if (versionMatch) {
76
- const version = versionMatch[1];
77
- try {
78
- const pathQuery = `reg query "HKLM\\SOFTWARE\\Mimer\\Mimer SQL\\${version}" /v PathName 2>nul`;
79
- const pathOutput = execSync(pathQuery, { encoding: 'utf8' });
80
- const pathMatch = pathOutput.match(/PathName\s+REG_SZ\s+(.+)/);
81
- if (pathMatch && pathMatch[1]) {
82
- const mimerPath = pathMatch[1].trim();
83
- const dll = hasDll(mimerPath);
84
- if (dll) {
85
- registryVersions.push({ version, path: dll });
86
- }
87
- }
88
- } catch (_) { /* skip */ }
89
- }
90
- }
91
-
92
- if (registryVersions.length > 0) {
93
- registryVersions.sort((a, b) => {
94
- const va = a.version.split('.').map(Number);
95
- const vb = b.version.split('.').map(Number);
96
- if (va[0] !== vb[0]) return vb[0] - va[0];
97
- return vb[1] - va[1];
98
- });
99
- return registryVersions[0].path;
100
- }
101
- } catch (_) { /* registry not available */ }
102
-
103
- // Try 3: Program Files scan
104
- const programFiles = [
105
- process.env.ProgramFiles,
106
- process.env['ProgramFiles(x86)'],
107
- ].filter(Boolean);
108
-
109
- const foundPaths = [];
110
- for (const pf of programFiles) {
111
- if (!fs.existsSync(pf)) continue;
112
- try {
113
- const entries = fs.readdirSync(pf, { withFileTypes: true });
114
- for (const entry of entries) {
115
- if (entry.isDirectory() && entry.name.startsWith('Mimer SQL Experience')) {
116
- const fullPath = path.join(pf, entry.name);
117
- const dll = hasDll(fullPath);
118
- if (dll) {
119
- foundPaths.push({
120
- path: dll,
121
- version: entry.name.match(/(\d+\.\d+)/)?.[1] || '0.0',
122
- });
123
- }
124
- }
125
- }
126
- } catch (_) { /* permission error */ }
127
- }
128
-
129
- if (foundPaths.length > 0) {
130
- foundPaths.sort((a, b) => {
131
- const va = a.version.split('.').map(Number);
132
- const vb = b.version.split('.').map(Number);
133
- if (va[0] !== vb[0]) return vb[0] - va[0];
134
- return vb[1] - va[1];
135
- });
136
- return foundPaths[0].path;
137
- }
138
-
139
- // Try 4: Default locations
140
- const defaultPaths = [
141
- 'C:\\Program Files\\Mimer SQL Experience 12.0',
142
- 'C:\\Program Files\\Mimer SQL Experience 11.0',
143
- ];
144
- for (const dp of defaultPaths) {
145
- const dll = hasDll(dp);
146
- if (dll) return dll;
147
- }
148
-
149
- throw new Error(
150
- 'Mimer SQL library not found. Please install Mimer SQL or set MIMER_HOME.'
151
- );
152
- }
153
-
154
- module.exports = findMimerLibrary;