relq 1.0.0 → 1.0.2
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/cjs/addon/buffer/index.cjs +1881 -0
- package/dist/cjs/addon/pg/index.cjs +4812 -0
- package/dist/cjs/addon/pg-cursor/index.cjs +1451 -0
- package/dist/cjs/addon/pg-format/index.cjs +2270 -0
- package/dist/cjs/cli/commands/add.cjs +30 -1
- package/dist/cjs/cli/commands/branch.cjs +141 -0
- package/dist/cjs/cli/commands/checkout.cjs +134 -0
- package/dist/cjs/cli/commands/cherry-pick.cjs +283 -0
- package/dist/cjs/cli/commands/diff.cjs +148 -69
- package/dist/cjs/cli/commands/export.cjs +64 -5
- package/dist/cjs/cli/commands/fetch.cjs +34 -4
- package/dist/cjs/cli/commands/history.cjs +1 -1
- package/dist/cjs/cli/commands/import.cjs +283 -12
- package/dist/cjs/cli/commands/log.cjs +75 -0
- package/dist/cjs/cli/commands/merge.cjs +224 -0
- package/dist/cjs/cli/commands/migrate.cjs +1 -1
- package/dist/cjs/cli/commands/pull.cjs +123 -7
- package/dist/cjs/cli/commands/push.cjs +245 -29
- package/dist/cjs/cli/commands/remote.cjs +16 -0
- package/dist/cjs/cli/commands/reset.cjs +169 -0
- package/dist/cjs/cli/commands/resolve.cjs +193 -0
- package/dist/cjs/cli/commands/rollback.cjs +1 -1
- package/dist/cjs/cli/commands/stash.cjs +154 -0
- package/dist/cjs/cli/commands/status.cjs +48 -0
- package/dist/cjs/cli/commands/tag.cjs +147 -0
- package/dist/cjs/cli/index.cjs +46 -2
- package/dist/cjs/cli/utils/commit-manager.cjs +3 -3
- package/dist/cjs/cli/utils/env-loader.cjs +3 -2
- package/dist/cjs/cli/utils/fast-introspect.cjs +1 -1
- package/dist/cjs/cli/utils/project-root.cjs +56 -0
- package/dist/cjs/cli/utils/relqignore.cjs +296 -38
- package/dist/cjs/cli/utils/repo-manager.cjs +45 -3
- package/dist/cjs/cli/utils/schema-introspect.cjs +2 -2
- package/dist/cjs/cli/utils/sql-generator.cjs +1 -1
- package/dist/cjs/cli/utils/sql-parser.cjs +102 -13
- package/dist/cjs/condition/array-condition-builder.cjs +1 -1
- package/dist/cjs/condition/condition-collector.cjs +1 -1
- package/dist/cjs/condition/fulltext-condition-builder.cjs +1 -1
- package/dist/cjs/condition/geometric-condition-builder.cjs +1 -1
- package/dist/cjs/condition/jsonb-condition-builder.cjs +1 -1
- package/dist/cjs/condition/network-condition-builder.cjs +1 -1
- package/dist/cjs/condition/range-condition-builder.cjs +1 -1
- package/dist/cjs/copy/copy-builder.cjs +1 -1
- package/dist/cjs/core/query-builder.cjs +1 -1
- package/dist/cjs/core/relq-client.cjs +2 -2
- package/dist/cjs/count/count-builder.cjs +1 -1
- package/dist/cjs/cte/cte-builder.cjs +1 -1
- package/dist/cjs/delete/delete-builder.cjs +1 -1
- package/dist/cjs/function/create-function-builder.cjs +1 -1
- package/dist/cjs/functions/advanced-functions.cjs +1 -1
- package/dist/cjs/functions/case-builder.cjs +1 -1
- package/dist/cjs/functions/geometric-functions.cjs +1 -1
- package/dist/cjs/functions/network-functions.cjs +1 -1
- package/dist/cjs/functions/sql-functions.cjs +1 -1
- package/dist/cjs/indexing/create-index-builder.cjs +1 -1
- package/dist/cjs/indexing/drop-index-builder.cjs +1 -1
- package/dist/cjs/insert/conflict-builder.cjs +1 -1
- package/dist/cjs/insert/insert-builder.cjs +1 -1
- package/dist/cjs/maintenance/vacuum-builder.cjs +1 -1
- package/dist/cjs/pubsub/listen-notify-builder.cjs +1 -1
- package/dist/cjs/pubsub/listener-connection.cjs +2 -2
- package/dist/cjs/raw/raw-query-builder.cjs +1 -1
- package/dist/cjs/schema/schema-builder.cjs +1 -1
- package/dist/cjs/schema-definition/table-definition.cjs +1 -1
- package/dist/cjs/select/aggregate-builder.cjs +1 -1
- package/dist/cjs/select/select-builder.cjs +1 -1
- package/dist/cjs/sequence/sequence-builder.cjs +1 -1
- package/dist/cjs/table/alter-table-builder.cjs +1 -1
- package/dist/cjs/table/constraint-builder.cjs +1 -1
- package/dist/cjs/table/create-table-builder.cjs +1 -1
- package/dist/cjs/table/partition-builder.cjs +1 -1
- package/dist/cjs/table/truncate-builder.cjs +1 -1
- package/dist/cjs/transaction/transaction-builder.cjs +1 -1
- package/dist/cjs/trigger/create-trigger-builder.cjs +1 -1
- package/dist/cjs/update/array-update-builder.cjs +1 -1
- package/dist/cjs/update/update-builder.cjs +1 -1
- package/dist/cjs/utils/index.cjs +1 -1
- package/dist/cjs/view/create-view-builder.cjs +1 -1
- package/dist/cjs/window/window-builder.cjs +1 -1
- package/dist/esm/cli/commands/add.js +30 -1
- package/dist/esm/cli/commands/branch.js +105 -0
- package/dist/esm/cli/commands/checkout.js +98 -0
- package/dist/esm/cli/commands/cherry-pick.js +247 -0
- package/dist/esm/cli/commands/diff.js +148 -69
- package/dist/esm/cli/commands/export.js +64 -5
- package/dist/esm/cli/commands/fetch.js +35 -5
- package/dist/esm/cli/commands/history.js +1 -1
- package/dist/esm/cli/commands/import.js +283 -12
- package/dist/esm/cli/commands/log.js +74 -0
- package/dist/esm/cli/commands/merge.js +188 -0
- package/dist/esm/cli/commands/migrate.js +1 -1
- package/dist/esm/cli/commands/pull.js +124 -8
- package/dist/esm/cli/commands/push.js +246 -30
- package/dist/esm/cli/commands/remote.js +13 -0
- package/dist/esm/cli/commands/reset.js +133 -0
- package/dist/esm/cli/commands/resolve.js +157 -0
- package/dist/esm/cli/commands/rollback.js +1 -1
- package/dist/esm/cli/commands/stash.js +118 -0
- package/dist/esm/cli/commands/status.js +15 -0
- package/dist/esm/cli/commands/tag.js +111 -0
- package/dist/esm/cli/index.js +47 -3
- package/dist/esm/cli/utils/commit-manager.js +3 -3
- package/dist/esm/cli/utils/env-loader.js +3 -2
- package/dist/esm/cli/utils/fast-introspect.js +1 -1
- package/dist/esm/cli/utils/project-root.js +19 -0
- package/dist/esm/cli/utils/relqignore.js +277 -37
- package/dist/esm/cli/utils/repo-manager.js +41 -3
- package/dist/esm/cli/utils/schema-introspect.js +2 -2
- package/dist/esm/cli/utils/sql-generator.js +1 -1
- package/dist/esm/cli/utils/sql-parser.js +102 -13
- package/dist/esm/condition/array-condition-builder.js +1 -1
- package/dist/esm/condition/condition-collector.js +1 -1
- package/dist/esm/condition/fulltext-condition-builder.js +1 -1
- package/dist/esm/condition/geometric-condition-builder.js +1 -1
- package/dist/esm/condition/jsonb-condition-builder.js +1 -1
- package/dist/esm/condition/network-condition-builder.js +1 -1
- package/dist/esm/condition/range-condition-builder.js +1 -1
- package/dist/esm/copy/copy-builder.js +1 -1
- package/dist/esm/core/query-builder.js +1 -1
- package/dist/esm/core/relq-client.js +2 -2
- package/dist/esm/count/count-builder.js +1 -1
- package/dist/esm/cte/cte-builder.js +1 -1
- package/dist/esm/delete/delete-builder.js +1 -1
- package/dist/esm/function/create-function-builder.js +1 -1
- package/dist/esm/functions/advanced-functions.js +1 -1
- package/dist/esm/functions/case-builder.js +1 -1
- package/dist/esm/functions/geometric-functions.js +1 -1
- package/dist/esm/functions/network-functions.js +1 -1
- package/dist/esm/functions/sql-functions.js +1 -1
- package/dist/esm/indexing/create-index-builder.js +1 -1
- package/dist/esm/indexing/drop-index-builder.js +1 -1
- package/dist/esm/insert/conflict-builder.js +1 -1
- package/dist/esm/insert/insert-builder.js +1 -1
- package/dist/esm/maintenance/vacuum-builder.js +1 -1
- package/dist/esm/pubsub/listen-notify-builder.js +1 -1
- package/dist/esm/pubsub/listener-connection.js +2 -2
- package/dist/esm/raw/raw-query-builder.js +1 -1
- package/dist/esm/schema/schema-builder.js +1 -1
- package/dist/esm/schema-definition/table-definition.js +1 -1
- package/dist/esm/select/aggregate-builder.js +1 -1
- package/dist/esm/select/select-builder.js +1 -1
- package/dist/esm/sequence/sequence-builder.js +1 -1
- package/dist/esm/table/alter-table-builder.js +1 -1
- package/dist/esm/table/constraint-builder.js +1 -1
- package/dist/esm/table/create-table-builder.js +1 -1
- package/dist/esm/table/partition-builder.js +1 -1
- package/dist/esm/table/truncate-builder.js +1 -1
- package/dist/esm/transaction/transaction-builder.js +1 -1
- package/dist/esm/trigger/create-trigger-builder.js +1 -1
- package/dist/esm/update/array-update-builder.js +1 -1
- package/dist/esm/update/update-builder.js +1 -1
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/view/create-view-builder.js +1 -1
- package/dist/esm/window/window-builder.js +1 -1
- package/package.json +1 -1
- /package/dist/{addons/buffer.js → esm/addon/buffer/index.js} +0 -0
- /package/dist/{addons/pg.js → esm/addon/pg/index.js} +0 -0
- /package/dist/{addons/pg-cursor.js → esm/addon/pg-cursor/index.js} +0 -0
- /package/dist/{addons/pg-format.js → esm/addon/pg-format/index.js} +0 -0
|
@@ -1,23 +1,88 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
const REQUIRES_PARENT = [
|
|
4
|
+
'COLUMN', 'INDEX', 'CONSTRAINT', 'CHECK', 'PRIMARY_KEY',
|
|
5
|
+
'FOREIGN_KEY', 'EXCLUSION', 'PARTITION', 'TRIGGER'
|
|
6
|
+
];
|
|
3
7
|
const DEFAULT_PATTERNS = [
|
|
4
|
-
'_relq_*',
|
|
5
|
-
'pg_*',
|
|
6
|
-
'_temp_*',
|
|
7
|
-
'tmp_*',
|
|
8
|
+
'TABLE:_relq_*',
|
|
9
|
+
'TABLE:pg_*',
|
|
10
|
+
'TABLE:_temp_*',
|
|
11
|
+
'TABLE:tmp_*',
|
|
8
12
|
];
|
|
13
|
+
export function parsePattern(line) {
|
|
14
|
+
const trimmed = line.trim();
|
|
15
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
let negated = false;
|
|
19
|
+
let pattern = trimmed;
|
|
20
|
+
if (pattern.startsWith('!')) {
|
|
21
|
+
negated = true;
|
|
22
|
+
pattern = pattern.slice(1);
|
|
23
|
+
}
|
|
24
|
+
const colonIndex = pattern.indexOf(':');
|
|
25
|
+
if (colonIndex > 0) {
|
|
26
|
+
const typeStr = pattern.slice(0, colonIndex).toUpperCase();
|
|
27
|
+
const rest = pattern.slice(colonIndex + 1);
|
|
28
|
+
const validTypes = [
|
|
29
|
+
'TABLE', 'COLUMN', 'INDEX', 'CONSTRAINT', 'CHECK', 'PRIMARY_KEY',
|
|
30
|
+
'FOREIGN_KEY', 'EXCLUSION', 'PARTITION', 'ENUM', 'DOMAIN', 'SEQUENCE',
|
|
31
|
+
'COMPOSITE_TYPE', 'FUNCTION', 'PROCEDURE', 'TRIGGER', 'VIEW',
|
|
32
|
+
'MATERIALIZED_VIEW', 'FOREIGN_TABLE', 'EXTENSION', 'COLLATION'
|
|
33
|
+
];
|
|
34
|
+
if (validTypes.includes(typeStr)) {
|
|
35
|
+
const type = typeStr;
|
|
36
|
+
if (REQUIRES_PARENT.includes(type)) {
|
|
37
|
+
const dotIndex = rest.indexOf('.');
|
|
38
|
+
if (dotIndex > 0) {
|
|
39
|
+
return {
|
|
40
|
+
type,
|
|
41
|
+
parent: rest.slice(0, dotIndex),
|
|
42
|
+
pattern: rest.slice(dotIndex + 1),
|
|
43
|
+
negated,
|
|
44
|
+
raw: trimmed,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.warn(`Warning: ${type} pattern requires table name (e.g., ${type}:table_name.pattern)`);
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
return {
|
|
54
|
+
type,
|
|
55
|
+
parent: null,
|
|
56
|
+
pattern: rest,
|
|
57
|
+
negated,
|
|
58
|
+
raw: trimmed,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
type: 'TABLE',
|
|
65
|
+
parent: null,
|
|
66
|
+
pattern,
|
|
67
|
+
negated,
|
|
68
|
+
raw: trimmed,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
9
71
|
export function loadRelqignore(projectRoot = process.cwd()) {
|
|
10
72
|
const ignorePath = path.join(projectRoot, '.relqignore');
|
|
11
|
-
const patterns = [
|
|
73
|
+
const patterns = [];
|
|
74
|
+
for (const defaultPattern of DEFAULT_PATTERNS) {
|
|
75
|
+
const parsed = parsePattern(defaultPattern);
|
|
76
|
+
if (parsed)
|
|
77
|
+
patterns.push(parsed);
|
|
78
|
+
}
|
|
12
79
|
if (fs.existsSync(ignorePath)) {
|
|
13
80
|
const content = fs.readFileSync(ignorePath, 'utf-8');
|
|
14
81
|
const lines = content.split('\n');
|
|
15
82
|
for (const line of lines) {
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
patterns.push(trimmed);
|
|
83
|
+
const parsed = parsePattern(line);
|
|
84
|
+
if (parsed)
|
|
85
|
+
patterns.push(parsed);
|
|
21
86
|
}
|
|
22
87
|
}
|
|
23
88
|
return patterns;
|
|
@@ -28,40 +93,58 @@ export function createDefaultRelqignore(projectRoot = process.cwd()) {
|
|
|
28
93
|
return;
|
|
29
94
|
}
|
|
30
95
|
const content = `# Relq Ignore File
|
|
31
|
-
#
|
|
96
|
+
# Objects matching these patterns will be ignored in import/export/add/commit
|
|
97
|
+
|
|
98
|
+
# =============================================================================
|
|
99
|
+
# PATTERN FORMAT
|
|
100
|
+
# =============================================================================
|
|
101
|
+
# TYPE:pattern - Match specific object type
|
|
102
|
+
# TYPE:table.pattern - Match sub-objects (columns, indexes, etc.)
|
|
103
|
+
# pattern - Match tables (backward compatible)
|
|
104
|
+
# !pattern - Negate (un-ignore) a pattern
|
|
105
|
+
|
|
106
|
+
# =============================================================================
|
|
107
|
+
# TYPES
|
|
108
|
+
# =============================================================================
|
|
109
|
+
# TABLE, COLUMN, INDEX, CONSTRAINT, CHECK, PRIMARY_KEY, FOREIGN_KEY, EXCLUSION
|
|
110
|
+
# PARTITION, ENUM, DOMAIN, SEQUENCE, COMPOSITE_TYPE, FUNCTION, PROCEDURE
|
|
111
|
+
# TRIGGER, VIEW, MATERIALIZED_VIEW, FOREIGN_TABLE, EXTENSION, COLLATION
|
|
112
|
+
|
|
113
|
+
# =============================================================================
|
|
114
|
+
# DEFAULT PATTERNS (always applied)
|
|
115
|
+
# =============================================================================
|
|
116
|
+
# TABLE:_relq_* - Relq internal tables
|
|
117
|
+
# TABLE:pg_* - PostgreSQL system tables
|
|
118
|
+
# TABLE:_temp_* - Temporary tables
|
|
119
|
+
# TABLE:tmp_* - Temporary tables
|
|
120
|
+
|
|
121
|
+
# =============================================================================
|
|
122
|
+
# EXAMPLES
|
|
123
|
+
# =============================================================================
|
|
124
|
+
|
|
125
|
+
# Ignore specific tables
|
|
126
|
+
# TABLE:debug_*
|
|
127
|
+
# TABLE:test_*
|
|
128
|
+
|
|
129
|
+
# Ignore sensitive columns (table.column format required)
|
|
130
|
+
# COLUMN:users.password_hash
|
|
131
|
+
# COLUMN:*.api_key
|
|
32
132
|
|
|
33
|
-
#
|
|
34
|
-
|
|
133
|
+
# Ignore temporary indexes
|
|
134
|
+
# INDEX:*.idx_temp_*
|
|
35
135
|
|
|
36
|
-
#
|
|
37
|
-
|
|
136
|
+
# Ignore debug functions
|
|
137
|
+
# FUNCTION:debug_*
|
|
38
138
|
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
tmp_*
|
|
139
|
+
# Ignore internal triggers
|
|
140
|
+
# TRIGGER:audit_logs.*
|
|
42
141
|
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
# debug_*
|
|
46
|
-
# test_*
|
|
142
|
+
# Ignore test enums
|
|
143
|
+
# ENUM:test_*
|
|
47
144
|
`;
|
|
48
145
|
fs.writeFileSync(ignorePath, content, 'utf-8');
|
|
49
146
|
}
|
|
50
|
-
|
|
51
|
-
for (const pattern of patterns) {
|
|
52
|
-
if (matchPattern(name, pattern)) {
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
export function filterIgnored(items, patterns) {
|
|
59
|
-
return items.filter(item => !isIgnored(item.name, patterns));
|
|
60
|
-
}
|
|
61
|
-
function matchPattern(name, pattern) {
|
|
62
|
-
if (pattern.startsWith('!')) {
|
|
63
|
-
return !matchPattern(name, pattern.slice(1));
|
|
64
|
-
}
|
|
147
|
+
function matchGlob(name, pattern) {
|
|
65
148
|
const regexPattern = pattern
|
|
66
149
|
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
67
150
|
.replace(/\*/g, '.*')
|
|
@@ -69,6 +152,163 @@ function matchPattern(name, pattern) {
|
|
|
69
152
|
const regex = new RegExp(`^${regexPattern}$`, 'i');
|
|
70
153
|
return regex.test(name);
|
|
71
154
|
}
|
|
155
|
+
export function isIgnored(objectType, objectName, parentName, patterns) {
|
|
156
|
+
let isIgnored = false;
|
|
157
|
+
let matchedPattern;
|
|
158
|
+
for (const pattern of patterns) {
|
|
159
|
+
if (pattern.type !== null && pattern.type !== objectType) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (pattern.parent !== null) {
|
|
163
|
+
if (parentName === null || !matchGlob(parentName, pattern.parent)) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (matchGlob(objectName, pattern.pattern)) {
|
|
168
|
+
if (pattern.negated) {
|
|
169
|
+
isIgnored = false;
|
|
170
|
+
matchedPattern = pattern;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
isIgnored = true;
|
|
174
|
+
matchedPattern = pattern;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
ignored: isIgnored,
|
|
180
|
+
pattern: matchedPattern,
|
|
181
|
+
reason: matchedPattern ? `Matched pattern: ${matchedPattern.raw}` : undefined,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
export function isTableIgnored(tableName, patterns) {
|
|
185
|
+
return isIgnored('TABLE', tableName, null, patterns);
|
|
186
|
+
}
|
|
187
|
+
export function isColumnIgnored(tableName, columnName, patterns) {
|
|
188
|
+
return isIgnored('COLUMN', columnName, tableName, patterns);
|
|
189
|
+
}
|
|
190
|
+
export function isIndexIgnored(tableName, indexName, patterns) {
|
|
191
|
+
return isIgnored('INDEX', indexName, tableName, patterns);
|
|
192
|
+
}
|
|
193
|
+
export function isConstraintIgnored(tableName, constraintName, patterns) {
|
|
194
|
+
return isIgnored('CONSTRAINT', constraintName, tableName, patterns);
|
|
195
|
+
}
|
|
196
|
+
export function isTriggerIgnored(tableName, triggerName, patterns) {
|
|
197
|
+
return isIgnored('TRIGGER', triggerName, tableName, patterns);
|
|
198
|
+
}
|
|
199
|
+
export function isEnumIgnored(enumName, patterns) {
|
|
200
|
+
return isIgnored('ENUM', enumName, null, patterns);
|
|
201
|
+
}
|
|
202
|
+
export function isDomainIgnored(domainName, patterns) {
|
|
203
|
+
return isIgnored('DOMAIN', domainName, null, patterns);
|
|
204
|
+
}
|
|
205
|
+
export function isSequenceIgnored(sequenceName, patterns) {
|
|
206
|
+
return isIgnored('SEQUENCE', sequenceName, null, patterns);
|
|
207
|
+
}
|
|
208
|
+
export function isCompositeTypeIgnored(typeName, patterns) {
|
|
209
|
+
return isIgnored('COMPOSITE_TYPE', typeName, null, patterns);
|
|
210
|
+
}
|
|
211
|
+
export function isFunctionIgnored(functionName, patterns) {
|
|
212
|
+
return isIgnored('FUNCTION', functionName, null, patterns);
|
|
213
|
+
}
|
|
214
|
+
export function isViewIgnored(viewName, patterns) {
|
|
215
|
+
return isIgnored('VIEW', viewName, null, patterns);
|
|
216
|
+
}
|
|
217
|
+
export function validateIgnoreDependencies(schema, patterns) {
|
|
218
|
+
const errors = [];
|
|
219
|
+
const ignoredEnums = new Set();
|
|
220
|
+
const ignoredDomains = new Set();
|
|
221
|
+
const ignoredSequences = new Set();
|
|
222
|
+
const ignoredComposites = new Set();
|
|
223
|
+
for (const e of schema.enums) {
|
|
224
|
+
if (isEnumIgnored(e.name, patterns).ignored) {
|
|
225
|
+
ignoredEnums.add(e.name.toLowerCase());
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
for (const d of schema.domains) {
|
|
229
|
+
if (isDomainIgnored(d.name, patterns).ignored) {
|
|
230
|
+
ignoredDomains.add(d.name.toLowerCase());
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
for (const s of schema.sequences) {
|
|
234
|
+
if (isSequenceIgnored(s.name, patterns).ignored) {
|
|
235
|
+
ignoredSequences.add(s.name.toLowerCase());
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
for (const c of schema.compositeTypes) {
|
|
239
|
+
if (isCompositeTypeIgnored(c.name, patterns).ignored) {
|
|
240
|
+
ignoredComposites.add(c.name.toLowerCase());
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
for (const table of schema.tables) {
|
|
244
|
+
if (isTableIgnored(table.name, patterns).ignored) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
for (const column of table.columns) {
|
|
248
|
+
if (isColumnIgnored(table.name, column.name, patterns).ignored) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
const typeLower = column.type.toLowerCase().replace(/\[\]$/, '');
|
|
252
|
+
if (ignoredEnums.has(typeLower)) {
|
|
253
|
+
errors.push({
|
|
254
|
+
type: 'ENUM',
|
|
255
|
+
name: column.type,
|
|
256
|
+
usedBy: { table: table.name, column: column.name },
|
|
257
|
+
message: `Column "${table.name}.${column.name}" uses ignored ENUM "${column.type}". Either un-ignore the ENUM or ignore this column.`,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
if (ignoredDomains.has(typeLower)) {
|
|
261
|
+
errors.push({
|
|
262
|
+
type: 'DOMAIN',
|
|
263
|
+
name: column.type,
|
|
264
|
+
usedBy: { table: table.name, column: column.name },
|
|
265
|
+
message: `Column "${table.name}.${column.name}" uses ignored DOMAIN "${column.type}". Either un-ignore the DOMAIN or ignore this column.`,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (ignoredComposites.has(typeLower)) {
|
|
269
|
+
errors.push({
|
|
270
|
+
type: 'COMPOSITE_TYPE',
|
|
271
|
+
name: column.type,
|
|
272
|
+
usedBy: { table: table.name, column: column.name },
|
|
273
|
+
message: `Column "${table.name}.${column.name}" uses ignored COMPOSITE_TYPE "${column.type}". Either un-ignore the type or ignore this column.`,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
if (column.default) {
|
|
277
|
+
const seqMatch = column.default.match(/nextval\(['"]?([^'"()]+)['"]?/i);
|
|
278
|
+
if (seqMatch) {
|
|
279
|
+
const seqName = seqMatch[1].toLowerCase().replace(/::regclass$/, '');
|
|
280
|
+
if (ignoredSequences.has(seqName)) {
|
|
281
|
+
errors.push({
|
|
282
|
+
type: 'SEQUENCE',
|
|
283
|
+
name: seqMatch[1],
|
|
284
|
+
usedBy: { table: table.name, column: column.name },
|
|
285
|
+
message: `Column "${table.name}.${column.name}" uses ignored SEQUENCE "${seqMatch[1]}". Either un-ignore the SEQUENCE or ignore this column.`,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return errors;
|
|
293
|
+
}
|
|
294
|
+
export function filterTables(tables, patterns) {
|
|
295
|
+
return tables.filter(t => !isTableIgnored(t.name, patterns).ignored);
|
|
296
|
+
}
|
|
297
|
+
export function filterColumns(tableName, columns, patterns) {
|
|
298
|
+
return columns.filter(c => !isColumnIgnored(tableName, c.name, patterns).ignored);
|
|
299
|
+
}
|
|
300
|
+
export function filterIndexes(tableName, indexes, patterns) {
|
|
301
|
+
return indexes.filter(i => !isIndexIgnored(tableName, i.name, patterns).ignored);
|
|
302
|
+
}
|
|
303
|
+
export function filterEnums(enums, patterns) {
|
|
304
|
+
return enums.filter(e => !isEnumIgnored(e.name, patterns).ignored);
|
|
305
|
+
}
|
|
306
|
+
export function filterDomains(domains, patterns) {
|
|
307
|
+
return domains.filter(d => !isDomainIgnored(d.name, patterns).ignored);
|
|
308
|
+
}
|
|
72
309
|
export function getIgnorePatterns(projectRoot = process.cwd()) {
|
|
73
310
|
return loadRelqignore(projectRoot);
|
|
74
311
|
}
|
|
312
|
+
export function filterIgnored(items, patterns) {
|
|
313
|
+
return items.filter(item => !isTableIgnored(item.name, patterns).ignored);
|
|
314
|
+
}
|
|
@@ -279,7 +279,7 @@ export function hasUncommittedChanges(projectRoot = process.cwd()) {
|
|
|
279
279
|
return state.staged.length > 0 || state.unstaged.length > 0;
|
|
280
280
|
}
|
|
281
281
|
export async function ensureRemoteTable(connection) {
|
|
282
|
-
const { Pool } = await import("../../addon/pg.js");
|
|
282
|
+
const { Pool } = await import("../../addon/pg/index.js");
|
|
283
283
|
const pool = new Pool({
|
|
284
284
|
host: connection.host,
|
|
285
285
|
port: connection.port || 5432,
|
|
@@ -311,7 +311,7 @@ export async function ensureRemoteTable(connection) {
|
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
313
|
export async function fetchRemoteCommits(connection, limit = 100) {
|
|
314
|
-
const { Pool } = await import("../../addon/pg.js");
|
|
314
|
+
const { Pool } = await import("../../addon/pg/index.js");
|
|
315
315
|
const pool = new Pool({
|
|
316
316
|
host: connection.host,
|
|
317
317
|
port: connection.port || 5432,
|
|
@@ -347,7 +347,7 @@ export async function getRemoteHead(connection) {
|
|
|
347
347
|
return commits.length > 0 ? commits[0].hash : null;
|
|
348
348
|
}
|
|
349
349
|
export async function pushCommit(connection, commit) {
|
|
350
|
-
const { Pool } = await import("../../addon/pg.js");
|
|
350
|
+
const { Pool } = await import("../../addon/pg/index.js");
|
|
351
351
|
const pool = new Pool({
|
|
352
352
|
host: connection.host,
|
|
353
353
|
port: connection.port || 5432,
|
|
@@ -442,3 +442,41 @@ export function migrateSnapshot(snapshot) {
|
|
|
442
442
|
foreignTables: snapshot.foreignTables || [],
|
|
443
443
|
};
|
|
444
444
|
}
|
|
445
|
+
export function loadTags(projectRoot = process.cwd()) {
|
|
446
|
+
const tagPath = path.join(projectRoot, '.relq', 'tags.json');
|
|
447
|
+
if (fs.existsSync(tagPath)) {
|
|
448
|
+
return JSON.parse(fs.readFileSync(tagPath, 'utf-8'));
|
|
449
|
+
}
|
|
450
|
+
return {};
|
|
451
|
+
}
|
|
452
|
+
export function saveTags(tags, projectRoot = process.cwd()) {
|
|
453
|
+
const tagPath = path.join(projectRoot, '.relq', 'tags.json');
|
|
454
|
+
fs.writeFileSync(tagPath, JSON.stringify(tags, null, 2));
|
|
455
|
+
}
|
|
456
|
+
export function resolveRef(ref, projectRoot = process.cwd()) {
|
|
457
|
+
const commit = loadCommit(ref, projectRoot);
|
|
458
|
+
if (commit)
|
|
459
|
+
return commit.hash;
|
|
460
|
+
const tags = loadTags(projectRoot);
|
|
461
|
+
if (tags[ref])
|
|
462
|
+
return tags[ref].hash;
|
|
463
|
+
const headMatch = ref.match(/^HEAD~(\d+)$/);
|
|
464
|
+
if (headMatch) {
|
|
465
|
+
const offset = parseInt(headMatch[1]);
|
|
466
|
+
let current = getHead(projectRoot);
|
|
467
|
+
for (let i = 0; i < offset && current; i++) {
|
|
468
|
+
const c = loadCommit(current, projectRoot);
|
|
469
|
+
if (!c || !c.parentHash)
|
|
470
|
+
return null;
|
|
471
|
+
current = c.parentHash;
|
|
472
|
+
}
|
|
473
|
+
return current;
|
|
474
|
+
}
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
export function loadParentCommit(hash, projectRoot = process.cwd()) {
|
|
478
|
+
const commit = loadCommit(hash, projectRoot);
|
|
479
|
+
if (!commit || !commit.parentHash)
|
|
480
|
+
return null;
|
|
481
|
+
return loadCommit(commit.parentHash, projectRoot);
|
|
482
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export async function introspectDatabase(connection, onProgress, options) {
|
|
2
2
|
const { includeFunctions = false, includeTriggers = false } = options || {};
|
|
3
|
-
const { Pool } = await import("../../addon/pg.js");
|
|
3
|
+
const { Pool } = await import("../../addon/pg/index.js");
|
|
4
4
|
onProgress?.('connecting', connection.database);
|
|
5
5
|
const pool = new Pool({
|
|
6
6
|
host: connection.host,
|
|
@@ -396,7 +396,7 @@ function parseOptions(options) {
|
|
|
396
396
|
return result;
|
|
397
397
|
}
|
|
398
398
|
export async function tableHasData(connection, tableName) {
|
|
399
|
-
const { Pool } = await import("../../addon/pg.js");
|
|
399
|
+
const { Pool } = await import("../../addon/pg/index.js");
|
|
400
400
|
const pool = new Pool({
|
|
401
401
|
host: connection.host,
|
|
402
402
|
port: connection.port || 5432,
|
|
@@ -206,7 +206,7 @@ function generateColumnSQL(col) {
|
|
|
206
206
|
if (col.identityGeneration) {
|
|
207
207
|
parts.push(`GENERATED ${col.identityGeneration} AS IDENTITY`);
|
|
208
208
|
}
|
|
209
|
-
if (!col.isNullable && !col.isPrimaryKey) {
|
|
209
|
+
if (!col.isNullable && !col.isPrimaryKey && !col.identityGeneration) {
|
|
210
210
|
parts.push('NOT NULL');
|
|
211
211
|
}
|
|
212
212
|
if (col.defaultValue !== null && col.defaultValue !== undefined && !col.isGenerated) {
|
|
@@ -6,6 +6,9 @@ export function parseSqlFile(sqlContent) {
|
|
|
6
6
|
const sequences = [];
|
|
7
7
|
const collations = [];
|
|
8
8
|
const foreignTables = [];
|
|
9
|
+
const views = [];
|
|
10
|
+
const materializedViews = [];
|
|
11
|
+
const foreignServers = [];
|
|
9
12
|
const extensions = [];
|
|
10
13
|
const partitions = [];
|
|
11
14
|
const cleanedSql = removeComments(sqlContent);
|
|
@@ -18,7 +21,14 @@ export function parseSqlFile(sqlContent) {
|
|
|
18
21
|
parseTables(cleanedSql, tables, partitions, enums, domains);
|
|
19
22
|
parseIndexes(cleanedSql, tables);
|
|
20
23
|
parseForeignTables(cleanedSql, foreignTables);
|
|
21
|
-
|
|
24
|
+
parseViews(cleanedSql, views);
|
|
25
|
+
parseMaterializedViews(cleanedSql, materializedViews);
|
|
26
|
+
parseForeignServers(cleanedSql, foreignServers);
|
|
27
|
+
return {
|
|
28
|
+
tables, enums, domains, compositeTypes, sequences, collations,
|
|
29
|
+
foreignTables, views, materializedViews, foreignServers,
|
|
30
|
+
extensions, partitions
|
|
31
|
+
};
|
|
22
32
|
}
|
|
23
33
|
function removeComments(sql) {
|
|
24
34
|
let result = sql.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
@@ -110,11 +120,11 @@ function parseCompositeTypeBody(body) {
|
|
|
110
120
|
const trimmed = line.trim();
|
|
111
121
|
if (!trimmed)
|
|
112
122
|
continue;
|
|
113
|
-
const attrMatch = trimmed.match(/^(\w+)\s+(.+)$/);
|
|
123
|
+
const attrMatch = trimmed.match(/^(?:"([^"]+)"|(\w+))\s+(.+)$/);
|
|
114
124
|
if (attrMatch) {
|
|
115
125
|
attributes.push({
|
|
116
|
-
name: attrMatch[1],
|
|
117
|
-
type: attrMatch[
|
|
126
|
+
name: attrMatch[1] || attrMatch[2],
|
|
127
|
+
type: attrMatch[3].trim().replace(/,\s*$/, ''),
|
|
118
128
|
});
|
|
119
129
|
}
|
|
120
130
|
}
|
|
@@ -359,12 +369,14 @@ function parseTableBody(body, enums, domains) {
|
|
|
359
369
|
const trimmed = part.trim();
|
|
360
370
|
if (!trimmed)
|
|
361
371
|
continue;
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
372
|
+
const upper = trimmed.toUpperCase();
|
|
373
|
+
const isConstraint = upper.startsWith('CONSTRAINT ') ||
|
|
374
|
+
/^PRIMARY\s+KEY\s*\(/i.test(trimmed) ||
|
|
375
|
+
/^FOREIGN\s+KEY\s*\(/i.test(trimmed) ||
|
|
376
|
+
/^UNIQUE\s*\(/i.test(trimmed) ||
|
|
377
|
+
/^CHECK\s*\(/i.test(trimmed) ||
|
|
378
|
+
/^EXCLUDE\s+(?:USING\s+|ON\s+|\()/i.test(trimmed);
|
|
379
|
+
if (isConstraint) {
|
|
368
380
|
const constraint = parseConstraint(trimmed);
|
|
369
381
|
if (constraint) {
|
|
370
382
|
constraints.push(constraint);
|
|
@@ -611,9 +623,9 @@ function extractMaxLength(type) {
|
|
|
611
623
|
}
|
|
612
624
|
function parseConstraint(def) {
|
|
613
625
|
const upper = def.toUpperCase();
|
|
614
|
-
const namedMatch = def.match(/CONSTRAINT\s+(\w+)\s+(.+)/
|
|
615
|
-
const constraintDef = namedMatch ? namedMatch[
|
|
616
|
-
const constraintName = namedMatch ? namedMatch[1] : '';
|
|
626
|
+
const namedMatch = def.match(/CONSTRAINT\s+(?:"([^"]+)"|(\w+))\s+(.+)/is);
|
|
627
|
+
const constraintDef = namedMatch ? namedMatch[3] : def;
|
|
628
|
+
const constraintName = namedMatch ? (namedMatch[1] || namedMatch[2]) : '';
|
|
617
629
|
const constraintUpper = constraintDef.toUpperCase();
|
|
618
630
|
if (constraintUpper.startsWith('PRIMARY KEY')) {
|
|
619
631
|
const colsMatch = constraintDef.match(/PRIMARY\s+KEY\s*\(\s*([^)]+)\s*\)/i);
|
|
@@ -989,4 +1001,81 @@ export function parseComments(sql) {
|
|
|
989
1001
|
}
|
|
990
1002
|
return comments;
|
|
991
1003
|
}
|
|
1004
|
+
function parseViews(sql, views) {
|
|
1005
|
+
const viewRegex = /CREATE\s+(?:OR\s+REPLACE\s+)?VIEW\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:(?:"?(\w+)"?\.)?"?(\w+)"?)\s*(?:\(([^)]+)\))?\s+AS\s+([\s\S]+?)(?:;|\s+WITH\s+(?:CASCADED|LOCAL)\s+CHECK\s+OPTION\s*;)/gi;
|
|
1006
|
+
let match;
|
|
1007
|
+
while ((match = viewRegex.exec(sql)) !== null) {
|
|
1008
|
+
const schema = match[1] || 'public';
|
|
1009
|
+
const name = match[2];
|
|
1010
|
+
const columnsStr = match[3];
|
|
1011
|
+
const definition = match[4].trim();
|
|
1012
|
+
const columns = columnsStr
|
|
1013
|
+
? columnsStr.split(',').map(c => c.trim().replace(/"/g, ''))
|
|
1014
|
+
: undefined;
|
|
1015
|
+
views.push({
|
|
1016
|
+
name,
|
|
1017
|
+
schema,
|
|
1018
|
+
definition,
|
|
1019
|
+
columns,
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
const simpleViewRegex = /CREATE\s+(?:OR\s+REPLACE\s+)?VIEW\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:(?:"?(\w+)"?\.)?"?(\w+)"?)\s*AS\s+(SELECT[\s\S]+?);/gi;
|
|
1023
|
+
while ((match = simpleViewRegex.exec(sql)) !== null) {
|
|
1024
|
+
const schema = match[1] || 'public';
|
|
1025
|
+
const name = match[2];
|
|
1026
|
+
const definition = match[3].trim();
|
|
1027
|
+
if (views.some(v => v.name === name))
|
|
1028
|
+
continue;
|
|
1029
|
+
views.push({
|
|
1030
|
+
name,
|
|
1031
|
+
schema,
|
|
1032
|
+
definition,
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
function parseMaterializedViews(sql, materializedViews) {
|
|
1037
|
+
const mviewRegex = /CREATE\s+MATERIALIZED\s+VIEW\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:(?:"?(\w+)"?\.)?"?(\w+)"?)\s*(?:\(([^)]+)\))?\s+AS\s+([\s\S]+?)(?:WITH\s+(NO\s+)?DATA\s*)?;/gi;
|
|
1038
|
+
let match;
|
|
1039
|
+
while ((match = mviewRegex.exec(sql)) !== null) {
|
|
1040
|
+
const schema = match[1] || 'public';
|
|
1041
|
+
const name = match[2];
|
|
1042
|
+
const columnsStr = match[3];
|
|
1043
|
+
const definition = match[4].trim();
|
|
1044
|
+
const withNoData = match[5] !== undefined;
|
|
1045
|
+
const columns = columnsStr
|
|
1046
|
+
? columnsStr.split(',').map(c => c.trim().replace(/"/g, ''))
|
|
1047
|
+
: undefined;
|
|
1048
|
+
materializedViews.push({
|
|
1049
|
+
name,
|
|
1050
|
+
schema,
|
|
1051
|
+
definition,
|
|
1052
|
+
columns,
|
|
1053
|
+
withData: !withNoData,
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
function parseForeignServers(sql, foreignServers) {
|
|
1058
|
+
const serverRegex = /CREATE\s+SERVER\s+(?:IF\s+NOT\s+EXISTS\s+)?"?(\w+)"?\s+FOREIGN\s+DATA\s+WRAPPER\s+"?(\w+)"?\s*(?:OPTIONS\s*\(([^)]+)\))?;/gi;
|
|
1059
|
+
let match;
|
|
1060
|
+
while ((match = serverRegex.exec(sql)) !== null) {
|
|
1061
|
+
const name = match[1];
|
|
1062
|
+
const fdwName = match[2];
|
|
1063
|
+
const optionsStr = match[3];
|
|
1064
|
+
const options = {};
|
|
1065
|
+
if (optionsStr) {
|
|
1066
|
+
const optParts = optionsStr.split(',');
|
|
1067
|
+
for (const part of optParts) {
|
|
1068
|
+
const optMatch = part.trim().match(/(\w+)\s+'([^']+)'/);
|
|
1069
|
+
if (optMatch) {
|
|
1070
|
+
options[optMatch[1]] = optMatch[2];
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
foreignServers.push({
|
|
1075
|
+
name,
|
|
1076
|
+
fdwName,
|
|
1077
|
+
options: Object.keys(options).length > 0 ? options : undefined,
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
992
1081
|
export default parseSqlFile;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import format from "
|
|
1
|
+
import format from "../addon/pg-format/index.js";
|
|
2
2
|
import { ArrayStringConditionBuilder } from "./array-string-condition-builder.js";
|
|
3
3
|
import { ArrayNumericConditionBuilder } from "./array-numeric-condition-builder.js";
|
|
4
4
|
import { ArrayUuidConditionBuilder, ArrayDateConditionBuilder, ArrayJsonbConditionBuilder } from "./array-specialized-condition-builder.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import format from "
|
|
1
|
+
import format from "../addon/pg-format/index.js";
|
|
2
2
|
import { JsonbConditionCollector, buildJsonbConditionSQL } from "./jsonb-condition-builder.js";
|
|
3
3
|
import { ArrayConditionCollector, buildArrayConditionSQL } from "./array-condition-builder.js";
|
|
4
4
|
import { FulltextConditionCollector, buildFulltextConditionSQL } from "./fulltext-condition-builder.js";
|
|
@@ -22,7 +22,7 @@ import { ExplainBuilder } from "../explain/explain-builder.js";
|
|
|
22
22
|
import { ListenBuilder, UnlistenBuilder, NotifyBuilder } from "../pubsub/listen-notify-builder.js";
|
|
23
23
|
import { VacuumBuilder, AnalyzeBuilder } from "../maintenance/vacuum-builder.js";
|
|
24
24
|
import { CopyToBuilder, CopyFromBuilder } from "../copy/copy-builder.js";
|
|
25
|
-
import format from "
|
|
25
|
+
import format from "../addon/pg-format/index.js";
|
|
26
26
|
export class TypedSelectBuilder {
|
|
27
27
|
builder;
|
|
28
28
|
constructor(builder) {
|