monetdb 1.3.3 → 2.0.0

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.
Files changed (47) hide show
  1. package/.github/workflows/Linux.yml +45 -0
  2. package/.github/workflows/docs.yml +79 -0
  3. package/.github/workflows/macos.yml +43 -0
  4. package/.github/workflows/monetdb-versions.yml +43 -0
  5. package/README.md +43 -512
  6. package/docs/components/alert.tsx +10 -0
  7. package/docs/components/info.tsx +6 -0
  8. package/docs/next.config.js +24 -0
  9. package/docs/package-lock.json +5069 -0
  10. package/docs/package.json +22 -0
  11. package/docs/pages/_app.js +9 -0
  12. package/docs/pages/_meta.json +16 -0
  13. package/docs/pages/apis/_meta.json +4 -0
  14. package/docs/pages/apis/connection.mdx +60 -0
  15. package/docs/pages/apis/result.mdx +39 -0
  16. package/docs/pages/index.mdx +27 -0
  17. package/docs/theme.config.js +35 -0
  18. package/docs/v1/README.md +532 -0
  19. package/package.json +17 -21
  20. package/src/PrepareStatement.ts +37 -0
  21. package/src/connection.ts +125 -0
  22. package/src/defaults.ts +13 -0
  23. package/src/file-transfer.ts +173 -0
  24. package/src/index.ts +3 -0
  25. package/src/mapi.ts +1016 -0
  26. package/src/monetize.ts +67 -0
  27. package/test/connection.ts +43 -0
  28. package/test/exec-queries.ts +100 -0
  29. package/test/filetransfer.ts +94 -0
  30. package/test/prepare-statement.ts +27 -0
  31. package/test/query-stream.ts +41 -0
  32. package/test/tmp/.gitignore +4 -0
  33. package/tsconfig.json +24 -0
  34. package/.travis.yml +0 -11
  35. package/index.js +0 -5
  36. package/src/mapi-connection.js +0 -784
  37. package/src/monetdb-connection.js +0 -385
  38. package/src/utils.js +0 -27
  39. package/test/common.js +0 -45
  40. package/test/install-monetdb.sh +0 -11
  41. package/test/monetdb_stream.js +0 -106
  42. package/test/start-monetdb.sh +0 -38
  43. package/test/test.js +0 -908
  44. package/test/test_connection.js +0 -290
  45. /package/docs/{README.v0.md → v0/README.v0.md} +0 -0
  46. /package/docs/{MapiConnection.md → v1/MapiConnection.md} +0 -0
  47. /package/docs/{v1-notes.md → v1/v1-notes.md} +0 -0
