@powersync/service-module-postgres 0.0.0-dev-20240918092408
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/CHANGELOG.md +18 -0
- package/LICENSE +67 -0
- package/README.md +3 -0
- package/dist/api/PostgresRouteAPIAdapter.d.ts +22 -0
- package/dist/api/PostgresRouteAPIAdapter.js +273 -0
- package/dist/api/PostgresRouteAPIAdapter.js.map +1 -0
- package/dist/auth/SupabaseKeyCollector.d.ts +22 -0
- package/dist/auth/SupabaseKeyCollector.js +64 -0
- package/dist/auth/SupabaseKeyCollector.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/module/PostgresModule.d.ts +14 -0
- package/dist/module/PostgresModule.js +108 -0
- package/dist/module/PostgresModule.js.map +1 -0
- package/dist/replication/ConnectionManagerFactory.d.ts +10 -0
- package/dist/replication/ConnectionManagerFactory.js +21 -0
- package/dist/replication/ConnectionManagerFactory.js.map +1 -0
- package/dist/replication/PgManager.d.ts +25 -0
- package/dist/replication/PgManager.js +60 -0
- package/dist/replication/PgManager.js.map +1 -0
- package/dist/replication/PgRelation.d.ts +6 -0
- package/dist/replication/PgRelation.js +27 -0
- package/dist/replication/PgRelation.js.map +1 -0
- package/dist/replication/PostgresErrorRateLimiter.d.ts +11 -0
- package/dist/replication/PostgresErrorRateLimiter.js +43 -0
- package/dist/replication/PostgresErrorRateLimiter.js.map +1 -0
- package/dist/replication/WalStream.d.ts +53 -0
- package/dist/replication/WalStream.js +536 -0
- package/dist/replication/WalStream.js.map +1 -0
- package/dist/replication/WalStreamReplicationJob.d.ts +27 -0
- package/dist/replication/WalStreamReplicationJob.js +131 -0
- package/dist/replication/WalStreamReplicationJob.js.map +1 -0
- package/dist/replication/WalStreamReplicator.d.ts +13 -0
- package/dist/replication/WalStreamReplicator.js +36 -0
- package/dist/replication/WalStreamReplicator.js.map +1 -0
- package/dist/replication/replication-index.d.ts +5 -0
- package/dist/replication/replication-index.js +6 -0
- package/dist/replication/replication-index.js.map +1 -0
- package/dist/replication/replication-utils.d.ts +32 -0
- package/dist/replication/replication-utils.js +272 -0
- package/dist/replication/replication-utils.js.map +1 -0
- package/dist/types/types.d.ts +76 -0
- package/dist/types/types.js +110 -0
- package/dist/types/types.js.map +1 -0
- package/dist/utils/migration_lib.d.ts +11 -0
- package/dist/utils/migration_lib.js +64 -0
- package/dist/utils/migration_lib.js.map +1 -0
- package/dist/utils/pgwire_utils.d.ts +16 -0
- package/dist/utils/pgwire_utils.js +70 -0
- package/dist/utils/pgwire_utils.js.map +1 -0
- package/dist/utils/populate_test_data.d.ts +8 -0
- package/dist/utils/populate_test_data.js +65 -0
- package/dist/utils/populate_test_data.js.map +1 -0
- package/package.json +49 -0
- package/src/api/PostgresRouteAPIAdapter.ts +307 -0
- package/src/auth/SupabaseKeyCollector.ts +70 -0
- package/src/index.ts +5 -0
- package/src/module/PostgresModule.ts +122 -0
- package/src/replication/ConnectionManagerFactory.ts +28 -0
- package/src/replication/PgManager.ts +70 -0
- package/src/replication/PgRelation.ts +31 -0
- package/src/replication/PostgresErrorRateLimiter.ts +44 -0
- package/src/replication/WalStream.ts +639 -0
- package/src/replication/WalStreamReplicationJob.ts +142 -0
- package/src/replication/WalStreamReplicator.ts +45 -0
- package/src/replication/replication-index.ts +5 -0
- package/src/replication/replication-utils.ts +329 -0
- package/src/types/types.ts +159 -0
- package/src/utils/migration_lib.ts +79 -0
- package/src/utils/pgwire_utils.ts +73 -0
- package/src/utils/populate_test_data.ts +77 -0
- package/test/src/__snapshots__/pg_test.test.ts.snap +256 -0
- package/test/src/env.ts +7 -0
- package/test/src/large_batch.test.ts +195 -0
- package/test/src/pg_test.test.ts +450 -0
- package/test/src/schema_changes.test.ts +543 -0
- package/test/src/setup.ts +7 -0
- package/test/src/slow_tests.test.ts +335 -0
- package/test/src/util.ts +105 -0
- package/test/src/validation.test.ts +64 -0
- package/test/src/wal_stream.test.ts +319 -0
- package/test/src/wal_stream_utils.ts +121 -0
- package/test/tsconfig.json +28 -0
- package/tsconfig.json +31 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +9 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# @powersync/service-module-postgres
|
|
2
|
+
|
|
3
|
+
## 0.0.0-dev-20240918092408
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d51921f: - Introduced modules to the powersync service architecture
|
|
8
|
+
- Core functionality has been moved to "engine" classes. Modules can register additional functionality with these engines.
|
|
9
|
+
- The sync API functionality used by the routes has been abstracted to an interface. API routes are now managed by the RouterEngine.
|
|
10
|
+
- Replication is managed by the ReplicationEngine and new replication data sources can be registered to the engine by modules.
|
|
11
|
+
- Refactored existing Postgres replication as a module.
|
|
12
|
+
- Removed Postgres specific code from the core service packages.
|
|
13
|
+
- Updated dependencies [d51921f]
|
|
14
|
+
- @powersync/service-core@0.0.0-dev-20240918092408
|
|
15
|
+
- @powersync/service-sync-rules@0.0.0-dev-20240918092408
|
|
16
|
+
- @powersync/lib-services-framework@0.0.0-dev-20240918092408
|
|
17
|
+
- @powersync/service-jpgwire@0.0.0-dev-20240918092408
|
|
18
|
+
- @powersync/service-types@0.0.0-dev-20240918092408
|
package/LICENSE
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Functional Source License, Version 1.1, Apache 2.0 Future License
|
|
2
|
+
|
|
3
|
+
## Abbreviation
|
|
4
|
+
|
|
5
|
+
FSL-1.1-Apache-2.0
|
|
6
|
+
|
|
7
|
+
## Notice
|
|
8
|
+
|
|
9
|
+
Copyright 2023-2024 Journey Mobile, Inc.
|
|
10
|
+
|
|
11
|
+
## Terms and Conditions
|
|
12
|
+
|
|
13
|
+
### Licensor ("We")
|
|
14
|
+
|
|
15
|
+
The party offering the Software under these Terms and Conditions.
|
|
16
|
+
|
|
17
|
+
### The Software
|
|
18
|
+
|
|
19
|
+
The "Software" is each version of the software that we make available under these Terms and Conditions, as indicated by our inclusion of these Terms and Conditions with the Software.
|
|
20
|
+
|
|
21
|
+
### License Grant
|
|
22
|
+
|
|
23
|
+
Subject to your compliance with this License Grant and the Patents, Redistribution and Trademark clauses below, we hereby grant you the right to use, copy, modify, create derivative works, publicly perform, publicly display and redistribute the Software for any Permitted Purpose identified below.
|
|
24
|
+
|
|
25
|
+
### Permitted Purpose
|
|
26
|
+
|
|
27
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use means making the Software available to others in a commercial product or service that:
|
|
28
|
+
|
|
29
|
+
1. substitutes for the Software;
|
|
30
|
+
2. substitutes for any other product or service we offer using the Software that exists as of the date we make the Software available; or
|
|
31
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
32
|
+
|
|
33
|
+
Permitted Purposes specifically include using the Software:
|
|
34
|
+
|
|
35
|
+
1. for your internal use and access;
|
|
36
|
+
2. for non-commercial education;
|
|
37
|
+
3. for non-commercial research; and
|
|
38
|
+
4. in connection with professional services that you provide to a licensee using the Software in accordance with these Terms and Conditions.
|
|
39
|
+
|
|
40
|
+
### Patents
|
|
41
|
+
|
|
42
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our patents, the license grant above includes a license under our patents. If you make a claim against any party that the Software infringes or contributes to the infringement of any patent, then your patent license to the Software ends immediately.
|
|
43
|
+
|
|
44
|
+
### Redistribution
|
|
45
|
+
|
|
46
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of the Software.
|
|
47
|
+
If you redistribute any copies, modifications or derivatives of the Software, you must include a copy of or a link to these Terms and Conditions and not remove any copyright notices provided in or with the Software.
|
|
48
|
+
|
|
49
|
+
### Disclaimer
|
|
50
|
+
|
|
51
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
52
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
53
|
+
|
|
54
|
+
### Trademarks
|
|
55
|
+
|
|
56
|
+
Except for displaying the License Details and identifying us as the origin of the Software, you have no right under these Terms and Conditions to use our trademarks, trade names, service marks or product names.
|
|
57
|
+
|
|
58
|
+
## Grant of Future License
|
|
59
|
+
|
|
60
|
+
We hereby irrevocably grant you an additional license to use the Software under the Apache License, Version 2.0 that is effective on the second anniversary of the date we make the Software available. On or after that date, you may use the Software under the Apache License, Version 2.0, in which case the following will apply:
|
|
61
|
+
|
|
62
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
|
|
63
|
+
You may obtain a copy of the License at
|
|
64
|
+
|
|
65
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
66
|
+
|
|
67
|
+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { api, ParseSyncRulesOptions } from '@powersync/service-core';
|
|
2
|
+
import * as pgwire from '@powersync/service-jpgwire';
|
|
3
|
+
import * as sync_rules from '@powersync/service-sync-rules';
|
|
4
|
+
import * as service_types from '@powersync/service-types';
|
|
5
|
+
import * as types from '../types/types.js';
|
|
6
|
+
export declare class PostgresRouteAPIAdapter implements api.RouteAPI {
|
|
7
|
+
protected config: types.ResolvedConnectionConfig;
|
|
8
|
+
protected pool: pgwire.PgClient;
|
|
9
|
+
connectionTag: string;
|
|
10
|
+
publicationName: string;
|
|
11
|
+
constructor(config: types.ResolvedConnectionConfig);
|
|
12
|
+
getParseSyncRulesOptions(): ParseSyncRulesOptions;
|
|
13
|
+
shutdown(): Promise<void>;
|
|
14
|
+
getSourceConfig(): Promise<service_types.configFile.DataSourceConfig>;
|
|
15
|
+
getConnectionStatus(): Promise<service_types.ConnectionStatusV2>;
|
|
16
|
+
executeQuery(query: string, params: any[]): Promise<service_types.internal_routes.ExecuteSqlResponse>;
|
|
17
|
+
getDebugTablesInfo(tablePatterns: sync_rules.TablePattern[], sqlSyncRules: sync_rules.SqlSyncRules): Promise<api.PatternResult[]>;
|
|
18
|
+
protected getDebugTableInfo(tablePattern: sync_rules.TablePattern, name: string, relationId: number | null, syncRules: sync_rules.SqlSyncRules): Promise<service_types.TableInfo>;
|
|
19
|
+
getReplicationLag(syncRulesId: string): Promise<number>;
|
|
20
|
+
getReplicationHead(): Promise<string>;
|
|
21
|
+
getConnectionSchema(): Promise<service_types.DatabaseSchema[]>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import * as pgwire from '@powersync/service-jpgwire';
|
|
2
|
+
import * as sync_rules from '@powersync/service-sync-rules';
|
|
3
|
+
import * as service_types from '@powersync/service-types';
|
|
4
|
+
import * as replication_utils from '../replication/replication-utils.js';
|
|
5
|
+
import * as types from '../types/types.js';
|
|
6
|
+
import * as pg_utils from '../utils/pgwire_utils.js';
|
|
7
|
+
import { getDebugTableInfo } from '../replication/replication-utils.js';
|
|
8
|
+
import { PUBLICATION_NAME } from '../replication/WalStream.js';
|
|
9
|
+
export class PostgresRouteAPIAdapter {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
// TODO this should probably be configurable one day
|
|
13
|
+
this.publicationName = PUBLICATION_NAME;
|
|
14
|
+
this.pool = pgwire.connectPgWirePool(config, {
|
|
15
|
+
idleTimeout: 30000
|
|
16
|
+
});
|
|
17
|
+
this.connectionTag = config.tag ?? sync_rules.DEFAULT_TAG;
|
|
18
|
+
}
|
|
19
|
+
getParseSyncRulesOptions() {
|
|
20
|
+
return {
|
|
21
|
+
defaultSchema: 'public'
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async shutdown() {
|
|
25
|
+
await this.pool.end();
|
|
26
|
+
}
|
|
27
|
+
async getSourceConfig() {
|
|
28
|
+
return this.config;
|
|
29
|
+
}
|
|
30
|
+
async getConnectionStatus() {
|
|
31
|
+
const base = {
|
|
32
|
+
id: this.config.id,
|
|
33
|
+
uri: types.baseUri(this.config)
|
|
34
|
+
};
|
|
35
|
+
try {
|
|
36
|
+
await pg_utils.retriedQuery(this.pool, `SELECT 'PowerSync connection test'`);
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
return {
|
|
40
|
+
...base,
|
|
41
|
+
connected: false,
|
|
42
|
+
errors: [{ level: 'fatal', message: e.message }]
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
await replication_utils.checkSourceConfiguration(this.pool, this.publicationName);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
return {
|
|
50
|
+
...base,
|
|
51
|
+
connected: true,
|
|
52
|
+
errors: [{ level: 'fatal', message: e.message }]
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
...base,
|
|
57
|
+
connected: true,
|
|
58
|
+
errors: []
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async executeQuery(query, params) {
|
|
62
|
+
if (!this.config.debug_api) {
|
|
63
|
+
return service_types.internal_routes.ExecuteSqlResponse.encode({
|
|
64
|
+
results: {
|
|
65
|
+
columns: [],
|
|
66
|
+
rows: []
|
|
67
|
+
},
|
|
68
|
+
success: false,
|
|
69
|
+
error: 'SQL querying is not enabled'
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const result = await this.pool.query({
|
|
74
|
+
statement: query,
|
|
75
|
+
params: params.map(pg_utils.autoParameter)
|
|
76
|
+
});
|
|
77
|
+
return service_types.internal_routes.ExecuteSqlResponse.encode({
|
|
78
|
+
success: true,
|
|
79
|
+
results: {
|
|
80
|
+
columns: result.columns.map((c) => c.name),
|
|
81
|
+
rows: result.rows.map((row) => {
|
|
82
|
+
return row.map((value) => {
|
|
83
|
+
const sqlValue = sync_rules.toSyncRulesValue(value);
|
|
84
|
+
if (typeof sqlValue == 'bigint') {
|
|
85
|
+
return Number(value);
|
|
86
|
+
}
|
|
87
|
+
else if (sync_rules.isJsonValue(sqlValue)) {
|
|
88
|
+
return sqlValue;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
return service_types.internal_routes.ExecuteSqlResponse.encode({
|
|
100
|
+
results: {
|
|
101
|
+
columns: [],
|
|
102
|
+
rows: []
|
|
103
|
+
},
|
|
104
|
+
success: false,
|
|
105
|
+
error: e.message
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async getDebugTablesInfo(tablePatterns, sqlSyncRules) {
|
|
110
|
+
let result = [];
|
|
111
|
+
for (let tablePattern of tablePatterns) {
|
|
112
|
+
const schema = tablePattern.schema;
|
|
113
|
+
let patternResult = {
|
|
114
|
+
schema: schema,
|
|
115
|
+
pattern: tablePattern.tablePattern,
|
|
116
|
+
wildcard: tablePattern.isWildcard
|
|
117
|
+
};
|
|
118
|
+
result.push(patternResult);
|
|
119
|
+
if (tablePattern.isWildcard) {
|
|
120
|
+
patternResult.tables = [];
|
|
121
|
+
const prefix = tablePattern.tablePrefix;
|
|
122
|
+
const results = await pg_utils.retriedQuery(this.pool, {
|
|
123
|
+
statement: `SELECT c.oid AS relid, c.relname AS table_name
|
|
124
|
+
FROM pg_class c
|
|
125
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
126
|
+
WHERE n.nspname = $1
|
|
127
|
+
AND c.relkind = 'r'
|
|
128
|
+
AND c.relname LIKE $2`,
|
|
129
|
+
params: [
|
|
130
|
+
{ type: 'varchar', value: schema },
|
|
131
|
+
{ type: 'varchar', value: tablePattern.tablePattern }
|
|
132
|
+
]
|
|
133
|
+
});
|
|
134
|
+
for (let row of pgwire.pgwireRows(results)) {
|
|
135
|
+
const name = row.table_name;
|
|
136
|
+
const relationId = row.relid;
|
|
137
|
+
if (!name.startsWith(prefix)) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const details = await this.getDebugTableInfo(tablePattern, name, relationId, sqlSyncRules);
|
|
141
|
+
patternResult.tables.push(details);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
const results = await pg_utils.retriedQuery(this.pool, {
|
|
146
|
+
statement: `SELECT c.oid AS relid, c.relname AS table_name
|
|
147
|
+
FROM pg_class c
|
|
148
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
149
|
+
WHERE n.nspname = $1
|
|
150
|
+
AND c.relkind = 'r'
|
|
151
|
+
AND c.relname = $2`,
|
|
152
|
+
params: [
|
|
153
|
+
{ type: 'varchar', value: schema },
|
|
154
|
+
{ type: 'varchar', value: tablePattern.tablePattern }
|
|
155
|
+
]
|
|
156
|
+
});
|
|
157
|
+
if (results.rows.length == 0) {
|
|
158
|
+
// Table not found
|
|
159
|
+
patternResult.table = await this.getDebugTableInfo(tablePattern, tablePattern.name, null, sqlSyncRules);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
const row = pgwire.pgwireRows(results)[0];
|
|
163
|
+
const name = row.table_name;
|
|
164
|
+
const relationId = row.relid;
|
|
165
|
+
patternResult.table = await this.getDebugTableInfo(tablePattern, name, relationId, sqlSyncRules);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
async getDebugTableInfo(tablePattern, name, relationId, syncRules) {
|
|
172
|
+
return getDebugTableInfo({
|
|
173
|
+
db: this.pool,
|
|
174
|
+
name: name,
|
|
175
|
+
publicationName: this.publicationName,
|
|
176
|
+
connectionTag: this.connectionTag,
|
|
177
|
+
tablePattern: tablePattern,
|
|
178
|
+
relationId: relationId,
|
|
179
|
+
syncRules: syncRules
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
async getReplicationLag(syncRulesId) {
|
|
183
|
+
const results = await pg_utils.retriedQuery(this.pool, {
|
|
184
|
+
statement: `SELECT
|
|
185
|
+
slot_name,
|
|
186
|
+
confirmed_flush_lsn,
|
|
187
|
+
pg_current_wal_lsn(),
|
|
188
|
+
(pg_current_wal_lsn() - confirmed_flush_lsn) AS lsn_distance
|
|
189
|
+
FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`,
|
|
190
|
+
params: [{ type: 'varchar', value: syncRulesId }]
|
|
191
|
+
});
|
|
192
|
+
const [row] = pgwire.pgwireRows(results);
|
|
193
|
+
if (row) {
|
|
194
|
+
return Number(row.lsn_distance);
|
|
195
|
+
}
|
|
196
|
+
throw new Error(`Could not determine replication lag for slot ${syncRulesId}`);
|
|
197
|
+
}
|
|
198
|
+
async getReplicationHead() {
|
|
199
|
+
const [{ lsn }] = pgwire.pgwireRows(await pg_utils.retriedQuery(this.pool, `SELECT pg_logical_emit_message(false, 'powersync', 'ping') as lsn`));
|
|
200
|
+
return String(lsn);
|
|
201
|
+
}
|
|
202
|
+
async getConnectionSchema() {
|
|
203
|
+
var _a;
|
|
204
|
+
// https://github.com/Borvik/vscode-postgres/blob/88ec5ed061a0c9bced6c5d4ec122d0759c3f3247/src/language/server.ts
|
|
205
|
+
const results = await pg_utils.retriedQuery(this.pool, `SELECT
|
|
206
|
+
tbl.schemaname,
|
|
207
|
+
tbl.tablename,
|
|
208
|
+
tbl.quoted_name,
|
|
209
|
+
json_agg(a ORDER BY attnum) as columns
|
|
210
|
+
FROM
|
|
211
|
+
(
|
|
212
|
+
SELECT
|
|
213
|
+
n.nspname as schemaname,
|
|
214
|
+
c.relname as tablename,
|
|
215
|
+
(quote_ident(n.nspname) || '.' || quote_ident(c.relname)) as quoted_name
|
|
216
|
+
FROM
|
|
217
|
+
pg_catalog.pg_class c
|
|
218
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
219
|
+
WHERE
|
|
220
|
+
c.relkind = 'r'
|
|
221
|
+
AND n.nspname not in ('information_schema', 'pg_catalog', 'pg_toast')
|
|
222
|
+
AND n.nspname not like 'pg_temp_%'
|
|
223
|
+
AND n.nspname not like 'pg_toast_temp_%'
|
|
224
|
+
AND c.relnatts > 0
|
|
225
|
+
AND has_schema_privilege(n.oid, 'USAGE') = true
|
|
226
|
+
AND has_table_privilege(quote_ident(n.nspname) || '.' || quote_ident(c.relname), 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER') = true
|
|
227
|
+
) as tbl
|
|
228
|
+
LEFT JOIN (
|
|
229
|
+
SELECT
|
|
230
|
+
attrelid,
|
|
231
|
+
attname,
|
|
232
|
+
format_type(atttypid, atttypmod) as data_type,
|
|
233
|
+
(SELECT typname FROM pg_catalog.pg_type WHERE oid = atttypid) as pg_type,
|
|
234
|
+
attnum,
|
|
235
|
+
attisdropped
|
|
236
|
+
FROM
|
|
237
|
+
pg_attribute
|
|
238
|
+
) as a ON (
|
|
239
|
+
a.attrelid = tbl.quoted_name::regclass
|
|
240
|
+
AND a.attnum > 0
|
|
241
|
+
AND NOT a.attisdropped
|
|
242
|
+
AND has_column_privilege(tbl.quoted_name, a.attname, 'SELECT, INSERT, UPDATE, REFERENCES')
|
|
243
|
+
)
|
|
244
|
+
GROUP BY schemaname, tablename, quoted_name`);
|
|
245
|
+
const rows = pgwire.pgwireRows(results);
|
|
246
|
+
let schemas = {};
|
|
247
|
+
for (let row of rows) {
|
|
248
|
+
const schema = (schemas[_a = row.schemaname] ?? (schemas[_a] = {
|
|
249
|
+
name: row.schemaname,
|
|
250
|
+
tables: []
|
|
251
|
+
}));
|
|
252
|
+
const table = {
|
|
253
|
+
name: row.tablename,
|
|
254
|
+
columns: []
|
|
255
|
+
};
|
|
256
|
+
schema.tables.push(table);
|
|
257
|
+
const columnInfo = JSON.parse(row.columns);
|
|
258
|
+
for (let column of columnInfo) {
|
|
259
|
+
let pg_type = column.pg_type;
|
|
260
|
+
if (pg_type.startsWith('_')) {
|
|
261
|
+
pg_type = `${pg_type.substring(1)}[]`;
|
|
262
|
+
}
|
|
263
|
+
table.columns.push({
|
|
264
|
+
name: column.attname,
|
|
265
|
+
type: column.data_type,
|
|
266
|
+
pg_type: pg_type
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return Object.values(schemas);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=PostgresRouteAPIAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgresRouteAPIAdapter.js","sourceRoot":"","sources":["../../src/api/PostgresRouteAPIAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { auth } from '@powersync/service-core';
|
|
2
|
+
import * as jose from 'jose';
|
|
3
|
+
import * as types from '../types/types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Fetches key from the Supabase database.
|
|
6
|
+
*
|
|
7
|
+
* Unfortunately, despite the JWTs containing a kid, we have no way to lookup that kid
|
|
8
|
+
* before receiving a valid token.
|
|
9
|
+
*/
|
|
10
|
+
export declare class SupabaseKeyCollector implements auth.KeyCollector {
|
|
11
|
+
private pool;
|
|
12
|
+
private keyOptions;
|
|
13
|
+
constructor(connectionConfig: types.ResolvedConnectionConfig);
|
|
14
|
+
shutdown(): Promise<void>;
|
|
15
|
+
getKeys(): Promise<{
|
|
16
|
+
keys: never[];
|
|
17
|
+
errors: jose.errors.JWKSNoMatchingKey[];
|
|
18
|
+
} | {
|
|
19
|
+
keys: auth.KeySpec[];
|
|
20
|
+
errors: never[];
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { auth } from '@powersync/service-core';
|
|
2
|
+
import * as pgwire from '@powersync/service-jpgwire';
|
|
3
|
+
import * as jose from 'jose';
|
|
4
|
+
import * as pgwire_utils from '../utils/pgwire_utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* Fetches key from the Supabase database.
|
|
7
|
+
*
|
|
8
|
+
* Unfortunately, despite the JWTs containing a kid, we have no way to lookup that kid
|
|
9
|
+
* before receiving a valid token.
|
|
10
|
+
*/
|
|
11
|
+
export class SupabaseKeyCollector {
|
|
12
|
+
constructor(connectionConfig) {
|
|
13
|
+
this.keyOptions = {
|
|
14
|
+
requiresAudience: ['authenticated'],
|
|
15
|
+
maxLifetimeSeconds: 86400 * 7 + 1200 // 1 week + 20 minutes margin
|
|
16
|
+
};
|
|
17
|
+
this.pool = pgwire.connectPgWirePool(connectionConfig, {
|
|
18
|
+
// To avoid overloading the source database with open connections,
|
|
19
|
+
// limit to a single connection, and close the connection shortly
|
|
20
|
+
// after using it.
|
|
21
|
+
idleTimeout: 5000,
|
|
22
|
+
maxSize: 1
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
shutdown() {
|
|
26
|
+
return this.pool.end();
|
|
27
|
+
}
|
|
28
|
+
async getKeys() {
|
|
29
|
+
let row;
|
|
30
|
+
try {
|
|
31
|
+
const rows = pgwire.pgwireRows(await pgwire_utils.retriedQuery(this.pool, `SELECT current_setting('app.settings.jwt_secret') as jwt_secret`));
|
|
32
|
+
row = rows[0];
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
if (e.message?.includes('unrecognized configuration parameter')) {
|
|
36
|
+
throw new jose.errors.JOSEError(`Generate a new JWT secret on Supabase. Cause: ${e.message}`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
throw e;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const secret = row?.jwt_secret;
|
|
43
|
+
if (secret == null) {
|
|
44
|
+
return {
|
|
45
|
+
keys: [],
|
|
46
|
+
errors: [new jose.errors.JWKSNoMatchingKey()]
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const key = {
|
|
51
|
+
kty: 'oct',
|
|
52
|
+
alg: 'HS256',
|
|
53
|
+
// While the secret is valid base64, the base64-encoded form is the secret value.
|
|
54
|
+
k: Buffer.from(secret, 'utf8').toString('base64url')
|
|
55
|
+
};
|
|
56
|
+
const imported = await auth.KeySpec.importKey(key, this.keyOptions);
|
|
57
|
+
return {
|
|
58
|
+
keys: [imported],
|
|
59
|
+
errors: []
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=SupabaseKeyCollector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SupabaseKeyCollector.js","sourceRoot":"","sources":["../../src/auth/SupabaseKeyCollector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC/C,OAAO,KAAK,MAAM,MAAM,4BAA4B,CAAC;AACrD,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,OAAO,KAAK,YAAY,MAAM,0BAA0B,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,OAAO,oBAAoB;IAQ/B,YAAY,gBAAgD;QALpD,eAAU,GAAoB;YACpC,gBAAgB,EAAE,CAAC,eAAe,CAAC;YACnC,kBAAkB,EAAE,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,6BAA6B;SACnE,CAAC;QAGA,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,iBAAiB,CAAC,gBAAgB,EAAE;YACrD,kEAAkE;YAClE,iEAAiE;YACjE,kBAAkB;YAClB,WAAW,EAAE,IAAK;YAClB,OAAO,EAAE,CAAC;SACX,CAAC,CAAC;IACL,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,GAA2B,CAAC;QAChC,IAAI;YACF,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAC5B,MAAM,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,iEAAiE,CAAC,CAC9G,CAAC;YACF,GAAG,GAAG,IAAI,CAAC,CAAC,CAAQ,CAAC;SACtB;QAAC,OAAO,CAAC,EAAE;YACV,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,sCAAsC,CAAC,EAAE;gBAC/D,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,iDAAiD,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;aAC/F;iBAAM;gBACL,MAAM,CAAC,CAAC;aACT;SACF;QACD,MAAM,MAAM,GAAG,GAAG,EAAE,UAAgC,CAAC;QACrD,IAAI,MAAM,IAAI,IAAI,EAAE;YAClB,OAAO;gBACL,IAAI,EAAE,EAAE;gBACR,MAAM,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;aAC9C,CAAC;SACH;aAAM;YACL,MAAM,GAAG,GAAa;gBACpB,GAAG,EAAE,KAAK;gBACV,GAAG,EAAE,OAAO;gBACZ,iFAAiF;gBACjF,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;aACrD,CAAC;YACF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YACpE,OAAO;gBACL,IAAI,EAAE,CAAC,QAAQ,CAAC;gBAChB,MAAM,EAAE,EAAE;aACX,CAAC;SACH;IACH,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;AAE3C,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { api, replication, system, TearDownOptions } from '@powersync/service-core';
|
|
2
|
+
import * as types from '../types/types.js';
|
|
3
|
+
export declare class PostgresModule extends replication.ReplicationModule<types.PostgresConnectionConfig> {
|
|
4
|
+
constructor();
|
|
5
|
+
initialize(context: system.ServiceContextContainer): Promise<void>;
|
|
6
|
+
protected createRouteAPIAdapter(): api.RouteAPI;
|
|
7
|
+
protected createReplicator(context: system.ServiceContext): replication.AbstractReplicator;
|
|
8
|
+
/**
|
|
9
|
+
* Combines base config with normalized connection settings
|
|
10
|
+
*/
|
|
11
|
+
private resolveConfig;
|
|
12
|
+
teardown(options: TearDownOptions): Promise<void>;
|
|
13
|
+
private registerSupabaseAuth;
|
|
14
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { auth, ConfigurationFileSyncRulesProvider, replication } from '@powersync/service-core';
|
|
2
|
+
import * as jpgwire from '@powersync/service-jpgwire';
|
|
3
|
+
import * as types from '../types/types.js';
|
|
4
|
+
import { PostgresRouteAPIAdapter } from '../api/PostgresRouteAPIAdapter.js';
|
|
5
|
+
import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js';
|
|
6
|
+
import { WalStreamReplicator } from '../replication/WalStreamReplicator.js';
|
|
7
|
+
import { ConnectionManagerFactory } from '../replication/ConnectionManagerFactory.js';
|
|
8
|
+
import { PostgresErrorRateLimiter } from '../replication/PostgresErrorRateLimiter.js';
|
|
9
|
+
import { cleanUpReplicationSlot } from '../replication/replication-utils.js';
|
|
10
|
+
import { PgManager } from '../replication/PgManager.js';
|
|
11
|
+
export class PostgresModule extends replication.ReplicationModule {
|
|
12
|
+
constructor() {
|
|
13
|
+
super({
|
|
14
|
+
name: 'Postgres',
|
|
15
|
+
type: types.POSTGRES_CONNECTION_TYPE,
|
|
16
|
+
configSchema: types.PostgresConnectionConfig
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
async initialize(context) {
|
|
20
|
+
await super.initialize(context);
|
|
21
|
+
// Record replicated bytes using global jpgwire metrics.
|
|
22
|
+
if (context.configuration.base_config.client_auth?.supabase) {
|
|
23
|
+
this.registerSupabaseAuth(context);
|
|
24
|
+
}
|
|
25
|
+
if (context.metrics) {
|
|
26
|
+
jpgwire.setMetricsRecorder({
|
|
27
|
+
addBytesRead(bytes) {
|
|
28
|
+
context.metrics.data_replicated_bytes.add(bytes);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
createRouteAPIAdapter() {
|
|
34
|
+
return new PostgresRouteAPIAdapter(this.resolveConfig(this.decodedConfig));
|
|
35
|
+
}
|
|
36
|
+
createReplicator(context) {
|
|
37
|
+
const normalisedConfig = this.resolveConfig(this.decodedConfig);
|
|
38
|
+
const syncRuleProvider = new ConfigurationFileSyncRulesProvider(context.configuration.sync_rules);
|
|
39
|
+
const connectionFactory = new ConnectionManagerFactory(normalisedConfig);
|
|
40
|
+
return new WalStreamReplicator({
|
|
41
|
+
id: this.getDefaultId(normalisedConfig.database),
|
|
42
|
+
syncRuleProvider: syncRuleProvider,
|
|
43
|
+
storageEngine: context.storageEngine,
|
|
44
|
+
connectionFactory: connectionFactory,
|
|
45
|
+
rateLimiter: new PostgresErrorRateLimiter()
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Combines base config with normalized connection settings
|
|
50
|
+
*/
|
|
51
|
+
resolveConfig(config) {
|
|
52
|
+
return {
|
|
53
|
+
...config,
|
|
54
|
+
...types.normalizeConnectionConfig(config)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async teardown(options) {
|
|
58
|
+
const normalisedConfig = this.resolveConfig(this.decodedConfig);
|
|
59
|
+
const connectionManager = new PgManager(normalisedConfig, {
|
|
60
|
+
idleTimeout: 30000,
|
|
61
|
+
maxSize: 1
|
|
62
|
+
});
|
|
63
|
+
try {
|
|
64
|
+
if (options.syncRules) {
|
|
65
|
+
// TODO: In the future, once we have more replication types, we will need to check if these syncRules are for Postgres
|
|
66
|
+
for (let syncRules of options.syncRules) {
|
|
67
|
+
try {
|
|
68
|
+
await cleanUpReplicationSlot(syncRules.slot_name, connectionManager.pool);
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
// Not really much we can do here for failures, most likely the database is no longer accessible
|
|
72
|
+
this.logger.warn(`Failed to fully clean up Postgres replication slot: ${syncRules.slot_name}`, e);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
await connectionManager.end();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// TODO: This should rather be done by registering the key collector in some kind of auth engine
|
|
82
|
+
registerSupabaseAuth(context) {
|
|
83
|
+
const { configuration } = context;
|
|
84
|
+
// Register the Supabase key collector(s)
|
|
85
|
+
configuration.connections
|
|
86
|
+
?.map((baseConfig) => {
|
|
87
|
+
if (baseConfig.type != types.POSTGRES_CONNECTION_TYPE) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
return this.resolveConfig(types.PostgresConnectionConfig.decode(baseConfig));
|
|
92
|
+
}
|
|
93
|
+
catch (ex) {
|
|
94
|
+
this.logger.warn('Failed to decode configuration.', ex);
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
.filter((c) => !!c)
|
|
98
|
+
.forEach((config) => {
|
|
99
|
+
const keyCollector = new SupabaseKeyCollector(config);
|
|
100
|
+
context.lifeCycleEngine.withLifecycle(keyCollector, {
|
|
101
|
+
// Close the internal pool
|
|
102
|
+
stop: (collector) => collector.shutdown()
|
|
103
|
+
});
|
|
104
|
+
configuration.client_keystore.collector.add(new auth.CachedKeyCollector(keyCollector));
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=PostgresModule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgresModule.js","sourceRoot":"","sources":["../../src/module/PostgresModule.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { PgManager } from './PgManager.js';
|
|
2
|
+
import { NormalizedPostgresConnectionConfig } from '../types/types.js';
|
|
3
|
+
import { PgPoolOptions } from '@powersync/service-jpgwire';
|
|
4
|
+
export declare class ConnectionManagerFactory {
|
|
5
|
+
private readonly connectionManagers;
|
|
6
|
+
private readonly dbConnectionConfig;
|
|
7
|
+
constructor(dbConnectionConfig: NormalizedPostgresConnectionConfig);
|
|
8
|
+
create(poolOptions: PgPoolOptions): PgManager;
|
|
9
|
+
shutdown(): Promise<void>;
|
|
10
|
+
}
|