@nymphjs/driver-postgresql 1.0.0-beta.2 → 1.0.0-beta.21

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.
@@ -1,4 +1,9 @@
1
- import cp from 'child_process';
1
+ import {
2
+ Worker,
3
+ MessageChannel,
4
+ receiveMessageOnPort,
5
+ } from 'node:worker_threads';
6
+ import { resolve } from 'node:path';
2
7
  import { Pool, PoolClient, PoolConfig, QueryResult } from 'pg';
3
8
  import format from 'pg-format';
4
9
  import {
@@ -44,6 +49,8 @@ export default class PostgreSQLDriver extends NymphDriver {
44
49
  // @ts-ignore: this is assigned in connect(), which is called by the constructor.
45
50
  protected link: Pool;
46
51
  protected transaction: PostgreSQLDriverTransaction | null = null;
52
+ // @ts-ignore: this is assigned in connect(), which is called by the constructor.
53
+ protected worker: Worker;
47
54
 
48
55
  static escape(input: string) {
49
56
  return format.ident(input);
@@ -56,7 +63,8 @@ export default class PostgreSQLDriver extends NymphDriver {
56
63
  constructor(
57
64
  config: Partial<PostgreSQLDriverConfig>,
58
65
  link?: Pool,
59
- transaction?: PostgreSQLDriverTransaction
66
+ transaction?: PostgreSQLDriverTransaction,
67
+ worker?: Worker
60
68
  ) {
61
69
  super();
62
70
  this.config = { ...defaults, ...config };
@@ -77,11 +85,28 @@ export default class PostgreSQLDriver extends NymphDriver {
77
85
  if (transaction != null) {
78
86
  this.transaction = transaction;
79
87
  }
88
+ if (worker != null) {
89
+ this.worker = worker;
90
+ }
80
91
  if (link == null) {
81
92
  this.connect();
82
93
  }
83
94
  }
84
95
 
96
+ /**
97
+ * This is used internally by Nymph. Don't call it yourself.
98
+ *
99
+ * @returns A clone of this instance.
100
+ */
101
+ public clone() {
102
+ return new PostgreSQLDriver(
103
+ this.config,
104
+ this.link,
105
+ this.transaction ?? undefined,
106
+ this.worker ?? undefined
107
+ );
108
+ }
109
+
85
110
  private getConnection(): Promise<PostgreSQLDriverConnection> {
86
111
  if (this.transaction != null && this.transaction.connection != null) {
87
112
  return Promise.resolve(this.transaction.connection);
@@ -117,6 +142,7 @@ export default class PostgreSQLDriver extends NymphDriver {
117
142
  })
118
143
  );
119
144
  connection.done();
145
+ this.worker.postMessage('halt');
120
146
  }
121
147
  } catch (e: any) {
122
148
  this.connected = false;
@@ -126,6 +152,17 @@ export default class PostgreSQLDriver extends NymphDriver {
126
152
  if (!this.connected) {
127
153
  try {
128
154
  this.link = new Pool(this.postgresqlConfig);
155
+ const worker = new Worker(resolve(__dirname, 'runPostgresqlSync.js'), {
156
+ workerData: this.postgresqlConfig,
157
+ });
158
+ worker.on('message', (message) => {
159
+ if (message === 'halted') {
160
+ worker.terminate();
161
+ } else if (typeof message === 'object' && 'error' in message) {
162
+ console.error('Worker Thread Error', message.error);
163
+ }
164
+ });
165
+ this.worker = worker;
129
166
  this.connected = true;
130
167
  } catch (e: any) {
131
168
  if (
@@ -153,6 +190,7 @@ export default class PostgreSQLDriver extends NymphDriver {
153
190
  public async disconnect() {
154
191
  if (this.connected) {
155
192
  await new Promise((resolve) => this.link.end(() => resolve(0)));
193
+ this.worker.postMessage('halt');
156
194
  this.connected = false;
157
195
  }
158
196
  return this.connected;
@@ -572,34 +610,24 @@ export default class PostgreSQLDriver extends NymphDriver {
572
610
  );
573
611
  return this.querySync(
574
612
  () => {
575
- const output = cp.spawnSync(
576
- process.argv0,
577
- [__dirname + '/runPostgresqlSync.js'],
578
- {
579
- input: JSON.stringify({
580
- postgresqlConfig: this.postgresqlConfig,
581
- query: newQuery,
582
- params: newParams,
583
- }),
584
- timeout: 30000,
585
- maxBuffer: 100 * 1024 * 1024,
586
- encoding: 'utf8',
587
- windowsHide: true,
588
- }
613
+ const channel = new MessageChannel();
614
+
615
+ this.worker.postMessage(
616
+ { query: newQuery, params: newParams, port: channel.port2 },
617
+ [channel.port2]
589
618
  );
590
- try {
591
- return JSON.parse(output.stdout).rows;
592
- } catch (e) {
593
- // Do nothing.
594
- }
595
- if (output.status === 0) {
596
- throw new Error('Unknown parse error.');
619
+
620
+ let output = undefined;
621
+ while (!output) {
622
+ output = receiveMessageOnPort(channel.port1);
597
623
  }
598
- const err = JSON.parse(output.stderr);
599
- const e = new Error(err.name);
600
- for (const name in err) {
601
- (e as any)[name] = err[name];
624
+
625
+ if (output.message.error) {
626
+ throw new Error(output.message.error);
602
627
  }
628
+
629
+ const { results } = output.message;
630
+ return results.rows;
603
631
  },
604
632
  `${query} -- ${JSON.stringify(params)}`,
605
633
  etypes
@@ -693,35 +721,24 @@ export default class PostgreSQLDriver extends NymphDriver {
693
721
  );
694
722
  return this.querySync(
695
723
  () => {
696
- const output = cp.spawnSync(
697
- process.argv0,
698
- [__dirname + '/runPostgresqlSync.js'],
699
- {
700
- input: JSON.stringify({
701
- postgresqlConfig: this.postgresqlConfig,
702
- query: newQuery,
703
- params: newParams,
704
- }),
705
- timeout: 30000,
706
- maxBuffer: 100 * 1024 * 1024,
707
- encoding: 'utf8',
708
- windowsHide: true,
709
- }
724
+ const channel = new MessageChannel();
725
+
726
+ this.worker.postMessage(
727
+ { query: newQuery, params: newParams, port: channel.port2 },
728
+ [channel.port2]
710
729
  );
711
- try {
712
- const results = JSON.parse(output.stdout);
713
- return { rowCount: results.rowCount ?? 0 };
714
- } catch (e) {
715
- // Do nothing.
716
- }
717
- if (output.status === 0) {
718
- throw new Error('Unknown parse error.');
730
+
731
+ let output = undefined;
732
+ while (!output) {
733
+ output = receiveMessageOnPort(channel.port1);
719
734
  }
720
- const err = JSON.parse(output.stderr);
721
- const e = new Error(err.name);
722
- for (const name in err) {
723
- (e as any)[name] = err[name];
735
+
736
+ if (output.message.error) {
737
+ throw new Error(output.message.error);
724
738
  }
739
+
740
+ const { results } = output.message;
741
+ return { rowCount: results.rowCount ?? 0 };
725
742
  },
726
743
  `${query} -- ${JSON.stringify(params)}`,
727
744
  etypes
@@ -953,6 +970,7 @@ export default class PostgreSQLDriver extends NymphDriver {
953
970
  const fTable = `f${tableSuffix}`;
954
971
  const ieTable = `ie${tableSuffix}`;
955
972
  const countTable = `count${tableSuffix}`;
973
+ const sTable = `s${tableSuffix}`;
956
974
  const sort = options.sort ?? 'cdate';
957
975
  const queryParts = this.iterateSelectorsForQuery(
958
976
  formattedSelectors,
@@ -1748,18 +1766,42 @@ export default class PostgreSQLDriver extends NymphDriver {
1748
1766
  );
1749
1767
 
1750
1768
  let sortBy: string;
1769
+ let sortByInner: string;
1770
+ let sortJoin = '';
1771
+ let sortJoinInner = '';
1772
+ const order = options.reverse ? ' DESC' : '';
1751
1773
  switch (sort) {
1752
1774
  case 'mdate':
1753
- sortBy = '"mdate"';
1775
+ sortBy = `${eTable}."mdate"${order}`;
1776
+ sortByInner = `${ieTable}."mdate"${order}`;
1754
1777
  break;
1755
1778
  case 'cdate':
1779
+ sortBy = `${eTable}."cdate"${order}`;
1780
+ sortByInner = `${ieTable}."cdate"${order}`;
1781
+ break;
1756
1782
  default:
1757
- sortBy = '"cdate"';
1783
+ const name = `param${++count.i}`;
1784
+ sortJoin = `LEFT JOIN (
1785
+ SELECT "guid", "string", "number"
1786
+ FROM ${PostgreSQLDriver.escape(
1787
+ this.prefix + 'comparisons_' + etype
1788
+ )}
1789
+ WHERE "name"=@${name}
1790
+ ORDER BY "number"${order}, "string"${order}
1791
+ ) ${sTable} ON ${eTable}."guid"=${sTable}."guid"`;
1792
+ sortJoinInner = `LEFT JOIN (
1793
+ SELECT "guid", "string", "number"
1794
+ FROM ${PostgreSQLDriver.escape(
1795
+ this.prefix + 'comparisons_' + etype
1796
+ )}
1797
+ WHERE "name"=@${name}
1798
+ ORDER BY "number"${order}, "string"${order}
1799
+ ) ${sTable} ON ${ieTable}."guid"=${sTable}."guid"`;
1800
+ sortBy = `${sTable}."number"${order}, ${sTable}."string"${order}`;
1801
+ sortByInner = sortBy;
1802
+ params[name] = sort;
1758
1803
  break;
1759
1804
  }
1760
- if (options.reverse) {
1761
- sortBy += ' DESC';
1762
- }
1763
1805
 
1764
1806
  let query: string;
1765
1807
  if (queryParts.length) {
@@ -1804,8 +1846,9 @@ export default class PostgreSQLDriver extends NymphDriver {
1804
1846
  FROM ${PostgreSQLDriver.escape(
1805
1847
  `${this.prefix}entities_${etype}`
1806
1848
  )} ${ieTable}
1849
+ ${sortJoinInner}
1807
1850
  WHERE (${whereClause})
1808
- ORDER BY ${ieTable}.${sortBy}${limit}${offset}`;
1851
+ ORDER BY ${sortByInner}, ${ieTable}."guid"${limit}${offset}`;
1809
1852
  } else {
1810
1853
  query = `SELECT
1811
1854
  encode(${eTable}."guid", 'hex') AS "guid",
@@ -1825,15 +1868,17 @@ export default class PostgreSQLDriver extends NymphDriver {
1825
1868
  INNER JOIN ${PostgreSQLDriver.escape(
1826
1869
  `${this.prefix}comparisons_${etype}`
1827
1870
  )} ${cTable} ON ${dTable}."guid"=${cTable}."guid" AND ${dTable}."name"=${cTable}."name"
1871
+ ${sortJoin}
1828
1872
  INNER JOIN (
1829
1873
  SELECT ${ieTable}."guid"
1830
1874
  FROM ${PostgreSQLDriver.escape(
1831
1875
  `${this.prefix}entities_${etype}`
1832
1876
  )} ${ieTable}
1877
+ ${sortJoinInner}
1833
1878
  WHERE (${whereClause})
1834
- ORDER BY ${ieTable}.${sortBy}${limit}${offset}
1879
+ ORDER BY ${sortByInner}${limit}${offset}
1835
1880
  ) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
1836
- ORDER BY ${eTable}.${sortBy}`;
1881
+ ORDER BY ${sortBy}, ${eTable}."guid"`;
1837
1882
  }
1838
1883
  }
1839
1884
  } else {
@@ -1875,7 +1920,8 @@ export default class PostgreSQLDriver extends NymphDriver {
1875
1920
  FROM ${PostgreSQLDriver.escape(
1876
1921
  `${this.prefix}entities_${etype}`
1877
1922
  )} ${ieTable}
1878
- ORDER BY ${ieTable}.${sortBy}${limit}${offset}`;
1923
+ ${sortJoinInner}
1924
+ ORDER BY ${sortByInner}, ${ieTable}."guid"${limit}${offset}`;
1879
1925
  } else {
1880
1926
  if (limit || offset) {
1881
1927
  query = `SELECT
@@ -1896,14 +1942,16 @@ export default class PostgreSQLDriver extends NymphDriver {
1896
1942
  INNER JOIN ${PostgreSQLDriver.escape(
1897
1943
  `${this.prefix}comparisons_${etype}`
1898
1944
  )} ${cTable} ON ${dTable}."guid"=${cTable}."guid" AND ${dTable}."name"=${cTable}."name"
1945
+ ${sortJoin}
1899
1946
  INNER JOIN (
1900
1947
  SELECT ${ieTable}."guid"
1901
1948
  FROM ${PostgreSQLDriver.escape(
1902
1949
  `${this.prefix}entities_${etype}`
1903
1950
  )} ${ieTable}
1904
- ORDER BY ${ieTable}.${sortBy}${limit}${offset}
1951
+ ${sortJoinInner}
1952
+ ORDER BY ${sortByInner}${limit}${offset}
1905
1953
  ) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
1906
- ORDER BY ${eTable}.${sortBy}`;
1954
+ ORDER BY ${sortBy}, ${eTable}."guid"`;
1907
1955
  } else {
1908
1956
  query = `SELECT
1909
1957
  encode(${eTable}."guid", 'hex') AS "guid",
@@ -1923,7 +1971,8 @@ export default class PostgreSQLDriver extends NymphDriver {
1923
1971
  INNER JOIN ${PostgreSQLDriver.escape(
1924
1972
  `${this.prefix}comparisons_${etype}`
1925
1973
  )} ${cTable} ON ${dTable}."guid"=${cTable}."guid" AND ${dTable}."name"=${cTable}."name"
1926
- ORDER BY ${eTable}.${sortBy}`;
1974
+ ${sortJoin}
1975
+ ORDER BY ${sortBy}, ${eTable}."guid"`;
1927
1976
  }
1928
1977
  }
1929
1978
  }
@@ -2026,7 +2075,11 @@ export default class PostgreSQLDriver extends NymphDriver {
2026
2075
  );
2027
2076
 
2028
2077
  const result = await resultPromise;
2029
- return process();
2078
+ const value = process();
2079
+ if (value instanceof Error) {
2080
+ throw value;
2081
+ }
2082
+ return value;
2030
2083
  }
2031
2084
 
2032
2085
  protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(
@@ -2073,7 +2126,11 @@ export default class PostgreSQLDriver extends NymphDriver {
2073
2126
  : row.value,
2074
2127
  })
2075
2128
  );
2076
- return process();
2129
+ const value = process();
2130
+ if (value instanceof Error) {
2131
+ throw value;
2132
+ }
2133
+ return value;
2077
2134
  }
2078
2135
 
2079
2136
  public async getUID(name: string) {
@@ -2674,8 +2731,7 @@ export default class PostgreSQLDriver extends NymphDriver {
2674
2731
  }
2675
2732
 
2676
2733
  const nymph = this.nymph.clone();
2677
- nymph.driver = new PostgreSQLDriver(this.config, this.link, transaction);
2678
- nymph.driver.init(nymph);
2734
+ (nymph.driver as PostgreSQLDriver).transaction = transaction;
2679
2735
 
2680
2736
  return nymph;
2681
2737
  }
@@ -1,35 +1,41 @@
1
+ const { isMainThread, workerData, parentPort } = require('node:worker_threads');
1
2
  const pg = require('pg');
2
3
 
3
- let stdin = '';
4
-
5
- process.stdin.on('data', (data) => {
6
- if (data != null) {
7
- stdin += data.toString();
8
- }
9
- });
4
+ if (isMainThread) {
5
+ throw new Error("Don't load this file as main thread.");
6
+ }
10
7
 
11
- process.stdin.on('end', () => {
12
- run();
13
- });
8
+ try {
9
+ const postgresqlConfig = workerData;
10
+ const pool = new pg.Pool(postgresqlConfig);
14
11
 
15
- async function run() {
16
- try {
17
- const { postgresqlConfig, query, params } = JSON.parse(stdin);
18
- const pool = new pg.Pool(postgresqlConfig);
19
- const results = await new Promise((resolve, reject) =>
20
- pool.query(query, params).then(
21
- (results) => resolve(results),
22
- (error) => reject(error)
23
- )
24
- );
25
- process.stdout.end(JSON.stringify(results), 'utf8', () => {
12
+ parentPort.on('message', async (message) => {
13
+ if (message === 'halt') {
26
14
  pool.end(() => {
27
- process.exit(0);
15
+ parentPort.postMessage('halted');
28
16
  });
29
- });
30
- } catch (e) {
31
- process.stderr.end(JSON.stringify(e), 'utf8', () => {
32
- process.exit(1);
33
- });
34
- }
17
+ } else {
18
+ const { query, params, port } = message;
19
+
20
+ try {
21
+ const results = await new Promise((resolve, reject) =>
22
+ pool.query(query, params).then(
23
+ (results) => resolve(results),
24
+ (error) => reject(error)
25
+ )
26
+ );
27
+
28
+ port.postMessage({
29
+ results: {
30
+ rows: results.rows,
31
+ rowCount: results.rowCount,
32
+ },
33
+ });
34
+ } catch (e) {
35
+ port.postMessage({ error: e.message });
36
+ }
37
+ }
38
+ });
39
+ } catch (e) {
40
+ parentPort.postMessage({ error: e.message });
35
41
  }
@@ -1,4 +1,13 @@
1
1
  const cp = require('child_process');
2
+ const {
3
+ Worker,
4
+ isMainThread,
5
+ workerData,
6
+ parentPort,
7
+ MessageChannel,
8
+ receiveMessageOnPort,
9
+ } = require('node:worker_threads');
10
+ const pg = require('pg');
2
11
 
3
12
  const postgresqlConfig = {
4
13
  host: 'localhost',
@@ -38,22 +47,73 @@ const params = [['Hello, world.']];
38
47
  // const query = 'SELECT relname FROM pg_stat_user_tables ORDER BY relname;';
39
48
  // const params = [];
40
49
 
41
- const output = cp.spawnSync(
42
- process.argv0,
43
- [__dirname + '/runPostgresqlSync.js'],
44
- {
45
- input: JSON.stringify({ postgresqlConfig, query, params }),
46
- timeout: 30000,
47
- maxBuffer: 100 * 1024 * 1024,
48
- encoding: 'utf8',
49
- windowsHide: true,
50
+ const methodOne = () => {
51
+ // Doesn't work anymore since runPostgresqlSync is updated.
52
+ const output = cp.spawnSync(
53
+ process.argv0,
54
+ [__dirname + '/runPostgresqlSync.js'],
55
+ {
56
+ input: JSON.stringify({ postgresqlConfig, query, params }),
57
+ timeout: 30000,
58
+ maxBuffer: 100 * 1024 * 1024,
59
+ encoding: 'utf8',
60
+ windowsHide: true,
61
+ }
62
+ );
63
+ try {
64
+ const result = JSON.parse(output.stdout);
65
+ console.log('Server reply: ', result);
66
+ } catch (e) {
67
+ // Do nothing.
50
68
  }
51
- );
52
- try {
53
- const result = JSON.parse(output.stdout);
54
- console.log('Server reply: ', result);
55
- } catch (e) {
56
- // Do nothing.
57
- }
58
- const err = output.status === 0 ? null : JSON.parse(output.stderr);
59
- if (err) console.error(err);
69
+ const err = output.status === 0 ? null : JSON.parse(output.stderr);
70
+ if (err) console.error(err);
71
+ };
72
+
73
+ const methodTwo = () => {
74
+ if (isMainThread) {
75
+ const worker = new Worker(__filename, { workerData: postgresqlConfig });
76
+ const channel = new MessageChannel();
77
+
78
+ worker.postMessage({ query, params, port: channel.port2 }, [channel.port2]);
79
+
80
+ let result = undefined;
81
+ while (!result) {
82
+ result = receiveMessageOnPort(channel.port1);
83
+ }
84
+
85
+ if (result.message.error) {
86
+ throw new Error(result.message.error);
87
+ }
88
+
89
+ console.log(JSON.stringify(result, null, 2));
90
+ } else {
91
+ const postgresqlConfig = workerData;
92
+ const pool = new pg.Pool(postgresqlConfig);
93
+ parentPort.on('message', async ({ query, params, port }) => {
94
+ try {
95
+ const results = await new Promise((resolve, reject) =>
96
+ pool.query(query, params).then(
97
+ (results) => resolve(results),
98
+ (error) => reject(error)
99
+ )
100
+ );
101
+
102
+ port.postMessage({
103
+ results: {
104
+ rows: results.rows,
105
+ rowCount: results.rowCount,
106
+ },
107
+ });
108
+
109
+ pool.end(() => {
110
+ process.exit(0);
111
+ });
112
+ } catch (e) {
113
+ port.postMessage({ error: e.message });
114
+ }
115
+ });
116
+ }
117
+ };
118
+
119
+ methodTwo();