@@ -0,0 +1,125 @@
1
+ import { EventEmitter } from "events";
2
+ import {
3
+ MapiConfig,
4
+ MapiConnection,
5
+ parseMapiUri,
6
+ createMapiConfig,
7
+ HandShakeOption,
8
+ QueryResult,
9
+ QueryStream,
10
+ } from "./mapi";
11
+ import PrepareStatement from "./PrepareStatement";
12
+
13
+ // MAPI URI:
14
+ // tcp socket: mapi:monetdb://[<username>[:<password>]@]<host>[:<port>]/<database>
15
+ // unix domain socket: mapi:monetdb:///[<username>[:<password>]@]path/to/socket?database=<database>
16
+ type MAPI_URI = string;
17
+
18
+ type ConnectCallback = (err?: Error) => void;
19
+
20
+ class Connection extends EventEmitter {
21
+ autoCommit?: boolean;
22
+ replySize?: number;
23
+ sizeHeader?: boolean;
24
+ mapi: MapiConnection;
25
+
26
+ constructor(params: MapiConfig | MAPI_URI) {
27
+ super();
28
+ const config =
29
+ typeof params === "string"
30
+ ? createMapiConfig(parseMapiUri(params))
31
+ : createMapiConfig(params);
32
+ this.mapi = new MapiConnection(config);
33
+ this.autoCommit = config.autoCommit;
34
+ this.replySize = config.replySize;
35
+ }
36
+
37
+ connect(callback?: ConnectCallback): Promise<boolean> {
38
+ const options = [
39
+ new HandShakeOption(
40
+ 1,
41
+ "auto_commit",
42
+ this.autoCommit,
43
+ this.setAutocommit
44
+ ),
45
+ new HandShakeOption(2, "reply_size", this.replySize, this.setReplySize),
46
+ new HandShakeOption(3, "size_header", true, this.setSizeHeader),
47
+ new HandShakeOption(
48
+ 5,
49
+ "time_zone",
50
+ new Date().getTimezoneOffset() * 60,
51
+ this.setTimezone
52
+ ),
53
+ ];
54
+ const mapi = this.mapi;
55
+ return new Promise(async function (resolve, reject) {
56
+ try {
57
+ await mapi.connect(options);
58
+ resolve(mapi.ready());
59
+ if (callback) callback();
60
+ } catch (err) {
61
+ reject(err);
62
+ if (callback) callback(err);
63
+ }
64
+ });
65
+ }
66
+
67
+ close(): Promise<boolean> {
68
+ return this.mapi.disconnect();
69
+ }
70
+
71
+ commit(): Promise<void> {
72
+ return this.execute("COMMIT");
73
+ }
74
+
75
+ private command(str: string): Promise<any> {
76
+ return this.mapi.request(str);
77
+ }
78
+
79
+ execute(sql: string, stream: boolean = false): Promise<any> {
80
+ const query = `s${sql};\n`;
81
+ if (stream && this.replySize !== -1) this.setReplySize(-1);
82
+ return this.mapi.request(query, stream);
83
+ }
84
+
85
+ async prepare(sql: string): Promise<PrepareStatement> {
86
+ const prepSQL = `PREPARE ${sql}`;
87
+ const res = await this.execute(prepSQL);
88
+ return new PrepareStatement(res, this.mapi);
89
+ }
90
+
91
+ setAutocommit(v: boolean): Promise<boolean> {
92
+ const cmd = `Xauto_commit ${Number(v)}`;
93
+ return this.command(cmd).then(() => {
94
+ this.autoCommit = v;
95
+ return this.autoCommit;
96
+ });
97
+ }
98
+
99
+ setReplySize(v: number): Promise<number> {
100
+ const cmd = `Xreply_size ${Number(v)}`;
101
+ return this.command(cmd).then(() => {
102
+ this.replySize = Number(v);
103
+ return this.replySize;
104
+ });
105
+ }
106
+
107
+ setSizeHeader(v: boolean): Promise<boolean> {
108
+ const cmd = `Xsizeheader ${Number(v)}`;
109
+ return this.command(cmd).then(() => {
110
+ this.sizeHeader = v;
111
+ return this.sizeHeader;
112
+ });
113
+ }
114
+
115
+ setTimezone(sec: number): Promise<any> {
116
+ const qry = `SET TIME ZONE INTERVAL '${sec}' SECOND`;
117
+ return this.execute(qry);
118
+ }
119
+
120
+ rollback(): Promise<void> {
121
+ return this.execute("ROLLBACK");
122
+ }
123
+ }
124
+
125
+ export default Connection;
@@ -0,0 +1,13 @@
1
+
2
+ const defaults = {
3
+ host: process.env.MAPI_HOST || 'localhost',
4
+ port: process.env.MAPI_PORT || 50000,
5
+ username: process.env.MAPI_USER || 'monetdb',
6
+ password: process.env.MAPI_PASSWORD || 'monetdb',
7
+ database: process.env.MAPI_DATABASE,
8
+ autoCommit: false,
9
+ replySize: 100,
10
+ };
11
+
12
+ export default defaults;
13
+
@@ -0,0 +1,173 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ import { cwd } from 'node:process';
4
+
5
+
6
+ class FileHandler {
7
+ mapi: any;
8
+ file: string;
9
+ state: string;
10
+ err?: string;
11
+ eof?: boolean;
12
+ fhandle?: fs.FileHandle;
13
+ resolve?: (v?: any) => void;
14
+ reject?: (err?: Error) => void;
15
+
16
+ constructor(mapi: any, file: string) {
17
+ this.mapi = mapi;
18
+ this.file = file;
19
+ this.state = 'init';
20
+ }
21
+
22
+ async close(): Promise<void> {
23
+ if (this.fhandle) {
24
+ this.state = 'closed';
25
+ await this.fhandle.close();
26
+ this.fhandle = undefined;
27
+ }
28
+ }
29
+
30
+ protected makePromise(): Promise<void> {
31
+ return new Promise((resolve, reject) => {
32
+ this.resolve = resolve;
33
+ this.reject = reject;
34
+ });
35
+ }
36
+
37
+ ready(): boolean {
38
+ return this.fhandle !== undefined
39
+ && this.err === undefined
40
+ && this.state === 'ready';
41
+ }
42
+
43
+ async initTransfer(flag: 'r'|'w'): Promise<void> {
44
+ if (this.fhandle === undefined) {
45
+ // for security reasons we do
46
+ // expect file to be relative to cwd
47
+ const fpath = path.join(cwd(), this.file);
48
+ if (fpath.startsWith(cwd())) {
49
+ try {
50
+ this.fhandle = await fs.open(fpath, flag);
51
+ } catch (err) {
52
+ await this.mapi.requestFileTransferError(`${err}\n`, this);
53
+ return this.makePromise();
54
+ }
55
+ // tell server we are okay with the download
56
+ // send magic new line
57
+ await this.mapi.requestFileTransfer(Buffer.from('\n'), this);
58
+ this.state = 'ready';
59
+ if (flag === 'r')
60
+ this.eof = false;
61
+ return this.makePromise();
62
+ } else {
63
+ // send err msg
64
+ await this.mapi.requestFileTransferError('Forbidden\n', this);
65
+ return this.makePromise();
66
+ }
67
+ }
68
+
69
+ }
70
+ }
71
+
72
+
73
+ class FileDownloader extends FileHandler {
74
+ bytesWritten: number;
75
+
76
+ constructor(mapi: any, file: string) {
77
+ super(mapi, file);
78
+ this.bytesWritten = 0;
79
+ }
80
+
81
+ async download(): Promise<void> {
82
+ if (this.state === 'init')
83
+ return this.initTransfer('w');
84
+ }
85
+
86
+ async writeChunk(data: Buffer): Promise<number> {
87
+ let bytes = 0;
88
+ if (this.ready()) {
89
+ try {
90
+ const { bytesWritten, buffer } = await this.fhandle.write(data);
91
+ bytes += bytesWritten;
92
+ } catch(err) {
93
+ this.err = err;
94
+ try {
95
+ await this.mapi.requestAbort();
96
+ } catch(err) {
97
+ // pass
98
+ console.error(err);
99
+ }
100
+ await this.close();
101
+ this.reject(err);
102
+ // kill connection
103
+ await this.mapi.disconnect();
104
+ throw err;
105
+ }
106
+ }
107
+ this.bytesWritten += bytes;
108
+ return bytes;
109
+ }
110
+ }
111
+
112
+
113
+ class FileUploader extends FileHandler {
114
+ skip: number;
115
+ bytesSent: number;
116
+ chunkSize: number;
117
+ eof: boolean;
118
+
119
+ constructor(mapi: any, file: string, skip: number = 0) {
120
+ super(mapi, file);
121
+ this.skip = skip > 0 ? skip - 1 : 0; // line based offset, super confusing
122
+ this.bytesSent = 0;
123
+ // configurable?
124
+ this.chunkSize = 1024 * 1024;
125
+ }
126
+
127
+ async upload(): Promise<void> {
128
+ if (this.state === 'init')
129
+ return this.initTransfer('r');
130
+ try {
131
+ await this.sendChunk();
132
+ } catch(err) {
133
+ this.err = err;
134
+ await this.mapi.requestAbort();
135
+ await this.close();
136
+ return this.reject(err);
137
+ }
138
+ }
139
+
140
+ private async sendChunk(): Promise<void> {
141
+ let bytesRead: number = 0;
142
+ let buffer: Buffer = Buffer.alloc(0);
143
+ do {
144
+ const res = await this.fhandle.read(Buffer.alloc(this.chunkSize), 0, this.chunkSize);
145
+ bytesRead += res.bytesRead;
146
+ const data = Buffer.concat([buffer, res.buffer]).toString('utf8');
147
+ let offset: number = 0;
148
+ let eol = data.indexOf('\n');
149
+ while(this.skip && eol) {
150
+ offset = eol + 1;
151
+ this.skip--;
152
+ eol = data.indexOf('\n', offset);
153
+ }
154
+ buffer = Buffer.from(data).subarray(offset);
155
+ } while(this.skip && this.bytesSent === 0)
156
+
157
+ if (bytesRead > 0) {
158
+ // console.log(`read ${bytesRead} bytes`)
159
+ await this.mapi.requestFileTransfer(buffer.subarray(0, bytesRead), this);
160
+ this.bytesSent += bytesRead;
161
+ // console.log(`sent ${bytesRead} bytes`)
162
+ } else {
163
+ // reached EOF
164
+ this.eof = true;
165
+ // console.log(`reached eof`);
166
+ // send empty block to indicate end of upload
167
+ await this.mapi.requestFileTransfer(Buffer.from(''), this);
168
+ }
169
+ }
170
+ }
171
+
172
+ export { FileUploader, FileDownloader };
173
+
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ import Connection from './connection';
2
+
3
+ export { Connection };