@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.
- package/README.md +32 -48
- package/binding.gyp +66 -0
- package/lib/native.js +6 -23
- package/package.json +25 -3
- package/prebuilds/darwin-arm64/@mimersql+node-mimer.node +0 -0
- package/prebuilds/darwin-x64/@mimersql+node-mimer.node +0 -0
- package/prebuilds/linux-arm64/@mimersql+node-mimer.node +0 -0
- package/prebuilds/linux-x64/@mimersql+node-mimer.node +0 -0
- package/prebuilds/win32-x64/@mimersql+node-mimer.node +0 -0
- package/scripts/check-mimer.js +149 -0
- package/scripts/find-mimer-windows.js +179 -0
- package/src/connection.cc +473 -0
- package/src/connection.h +77 -0
- package/src/helpers.cc +440 -0
- package/src/helpers.h +79 -0
- package/src/mimer_addon.cc +49 -0
- package/src/resultset.cc +161 -0
- package/src/resultset.h +74 -0
- package/src/statement.cc +201 -0
- package/src/statement.h +74 -0
- package/lib/find-mimer-library.js +0 -154
- package/lib/koffi-binding.js +0 -970
package/src/resultset.cc
ADDED
|
@@ -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
|
+
}
|
package/src/resultset.h
ADDED
|
@@ -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
|
package/src/statement.cc
ADDED
|
@@ -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
|
+
}
|
package/src/statement.h
ADDED
|
@@ -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;
|