Skip to content

Commit 8fe1dac

Browse files
committed
Initial commit.
0 parents  commit 8fe1dac

21 files changed

+4844
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dist
2+
node_modules
3+
.idea

LICENSE

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2019 Enzo Madda <[email protected]>, sqlitempi.com
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.

package.json

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "sqlite-mpi-client-js",
3+
"version": "0.0.1",
4+
"main": "dist/index.js",
5+
"types": "dist/index.d.ts",
6+
"author": "Enzo Madda",
7+
"license": "MIT",
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/sqlite-mpi/sqlite-mpi-client-js.git"
11+
},
12+
"scripts": {
13+
"test": "jest"
14+
},
15+
"jest": {
16+
"testRegex": "test/all/.*\\.js$"
17+
},
18+
"dependencies": {
19+
"lodash": "^4.17.15",
20+
"uuid": "^3.3.3"
21+
},
22+
"devDependencies": {
23+
"jest": "^24.9.0",
24+
"smpi-iop-node-ffi": "file:./../smpi-iop-node-ffi",
25+
"typescript": "^3.7.2"
26+
}
27+
}

sh/run-tests.sh

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
# Note: `-t` allows filtering tests by name:
3+
# `jest test/all/params.js -t "bound"`
4+
5+
# Issue: `jest` runtime has an issue running many tests at the same time that use the `ffi` NPM package.
6+
# Fix: run separately.
7+
jest test/all/basic;
8+
jest test/all/extensions.js;
9+
jest test/all/interleaving.js;
10+
jest test/all/params.js;

sh/watch-test.sh

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
#jest --watch #runs jest -o by default
3+
jest --watchAll #runs all tests
4+
5+
# Runs tests with matching description:
6+
# jest -t "Param"

sh/watch.sh

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env bash
2+
tsc --watch

sh/yarn-add-ffi.sh

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# After re-compiling the local FFI dylib during development, it needs to be removed and added to use the new binary:
2+
3+
yarn remove smpi-iop-node-ffi;
4+
yarn add --dev file:./../smpi-iop-node-ffi;

src/classes/client.ts

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import {
2+
toReqRes
3+
} from "./../lib/iop-to-reqres";
4+
5+
import {
6+
throwIfError,
7+
} from "./../lib-app/util";
8+
9+
import {ReadTx, WriteTx} from "./tx";
10+
11+
class Client {
12+
rr = null;
13+
14+
constructor(iop) {
15+
const rr = toReqRes(iop);
16+
17+
this.rr = throwIfError(rr);
18+
}
19+
20+
newFileRef(absFile) {
21+
return new FileRef(this.rr, absFile);
22+
}
23+
}
24+
25+
class FileRef {
26+
rr = null;
27+
absFile = null;
28+
29+
constructor(rr, absFile) {
30+
this.rr = rr;
31+
this.absFile = absFile;
32+
}
33+
34+
async getReadTx() {
35+
const {rr} = this;
36+
37+
const {tx_id} = await rr.send({
38+
fn: "file/get_read_tx",
39+
args: {
40+
file: this.absFile
41+
}
42+
});
43+
44+
return new ReadTx(rr, tx_id);
45+
}
46+
47+
async getWriteTx() {
48+
const {rr} = this;
49+
50+
const {tx_id} = await rr.send({
51+
fn: "file/get_write_tx",
52+
args: {
53+
file: this.absFile
54+
}
55+
});
56+
57+
return new WriteTx(rr, tx_id);
58+
}
59+
}
60+
61+
export {
62+
Client
63+
}

src/classes/tx.ts

