prostgles-server 4.1.127 → 4.1.129
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/dist/DboBuilder/QueryStreamer.d.ts +3 -3
- package/dist/DboBuilder/QueryStreamer.d.ts.map +1 -1
- package/dist/DboBuilder/QueryStreamer.js +106 -100
- package/dist/DboBuilder/QueryStreamer.js.map +1 -1
- package/lib/DboBuilder/QueryStreamer.ts +107 -98
- package/package.json +2 -2
- package/tests/client/index.ts +11 -7
- package/tests/client/package-lock.json +15 -15
- package/tests/client/package.json +2 -2
- package/tests/client_only_queries.ts +51 -23
- package/tests/server/index.ts +3 -0
- package/tests/server/package-lock.json +3 -3
|
@@ -12,15 +12,15 @@ type ClientStreamedRequest = {
|
|
|
12
12
|
};
|
|
13
13
|
type StreamedQuery = ClientStreamedRequest & {
|
|
14
14
|
stream: QueryStreamType | undefined;
|
|
15
|
-
|
|
15
|
+
client: pg.Client | undefined;
|
|
16
|
+
onError: ((error: any) => void);
|
|
16
17
|
};
|
|
17
18
|
export declare class QueryStreamer {
|
|
18
19
|
db: DB;
|
|
19
20
|
dboBuilder: DboBuilder;
|
|
20
21
|
socketQueries: Record<string, Record<string, StreamedQuery>>;
|
|
21
|
-
pool: pg.Pool;
|
|
22
|
-
adminPool: pg.Pool;
|
|
23
22
|
constructor(dboBuilder: DboBuilder);
|
|
23
|
+
getConnection: (onError: ((err: any) => void) | undefined) => pg.Client;
|
|
24
24
|
create: (query: ClientStreamedRequest) => Promise<SocketSQLStreamServer>;
|
|
25
25
|
}
|
|
26
26
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryStreamer.d.ts","sourceRoot":"","sources":["../../lib/DboBuilder/QueryStreamer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,
|
|
1
|
+
{"version":3,"file":"QueryStreamer.d.ts","sourceRoot":"","sources":["../../lib/DboBuilder/QueryStreamer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAY,UAAU,EAAyB,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACrG,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,eAAe,MAAM,iBAAiB,CAAC;AAK9C,KAAK,qBAAqB,GAAG;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAE7B,CAAA;AACD,KAAK,aAAa,GAAG,qBAAqB,GAAG;IAC3C,MAAM,EAAE,eAAe,GAAG,SAAS,CAAC;IACpC,MAAM,EAAE,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC,CAAC;CACjC,CAAA;AAWD,qBAAa,aAAa;IACxB,EAAE,EAAE,EAAE,CAAC;IACP,UAAU,EAAE,UAAU,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAM;gBACtD,UAAU,EAAE,UAAU;IAKlC,aAAa,mBAAoB,GAAG,KAAK,IAAI,4BAO5C;IAED,MAAM,UAAiB,qBAAqB,KAAG,QAAQ,qBAAqB,CAAC,CAqJ5E;CACF"}
|
|
@@ -18,14 +18,18 @@ class QueryStreamer {
|
|
|
18
18
|
db;
|
|
19
19
|
dboBuilder;
|
|
20
20
|
socketQueries = {};
|
|
21
|
-
pool;
|
|
22
|
-
adminPool;
|
|
23
21
|
constructor(dboBuilder) {
|
|
24
22
|
this.dboBuilder = dboBuilder;
|
|
25
23
|
this.db = dboBuilder.db;
|
|
26
|
-
this.pool = new pg.Pool(typeof this.db.$cn === "string" ? { connectionString: this.db.$cn } : this.db.$cn);
|
|
27
|
-
this.adminPool = new pg.Pool(typeof this.db.$cn === "string" ? { connectionString: this.db.$cn } : this.db.$cn);
|
|
28
24
|
}
|
|
25
|
+
getConnection = (onError) => {
|
|
26
|
+
const connectionInfo = typeof this.db.$cn === "string" ? { connectionString: this.db.$cn } : this.db.$cn;
|
|
27
|
+
const client = new pg.Client(connectionInfo);
|
|
28
|
+
client.on("error", (err) => {
|
|
29
|
+
onError?.(err);
|
|
30
|
+
});
|
|
31
|
+
return client;
|
|
32
|
+
};
|
|
29
33
|
create = async (query) => {
|
|
30
34
|
const { socket, persistConnection } = query;
|
|
31
35
|
const socketId = socket.id;
|
|
@@ -36,11 +40,22 @@ class QueryStreamer {
|
|
|
36
40
|
throw `Must stop existing query ${id} first`;
|
|
37
41
|
}
|
|
38
42
|
this.socketQueries[socketId] ??= {};
|
|
39
|
-
|
|
43
|
+
let errored = false;
|
|
44
|
+
const socketQuery = {
|
|
40
45
|
...query,
|
|
41
|
-
|
|
46
|
+
client: undefined,
|
|
42
47
|
stream: undefined,
|
|
48
|
+
onError: (rawError) => {
|
|
49
|
+
if (errored)
|
|
50
|
+
return;
|
|
51
|
+
errored = true;
|
|
52
|
+
const errorWithoutQuery = (0, dboBuilderUtils_1.getSerializedClientErrorFromPGError)(rawError, { type: "sql" });
|
|
53
|
+
// For some reason query is not present on the error object from sql stream mode
|
|
54
|
+
const error = { ...errorWithoutQuery, query: query.query };
|
|
55
|
+
socket.emit(channel, { type: "error", error });
|
|
56
|
+
},
|
|
43
57
|
};
|
|
58
|
+
this.socketQueries[socketId][id] ??= socketQuery;
|
|
44
59
|
const { options } = query;
|
|
45
60
|
let processID = -1;
|
|
46
61
|
const startStream = async () => {
|
|
@@ -48,107 +63,98 @@ class QueryStreamer {
|
|
|
48
63
|
if (!socketQuery) {
|
|
49
64
|
throw "socket query not found";
|
|
50
65
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
66
|
+
let emittedPackets = 0;
|
|
67
|
+
let batchRows = [];
|
|
68
|
+
let finished = false;
|
|
69
|
+
const batchSize = 10000;
|
|
70
|
+
let stream;
|
|
71
|
+
let poolClient;
|
|
72
|
+
const emit = (type, stream) => {
|
|
73
|
+
const result = stream?._result;
|
|
74
|
+
let packet;
|
|
75
|
+
const ended = type === "ended";
|
|
76
|
+
if (finished)
|
|
77
|
+
return;
|
|
78
|
+
finished = finished || ended;
|
|
79
|
+
if (!emittedPackets) {
|
|
80
|
+
if (!result?.fields)
|
|
81
|
+
throw "No fields";
|
|
82
|
+
const fields = runSQL_1.getDetailedFieldInfo.bind(this.dboBuilder)(result.fields);
|
|
83
|
+
packet = { type: "start", rows: batchRows, fields, ended, processId: processID };
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
packet = { type: "rows", rows: batchRows, ended };
|
|
87
|
+
}
|
|
88
|
+
socket.emit(channel, packet);
|
|
89
|
+
if (ended) {
|
|
90
|
+
if (!result)
|
|
91
|
+
throw "No result info";
|
|
92
|
+
runSQL_1.watchSchemaFallback.bind(this.dboBuilder)({ queryWithoutRLS: query.query, command: result.command });
|
|
93
|
+
}
|
|
94
|
+
emittedPackets++;
|
|
95
|
+
};
|
|
96
|
+
const client = this.getConnection(err => {
|
|
97
|
+
socketQuery.onError(err);
|
|
98
|
+
client.end();
|
|
99
|
+
});
|
|
100
|
+
try {
|
|
101
|
+
await client.connect();
|
|
102
|
+
poolClient = client;
|
|
103
|
+
processID = client.processID;
|
|
104
|
+
const queryStream = new QueryStream(query.query, [], { batchSize: 1e6, highWaterMark: 1e6, rowMode: "array" });
|
|
105
|
+
stream = client.query(queryStream);
|
|
106
|
+
this.socketQueries[socketId][id].client = poolClient;
|
|
107
|
+
this.socketQueries[socketId][id].stream = stream;
|
|
108
|
+
stream.on('data', async (data) => {
|
|
109
|
+
batchRows.push(data);
|
|
110
|
+
if (options?.streamLimit) {
|
|
111
|
+
if (batchRows.length >= options.streamLimit) {
|
|
112
|
+
emit("ended", stream);
|
|
113
|
+
}
|
|
76
114
|
}
|
|
77
|
-
|
|
78
|
-
|
|
115
|
+
if (batchRows.length >= batchSize) {
|
|
116
|
+
emit("rows", stream);
|
|
117
|
+
batchRows = [];
|
|
79
118
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
119
|
+
});
|
|
120
|
+
stream.on('error', error => {
|
|
121
|
+
socketQuery.onError(error);
|
|
122
|
+
});
|
|
123
|
+
stream.on('end', () => {
|
|
124
|
+
emit("ended", stream);
|
|
125
|
+
// release the client when the stream is finished AND connection is not persisted
|
|
126
|
+
if (!options?.persistStreamConnection) {
|
|
127
|
+
delete this.socketQueries[socketId]?.[id];
|
|
128
|
+
client.end();
|
|
85
129
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (!client)
|
|
93
|
-
throw "No client";
|
|
94
|
-
poolClient = client;
|
|
95
|
-
processID = client.processID;
|
|
96
|
-
const queryStream = new QueryStream(query.query, [], { batchSize, rowMode: "array" });
|
|
97
|
-
stream = client.query(queryStream);
|
|
98
|
-
this.socketQueries[socketId][id].poolClient = poolClient;
|
|
99
|
-
this.socketQueries[socketId][id].stream = stream;
|
|
100
|
-
stream.on('data', async (data) => {
|
|
101
|
-
batchRows.push(data);
|
|
102
|
-
if (options?.streamLimit) {
|
|
103
|
-
if (batchRows.length >= options.streamLimit) {
|
|
104
|
-
emit("ended", undefined, stream);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (batchRows.length >= batchSize) {
|
|
108
|
-
emit("rows", undefined, stream);
|
|
109
|
-
batchRows = [];
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
stream.on('error', error => {
|
|
113
|
-
emit("error", error, stream);
|
|
114
|
-
});
|
|
115
|
-
stream.on('end', () => {
|
|
116
|
-
emit("ended", undefined, stream);
|
|
117
|
-
// release the client when the stream is finished
|
|
118
|
-
if (!options?.persistStreamConnection) {
|
|
119
|
-
delete this.socketQueries[socketId]?.[id];
|
|
120
|
-
done();
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
catch (err) {
|
|
126
|
-
emit("error", err, undefined);
|
|
127
|
-
}
|
|
128
|
-
})();
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
socketQuery.onError(err);
|
|
134
|
+
await client.end();
|
|
135
|
+
}
|
|
129
136
|
};
|
|
130
|
-
const stop = (
|
|
131
|
-
|
|
132
|
-
const { stream, poolClient } = this.socketQueries[socketId]?.[id] ?? {};
|
|
137
|
+
const stop = async (opts, cb) => {
|
|
138
|
+
const { stream, client: poolClient } = this.socketQueries[socketId]?.[id] ?? {};
|
|
133
139
|
if (!stream || !poolClient)
|
|
134
140
|
return;
|
|
135
|
-
this.
|
|
136
|
-
|
|
137
|
-
|
|
141
|
+
const client = this.getConnection(undefined);
|
|
142
|
+
try {
|
|
143
|
+
await client.connect();
|
|
138
144
|
if (!client)
|
|
139
145
|
return cb(null, "No client");
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
146
|
+
const stopFunction = opts?.terminate ? "pg_terminate_backend" : "pg_cancel_backend";
|
|
147
|
+
const rows = await client.query(`SELECT ${stopFunction}(pid), pid, state, query FROM pg_stat_activity WHERE pid = $1 AND query = $2`, [processID, query.query]);
|
|
148
|
+
socket.removeAllListeners(unsubChannel);
|
|
149
|
+
socket.removeAllListeners(channel);
|
|
150
|
+
cb({ processID, info: rows.rows[0] });
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
cb(null, error);
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
await client.end();
|
|
157
|
+
}
|
|
152
158
|
};
|
|
153
159
|
socket.removeAllListeners(unsubChannel);
|
|
154
160
|
socket.once(unsubChannel, stop);
|
|
@@ -168,7 +174,7 @@ class QueryStreamer {
|
|
|
168
174
|
setTimeout(() => {
|
|
169
175
|
if (started)
|
|
170
176
|
return;
|
|
171
|
-
stop(
|
|
177
|
+
stop({}, () => {
|
|
172
178
|
// empty
|
|
173
179
|
});
|
|
174
180
|
}, 5e3);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueryStreamer.js","sourceRoot":"","sources":["../../lib/DboBuilder/QueryStreamer.ts"],"names":[],"mappings":";;;AACA,yBAAyB;AACzB,
|
|
1
|
+
{"version":3,"file":"QueryStreamer.js","sourceRoot":"","sources":["../../lib/DboBuilder/QueryStreamer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DB } from "../initProstgles";
|
|
2
2
|
import * as pg from "pg";
|
|
3
|
-
import {
|
|
3
|
+
import { CHANNELS, SQLOptions, SocketSQLStreamPacket, SocketSQLStreamServer } from "prostgles-types";
|
|
4
4
|
import { PRGLIOSocket } from "./DboBuilderTypes";
|
|
5
5
|
import { getSerializedClientErrorFromPGError } from "./dboBuilderUtils";
|
|
6
6
|
import { getDetailedFieldInfo, watchSchemaFallback } from "./runSQL";
|
|
@@ -19,7 +19,8 @@ type ClientStreamedRequest = {
|
|
|
19
19
|
}
|
|
20
20
|
type StreamedQuery = ClientStreamedRequest & {
|
|
21
21
|
stream: QueryStreamType | undefined;
|
|
22
|
-
|
|
22
|
+
client: pg.Client | undefined;
|
|
23
|
+
onError: ((error: any) => void);
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
const shortSocketIds: Record<string, number> = {};
|
|
@@ -35,13 +36,18 @@ export class QueryStreamer {
|
|
|
35
36
|
db: DB;
|
|
36
37
|
dboBuilder: DboBuilder;
|
|
37
38
|
socketQueries: Record<string, Record<string, StreamedQuery>> = {};
|
|
38
|
-
pool: pg.Pool;
|
|
39
|
-
adminPool: pg.Pool;
|
|
40
39
|
constructor(dboBuilder: DboBuilder) {
|
|
41
40
|
this.dboBuilder = dboBuilder;
|
|
42
41
|
this.db = dboBuilder.db;
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getConnection = (onError: ((err: any) => void) | undefined) => {
|
|
45
|
+
const connectionInfo = typeof this.db.$cn === "string"? { connectionString: this.db.$cn } : this.db.$cn as any;
|
|
46
|
+
const client = new pg.Client(connectionInfo);
|
|
47
|
+
client.on("error", (err) => {
|
|
48
|
+
onError?.(err);
|
|
49
|
+
});
|
|
50
|
+
return client;
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
create = async (query: ClientStreamedRequest): Promise<SocketSQLStreamServer> => {
|
|
@@ -54,113 +60,116 @@ export class QueryStreamer {
|
|
|
54
60
|
throw `Must stop existing query ${id} first`;
|
|
55
61
|
}
|
|
56
62
|
|
|
57
|
-
this.socketQueries[socketId] ??= {}
|
|
58
|
-
|
|
63
|
+
this.socketQueries[socketId] ??= {};
|
|
64
|
+
let errored = false;
|
|
65
|
+
const socketQuery = {
|
|
59
66
|
...query,
|
|
60
|
-
|
|
67
|
+
client: undefined,
|
|
61
68
|
stream: undefined,
|
|
69
|
+
onError: (rawError: any) => {
|
|
70
|
+
if(errored) return;
|
|
71
|
+
errored = true;
|
|
72
|
+
|
|
73
|
+
const errorWithoutQuery = getSerializedClientErrorFromPGError(rawError, { type: "sql" });
|
|
74
|
+
// For some reason query is not present on the error object from sql stream mode
|
|
75
|
+
const error = { ...errorWithoutQuery, query: query.query };
|
|
76
|
+
socket.emit(channel, { type: "error", error } satisfies SocketSQLStreamPacket);
|
|
77
|
+
},
|
|
62
78
|
};
|
|
79
|
+
this.socketQueries[socketId]![id] ??= socketQuery;
|
|
63
80
|
const { options } = query
|
|
64
81
|
let processID = -1;
|
|
82
|
+
|
|
65
83
|
const startStream = async () => {
|
|
66
84
|
const socketQuery = this.socketQueries[socketId]?.[id];
|
|
67
85
|
if(!socketQuery){
|
|
68
86
|
throw "socket query not found";
|
|
87
|
+
}
|
|
88
|
+
let emittedPackets = 0;
|
|
89
|
+
let batchRows: any[] = [];
|
|
90
|
+
let finished = false;
|
|
91
|
+
const batchSize = 10000;
|
|
92
|
+
let stream: QueryStreamType;
|
|
93
|
+
let poolClient: pg.Client;
|
|
94
|
+
const emit = (type: "rows" | "ended", stream: QueryStreamType | undefined) => {
|
|
95
|
+
const result = stream?._result as { command: string; fields: any[] } | undefined;
|
|
96
|
+
let packet: SocketSQLStreamPacket | undefined;
|
|
97
|
+
const ended = type === "ended";
|
|
98
|
+
if(finished) return;
|
|
99
|
+
finished = finished || ended;
|
|
100
|
+
if (!emittedPackets) {
|
|
101
|
+
if(!result?.fields) throw "No fields";
|
|
102
|
+
const fields = getDetailedFieldInfo.bind(this.dboBuilder)(result.fields as any);
|
|
103
|
+
packet = { type: "start", rows: batchRows, fields, ended, processId: processID };
|
|
104
|
+
} else {
|
|
105
|
+
packet = { type: "rows", rows: batchRows, ended };
|
|
106
|
+
}
|
|
107
|
+
socket.emit(channel, packet);
|
|
108
|
+
if(ended){
|
|
109
|
+
if(!result) throw "No result info";
|
|
110
|
+
watchSchemaFallback.bind(this.dboBuilder)({ queryWithoutRLS: query.query, command: result.command });
|
|
111
|
+
}
|
|
112
|
+
emittedPackets++;
|
|
69
113
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
} else if (!emittedPackets) {
|
|
89
|
-
if(!result?.fields) throw "No fields";
|
|
90
|
-
const fields = getDetailedFieldInfo.bind(this.dboBuilder)(result.fields as any);
|
|
91
|
-
packet = { type: "start", rows: batchRows, fields, ended, processId: processID };
|
|
92
|
-
} else {
|
|
93
|
-
packet = { type: "rows", rows: batchRows, ended };
|
|
114
|
+
const client = this.getConnection(err => {
|
|
115
|
+
socketQuery.onError(err);
|
|
116
|
+
client.end();
|
|
117
|
+
});
|
|
118
|
+
try {
|
|
119
|
+
await client.connect();
|
|
120
|
+
poolClient = client;
|
|
121
|
+
processID = (client as any).processID
|
|
122
|
+
const queryStream = new QueryStream(query.query, [], { batchSize: 1e6, highWaterMark: 1e6, rowMode: "array" });
|
|
123
|
+
stream = client.query(queryStream);
|
|
124
|
+
this.socketQueries[socketId]![id]!.client = poolClient;
|
|
125
|
+
this.socketQueries[socketId]![id]!.stream = stream;
|
|
126
|
+
stream.on('data', async (data) => {
|
|
127
|
+
batchRows.push(data);
|
|
128
|
+
if(options?.streamLimit) {
|
|
129
|
+
if(batchRows.length >= options.streamLimit){
|
|
130
|
+
emit("ended", stream);
|
|
131
|
+
}
|
|
94
132
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
watchSchemaFallback.bind(this.dboBuilder)({ queryWithoutRLS: query.query, command: result.command });
|
|
133
|
+
if (batchRows.length >= batchSize) {
|
|
134
|
+
emit("rows", stream);
|
|
135
|
+
batchRows = [];
|
|
99
136
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if(batchRows.length >= options.streamLimit){
|
|
118
|
-
emit("ended", undefined, stream);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if (batchRows.length >= batchSize) {
|
|
122
|
-
emit("rows", undefined, stream);
|
|
123
|
-
batchRows = [];
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
stream.on('error', error => {
|
|
127
|
-
emit("error", error, stream);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
stream.on('end', () => {
|
|
131
|
-
emit("ended", undefined, stream);
|
|
132
|
-
// release the client when the stream is finished
|
|
133
|
-
if(!options?.persistStreamConnection){
|
|
134
|
-
delete this.socketQueries[socketId]?.[id];
|
|
135
|
-
done();
|
|
136
|
-
}
|
|
137
|
-
})
|
|
138
|
-
});
|
|
139
|
-
} catch(err){
|
|
140
|
-
emit("error", err, undefined);
|
|
141
|
-
}
|
|
142
|
-
})();
|
|
137
|
+
});
|
|
138
|
+
stream.on('error', error => {
|
|
139
|
+
socketQuery.onError(error);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
stream.on('end', () => {
|
|
143
|
+
emit("ended", stream);
|
|
144
|
+
// release the client when the stream is finished AND connection is not persisted
|
|
145
|
+
if(!options?.persistStreamConnection){
|
|
146
|
+
delete this.socketQueries[socketId]?.[id];
|
|
147
|
+
client.end();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
} catch(err){
|
|
151
|
+
socketQuery.onError(err);
|
|
152
|
+
await client.end();
|
|
153
|
+
}
|
|
143
154
|
}
|
|
144
155
|
|
|
145
|
-
const stop = (
|
|
146
|
-
|
|
147
|
-
const { stream, poolClient } = this.socketQueries[socketId]?.[id] ?? {};
|
|
156
|
+
const stop = async (opts: { terminate?: boolean; } | undefined, cb: BasicCallback) => {
|
|
157
|
+
const { stream, client: poolClient } = this.socketQueries[socketId]?.[id] ?? {};
|
|
148
158
|
if(!stream || !poolClient) return;
|
|
149
|
-
this.
|
|
150
|
-
|
|
159
|
+
const client = this.getConnection(undefined);
|
|
160
|
+
try {
|
|
161
|
+
await client.connect();
|
|
151
162
|
if (!client) return cb(null, "No client");
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
});
|
|
163
|
-
});
|
|
163
|
+
const stopFunction = opts?.terminate? "pg_terminate_backend" : "pg_cancel_backend";
|
|
164
|
+
const rows = await client.query(`SELECT ${stopFunction}(pid), pid, state, query FROM pg_stat_activity WHERE pid = $1 AND query = $2`, [processID, query.query]);
|
|
165
|
+
socket.removeAllListeners(unsubChannel);
|
|
166
|
+
socket.removeAllListeners(channel);
|
|
167
|
+
cb({ processID, info: rows.rows[0] });
|
|
168
|
+
} catch (error){
|
|
169
|
+
cb(null, error);
|
|
170
|
+
} finally {
|
|
171
|
+
await client.end();
|
|
172
|
+
}
|
|
164
173
|
}
|
|
165
174
|
|
|
166
175
|
socket.removeAllListeners(unsubChannel);
|
|
@@ -181,7 +190,7 @@ export class QueryStreamer {
|
|
|
181
190
|
/** If not started in 5 seconds then assume this will never happen */
|
|
182
191
|
setTimeout(() => {
|
|
183
192
|
if(started) return;
|
|
184
|
-
stop(
|
|
193
|
+
stop({}, () => {
|
|
185
194
|
// empty
|
|
186
195
|
});
|
|
187
196
|
}, 5e3);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prostgles-server",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.129",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"pg-promise": "^11.5.4",
|
|
47
47
|
"pg-query-stream": "^4.5.3",
|
|
48
48
|
"prostgles-client": "^4.0.53",
|
|
49
|
-
"prostgles-types": "^4.0.
|
|
49
|
+
"prostgles-types": "^4.0.66"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/bluebird": "^3.5.36",
|
package/tests/client/index.ts
CHANGED
|
@@ -92,13 +92,17 @@ try {
|
|
|
92
92
|
onReady: async (db, methods, tableSchema, auth, isReconnect) => {
|
|
93
93
|
log(`TEST_NAME: ${TEST_NAME} Started`)
|
|
94
94
|
try {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
95
|
+
if(typeof window !== "undefined"){
|
|
96
|
+
const onLog = (...args: any[]) => {
|
|
97
|
+
socket.emit("log", args.map(v => typeof v === "object"? JSON.stringify(v) : v).join(" "));
|
|
98
|
+
}
|
|
99
|
+
window.onerror = function myErrorHandler(errorMsg, url, lineNumber) {
|
|
100
|
+
console.error("Error occured: " + errorMsg);
|
|
101
|
+
stopTest({ err: errorMsg });
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
console.log = onLog;
|
|
105
|
+
}
|
|
102
106
|
await test.onRun(db, methods, tableSchema, auth, isReconnect);
|
|
103
107
|
|
|
104
108
|
stopTest();
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@types/node": "^14.14.16",
|
|
13
|
-
"prostgles-client": "^4.0.
|
|
13
|
+
"prostgles-client": "^4.0.61",
|
|
14
14
|
"prostgles-types": "^4.0.51",
|
|
15
15
|
"socket.io-client": "^4.7.1"
|
|
16
16
|
},
|
|
@@ -75,11 +75,11 @@
|
|
|
75
75
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
76
76
|
},
|
|
77
77
|
"node_modules/prostgles-client": {
|
|
78
|
-
"version": "4.0.
|
|
79
|
-
"resolved": "https://registry.npmjs.org/prostgles-client/-/prostgles-client-4.0.
|
|
80
|
-
"integrity": "sha512-
|
|
78
|
+
"version": "4.0.61",
|
|
79
|
+
"resolved": "https://registry.npmjs.org/prostgles-client/-/prostgles-client-4.0.61.tgz",
|
|
80
|
+
"integrity": "sha512-AEgiO5erJnDde0o5hSd1ztKGRWPK2OVCKeVYCbEDGHYIcZzvBA1jRwxpreGaqENZDrWix5LJHyTTTZuLr+LhCw==",
|
|
81
81
|
"dependencies": {
|
|
82
|
-
"prostgles-types": "^4.0.
|
|
82
|
+
"prostgles-types": "^4.0.66"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
85
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
|
@@ -91,9 +91,9 @@
|
|
|
91
91
|
}
|
|
92
92
|
},
|
|
93
93
|
"node_modules/prostgles-types": {
|
|
94
|
-
"version": "4.0.
|
|
95
|
-
"resolved": "https://registry.npmjs.org/prostgles-types/-/prostgles-types-4.0.
|
|
96
|
-
"integrity": "sha512-
|
|
94
|
+
"version": "4.0.66",
|
|
95
|
+
"resolved": "https://registry.npmjs.org/prostgles-types/-/prostgles-types-4.0.66.tgz",
|
|
96
|
+
"integrity": "sha512-nDMoWmXmHPBFRK1KJaf7ttfy1VTiB072CtxZoY6LExPPKVCjnJ5PBafIEG1N6opa1WFaU0ez4WTm9Ykc4AOmAQ==",
|
|
97
97
|
"dependencies": {
|
|
98
98
|
"json-schema": "^0.4.0"
|
|
99
99
|
}
|
|
@@ -213,17 +213,17 @@
|
|
|
213
213
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
214
214
|
},
|
|
215
215
|
"prostgles-client": {
|
|
216
|
-
"version": "4.0.
|
|
217
|
-
"resolved": "https://registry.npmjs.org/prostgles-client/-/prostgles-client-4.0.
|
|
218
|
-
"integrity": "sha512-
|
|
216
|
+
"version": "4.0.61",
|
|
217
|
+
"resolved": "https://registry.npmjs.org/prostgles-client/-/prostgles-client-4.0.61.tgz",
|
|
218
|
+
"integrity": "sha512-AEgiO5erJnDde0o5hSd1ztKGRWPK2OVCKeVYCbEDGHYIcZzvBA1jRwxpreGaqENZDrWix5LJHyTTTZuLr+LhCw==",
|
|
219
219
|
"requires": {
|
|
220
|
-
"prostgles-types": "^4.0.
|
|
220
|
+
"prostgles-types": "^4.0.66"
|
|
221
221
|
}
|
|
222
222
|
},
|
|
223
223
|
"prostgles-types": {
|
|
224
|
-
"version": "4.0.
|
|
225
|
-
"resolved": "https://registry.npmjs.org/prostgles-types/-/prostgles-types-4.0.
|
|
226
|
-
"integrity": "sha512-
|
|
224
|
+
"version": "4.0.66",
|
|
225
|
+
"resolved": "https://registry.npmjs.org/prostgles-types/-/prostgles-types-4.0.66.tgz",
|
|
226
|
+
"integrity": "sha512-nDMoWmXmHPBFRK1KJaf7ttfy1VTiB072CtxZoY6LExPPKVCjnJ5PBafIEG1N6opa1WFaU0ez4WTm9Ykc4AOmAQ==",
|
|
227
227
|
"requires": {
|
|
228
228
|
"json-schema": "^0.4.0"
|
|
229
229
|
}
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"start": "tsc-watch --onSuccess \"node dist/client/index.js\"",
|
|
8
8
|
"dev": "NOSTOP=true npm start",
|
|
9
9
|
"build": "rm -rf ./node_modules/* && rm -rf ./dist/* && npm i && tsc",
|
|
10
|
-
"test": "node
|
|
10
|
+
"test": "node dist/client/index.js"
|
|
11
11
|
},
|
|
12
12
|
"author": "",
|
|
13
13
|
"license": "ISC",
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@types/node": "^14.14.16",
|
|
16
|
-
"prostgles-client": "^4.0.
|
|
16
|
+
"prostgles-client": "^4.0.61",
|
|
17
17
|
"prostgles-types": "^4.0.51",
|
|
18
18
|
"socket.io-client": "^4.7.1"
|
|
19
19
|
},
|
|
@@ -5,6 +5,57 @@ import { tryRun, tryRunP } from './isomorphic_queries';
|
|
|
5
5
|
import { reject } from 'bluebird';
|
|
6
6
|
|
|
7
7
|
export default async function client_only(db: DBHandlerClient, auth: Auth, log: (...args: any[]) => any, methods, tableSchema: DBSchemaTable[], token: string){
|
|
8
|
+
|
|
9
|
+
await tryRunP("SQL Stream stop kills the query", async (resolve, reject) => {
|
|
10
|
+
const query = "SELECT * FROM pg_sleep(5)"
|
|
11
|
+
const res = await db.sql!(query, {}, { returnType: "stream" });
|
|
12
|
+
const listener = async (packet: SocketSQLStreamPacket) => {
|
|
13
|
+
if(packet.type === "error"){
|
|
14
|
+
const queryState = await db.sql!("SELECT * FROM pg_stat_activity WHERE query = $1", [query], { returnType: "rows" });
|
|
15
|
+
assert.equal(queryState.length, 1);
|
|
16
|
+
assert.equal(queryState[0].state, "idle");
|
|
17
|
+
assert.equal(packet.error.message, "canceling statement due to user request");
|
|
18
|
+
resolve("ok");
|
|
19
|
+
} else {
|
|
20
|
+
assert.equal(packet.type, "start");
|
|
21
|
+
assert.equal(packet.ended, true);
|
|
22
|
+
assert.deepStrictEqual(packet.rows, []);
|
|
23
|
+
reject("ok");
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const startHandler = await res.start(listener);
|
|
27
|
+
setTimeout(() => {
|
|
28
|
+
startHandler.stop().catch(reject);
|
|
29
|
+
}, 1000);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
await tryRunP("SQL Stream stop with terminate kills the query", async (resolve, reject) => {
|
|
33
|
+
const totalRows = 5e6;
|
|
34
|
+
const query = `SELECT * FROM generate_series(1, ${totalRows})`;
|
|
35
|
+
const res = await db.sql!(query, {}, { returnType: "stream" });
|
|
36
|
+
const rowsReceived: any[] = [];
|
|
37
|
+
const listener = async (packet: SocketSQLStreamPacket) => {
|
|
38
|
+
if(packet.type === "error"){
|
|
39
|
+
const queryState = await db.sql!("SELECT * FROM pg_stat_activity WHERE query = $1", [query], { returnType: "rows" });
|
|
40
|
+
assert.equal(queryState.length, 0);
|
|
41
|
+
resolve("ok");
|
|
42
|
+
} else {
|
|
43
|
+
try {
|
|
44
|
+
rowsReceived.push(...packet.rows);
|
|
45
|
+
console.log(rowsReceived.length)
|
|
46
|
+
assert.equal(packet.ended, false);
|
|
47
|
+
assert.equal(rowsReceived.length < totalRows, true);
|
|
48
|
+
} catch(error){
|
|
49
|
+
reject(error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const startHandler = await res.start(listener);
|
|
54
|
+
setTimeout(() => {
|
|
55
|
+
startHandler.stop(true).catch(reject);
|
|
56
|
+
}, 22);
|
|
57
|
+
});
|
|
58
|
+
|
|
8
59
|
await Promise.all([1e3, 1e2].map(async (numberOfRows) => {
|
|
9
60
|
await tryRunP("SQL Stream", async (resolve) => {
|
|
10
61
|
const res = await db.sql!(`SELECT v.* FROM generate_series(1, ${numberOfRows}) v`, {}, { returnType: "stream" });
|
|
@@ -112,29 +163,6 @@ export default async function client_only(db: DBHandlerClient, auth: Auth, log:
|
|
|
112
163
|
await res.start(listener);
|
|
113
164
|
});
|
|
114
165
|
|
|
115
|
-
await tryRunP("SQL Stream stop kills the query", async (resolve, reject) => {
|
|
116
|
-
const query = "SELECT * FROM pg_sleep(5)"
|
|
117
|
-
const res = await db.sql!(query, {}, { returnType: "stream" });
|
|
118
|
-
const listener = async (packet: SocketSQLStreamPacket) => {
|
|
119
|
-
if(packet.type === "error"){
|
|
120
|
-
const queryState = await db.sql!("SELECT * FROM pg_stat_activity WHERE query = $1", [query], { returnType: "rows" });
|
|
121
|
-
assert.equal(queryState.length, 1);
|
|
122
|
-
assert.equal(queryState[0].state, "idle");
|
|
123
|
-
assert.equal(packet.error.message, "canceling statement due to user request");
|
|
124
|
-
resolve("ok");
|
|
125
|
-
} else {
|
|
126
|
-
assert.equal(packet.type, "start");
|
|
127
|
-
assert.equal(packet.ended, true);
|
|
128
|
-
assert.deepStrictEqual(packet.rows, []);
|
|
129
|
-
reject("ok");
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
const startHandler = await res.start(listener);
|
|
133
|
-
setTimeout(() => {
|
|
134
|
-
startHandler.stop().catch(reject);
|
|
135
|
-
}, 1000);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
166
|
// await tryRunP("SQL Stream ensure the connection is never released (same pg_backend_pid is the same for subsequent) queries when using persistConnectionId", async (resolve, reject) => {
|
|
139
167
|
// const res = await db.sql!("SELECT pg_backend_pid()", {}, { returnType: "stream", persistConnectionId: true });
|
|
140
168
|
// const listener = async (packet: SocketSQLStreamPacket) => {
|
package/tests/server/index.ts
CHANGED
|
@@ -115,6 +115,9 @@ prostgles<DBSchemaGenerated>({
|
|
|
115
115
|
if(clientTest){
|
|
116
116
|
log("Client connected -> console does not work. use log function. socket.id:", socket.id);
|
|
117
117
|
socket.emit("start-test", { server_id: Math.random() });
|
|
118
|
+
socket.on("log", async (data, cb) => {
|
|
119
|
+
console.log("Client log ", data);
|
|
120
|
+
});
|
|
118
121
|
socket.on("stop-test", async (err, cb) => {
|
|
119
122
|
cb();
|
|
120
123
|
console.log("Client test " + (!err? "successful" : "failed"));
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"../..": {
|
|
23
23
|
"name": "prostgles-server",
|
|
24
|
-
"version": "4.1.
|
|
24
|
+
"version": "4.1.128",
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@types/express": "^4.17.13",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"pg-promise": "^11.5.4",
|
|
36
36
|
"pg-query-stream": "^4.5.3",
|
|
37
37
|
"prostgles-client": "^4.0.53",
|
|
38
|
-
"prostgles-types": "^4.0.
|
|
38
|
+
"prostgles-types": "^4.0.66"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/bluebird": "^3.5.36",
|
|
@@ -1546,7 +1546,7 @@
|
|
|
1546
1546
|
"pg-promise": "^11.5.4",
|
|
1547
1547
|
"pg-query-stream": "^4.5.3",
|
|
1548
1548
|
"prostgles-client": "^4.0.53",
|
|
1549
|
-
"prostgles-types": "^4.0.
|
|
1549
|
+
"prostgles-types": "^4.0.66",
|
|
1550
1550
|
"typescript": "^5.2.2"
|
|
1551
1551
|
}
|
|
1552
1552
|
},
|