+211
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import _ from "lodash";
2+
3+
/**
4+
* @returns string `q` without params, `q_params` with.
5+
*/
6+
const getFn = (base, p) => {
7+
if (hasParams(p)) {
8+
return `${base}_params`
9+
}
10+
return base;
11+
};
12+
13+
const hasParams = (p) => {
14+
return _.isObject(p) || _.isArray(p)
15+
};
16+
17+
const getParamKey = (p) => {
18+
if (_.isPlainObject(p)) {
19+
return "key_based";
20+
}
21+
if (_.isArray(p)) {
22+
return "index_based";
23+
}
24+
25+
// This function should only be called when `params` is valid.
26+
throw Error("Invalid params");
27+
};
28+
29+
const getRSet = async (rr, tx_id, base, q, p) => {
30+
if (p !== null && !hasParams(p)) {
31+
throw Error("Invalid type provided for params.");
32+
}
33+
34+
const fn = `tx/${getFn(base, p)}`;
35+
36+
const args = {
37+
tx_id,
38+
q
39+
};
40+
41+
if (hasParams(p)) {
42+
args[getParamKey(p)] = p;
43+
}
44+
45+
const rset = await rr.send({
46+
fn,
47+
args
48+
});
49+
50+
return new RSet(rset);
51+
};
52+
53+
class ReadTx {
54+
rr = null;
55+
txId = null;
56+
57+
constructor(rr, txId) {
58+
this.rr = rr;
59+
this.txId = txId;
60+
}
61+
62+
63+
async q(q, p = null) {
64+
const {rr, txId: tx_id} = this;
65+
return getRSet(rr, tx_id, "q", q, p);
66+
}
67+
68+
async read(q, p = null) {
69+
const {rr, txId: tx_id} = this;
70+
return getRSet(rr, tx_id, "read", q, p);
71+
}
72+
73+
async commit() {
74+
const {rr, txId: tx_id} = this;
75+
76+
const rset = await rr.send({
77+
fn: "tx/commit",
78+
args: {
79+
tx_id
80+
}
81+
});
82+
83+
return new RSet(rset);
84+
}
85+
86+
async rollback() {
87+
const {rr, txId: tx_id} = this;
88+
89+
const rset = await rr.send({
90+
fn: "tx/rollback",
91+
args: {
92+
tx_id
93+
}
94+
});
95+
96+
return new RSet(rset);
97+
}
98+
}
99+
100+
101+
class WriteTx {
102+
rr = null;
103+
txId = null;
104+
105+
constructor(rr, txId) {
106+
this.rr = rr;
107+
this.txId = txId;
108+
}
109+
110+
async q(q, p = null) {
111+
const {rr, txId: tx_id} = this;
112+
return getRSet(rr, tx_id, "q", q, p);
113+
}
114+
115+
async read(q, p = null) {
116+
const {rr, txId: tx_id} = this;
117+
return getRSet(rr, tx_id, "read", q, p);
118+
}
119+
120+
async write(q, p = null) {
121+
const {rr, txId: tx_id} = this;
122+
return getRSet(rr, tx_id, "write", q, p);
123+
}
124+
125+
async commit() {
126+
const {rr, txId: tx_id} = this;
127+
128+
const rset = await rr.send({
129+
fn: "tx/commit",
130+
args: {
131+
tx_id
132+
}
133+
});
134+
135+
return new RSet(rset);
136+
}
137+
138+
async rollback() {
139+
const {rr, txId: tx_id} = this;
140+
141+
const rset = await rr.send({
142+
fn: "tx/rollback",
143+
args: {
144+
tx_id
145+
}
146+
});
147+
148+
return new RSet(rset);
149+
}
150+
}
151+
152+
153+
class RSet {
154+
res = null;
155+
rows_cache = null;
156+
157+
constructor(res) {
158+
// @todo/low deep freeze object.
159+
this.res = res;
160+
}
161+
162+
/**
163+
* Allow JS destructuring syntax by using a getter.
164+
* - E.g. `const {rows} = rset;`
165+
* Only compute rows when requested.
166+
*/
167+
get rows() {
168+
if (this.rows_cache === null) {
169+
// Convert `[[1,2,3]]` into `[{x: 1, y: 2, z: 3}]`.
170+
const {col_names, rows: {data}} = this.res;
171+
const keys = col_names.map(({name}) => name);
172+
const o = [];
173+
174+
for (const r of data) {
175+
const one = {};
176+
177+
for (const [i, v] of r.entries()) {
178+
one[keys[i]] = v;
179+
}
180+
o.push(one);
181+
}
182+
this.rows_cache = o;
183+
}
184+
return this.rows_cache;
185+
}
186+
187+
get rowsAsArray() {
188+
return this.res.rows.data;
189+
}
190+
191+
get cols() {
192+
return this.res.col_names.map(({name}) => name);
193+
}
194+
195+
get colsOrigin() {
196+
return this.res.col_names.map(({name_origin}) => name_origin);
197+
}
198+
199+
get rowsChanged() {
200+
const c = this.res.rows_changed;
201+
202+
// `null` when the query is not an insert, update or delete.
203+
return c === null ? 0 : c;
204+
}
205+
}
206+
207+
export {
208+
ReadTx,
209+
WriteTx
210+
}
211+

src/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import {smpi} from "./smpi";
2+
3+
export {
4+
smpi,
5+
}

0 commit comments

Comments
 (0)