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,7 +1,7 @@
|
|
|
1
1
|
import { requireValidConfig } from "../utils/config-loader.js";
|
|
2
2
|
import { getConnectionDescription } from "../utils/env-loader.js";
|
|
3
3
|
import { colors, createSpinner } from "../utils/spinner.js";
|
|
4
|
-
import { isInitialized, getHead, loadCommit, saveCommit, setFetchHead,
|
|
4
|
+
import { isInitialized, getHead, loadCommit, saveCommit, setFetchHead, fetchRemoteCommits, ensureRemoteTable, } from "../utils/repo-manager.js";
|
|
5
5
|
export async function fetchCommand(context) {
|
|
6
6
|
const { config, flags } = context;
|
|
7
7
|
if (!config) {
|
|
@@ -41,10 +41,25 @@ export async function fetchCommand(context) {
|
|
|
41
41
|
setFetchHead(latestRemote.hash, projectRoot);
|
|
42
42
|
spinner.succeed(`Fetched ${remoteCommits.length} commits (${newCommits} new)`);
|
|
43
43
|
const localHead = getHead(projectRoot);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
const localCommits = localHead ? getAllLocalCommitHashes(projectRoot) : new Set();
|
|
45
|
+
const remoteHashes = new Set(remoteCommits.map(c => c.hash));
|
|
46
|
+
const behind = remoteCommits.filter(c => !localCommits.has(c.hash)).length;
|
|
47
|
+
const ahead = [...localCommits].filter(h => !remoteHashes.has(h)).length;
|
|
48
|
+
console.log('');
|
|
49
|
+
if (behind > 0 && ahead > 0) {
|
|
50
|
+
console.log(`Your branch and 'origin/main' have diverged.`);
|
|
51
|
+
console.log(` ${colors.yellow(String(ahead))} commit(s) ahead, ${colors.yellow(String(behind))} commit(s) behind`);
|
|
52
|
+
}
|
|
53
|
+
else if (behind > 0) {
|
|
54
|
+
console.log(`Your branch is ${colors.yellow(String(behind))} commit(s) behind 'origin/main'.`);
|
|
55
|
+
console.log(` ${colors.muted('Use')} ${colors.cyan('relq pull')} ${colors.muted('to update.')}`);
|
|
56
|
+
}
|
|
57
|
+
else if (ahead > 0) {
|
|
58
|
+
console.log(`Your branch is ${colors.green(String(ahead))} commit(s) ahead of 'origin/main'.`);
|
|
59
|
+
console.log(` ${colors.muted('Use')} ${colors.cyan('relq push')} ${colors.muted('to publish.')}`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log(`${colors.green('✓')} Your branch is up to date with 'origin/main'.`);
|
|
48
63
|
}
|
|
49
64
|
}
|
|
50
65
|
catch (error) {
|
|
@@ -54,3 +69,18 @@ export async function fetchCommand(context) {
|
|
|
54
69
|
}
|
|
55
70
|
console.log('');
|
|
56
71
|
}
|
|
72
|
+
function getAllLocalCommitHashes(projectRoot) {
|
|
73
|
+
const fs = require('fs');
|
|
74
|
+
const path = require('path');
|
|
75
|
+
const commitsDir = path.join(projectRoot, '.relq', 'commits');
|
|
76
|
+
const hashes = new Set();
|
|
77
|
+
if (fs.existsSync(commitsDir)) {
|
|
78
|
+
const files = fs.readdirSync(commitsDir);
|
|
79
|
+
for (const file of files) {
|
|
80
|
+
if (file.endsWith('.json')) {
|
|
81
|
+
hashes.add(file.replace('.json', ''));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return hashes;
|
|
86
|
+
}
|
|
@@ -87,7 +87,7 @@ function getMigrationFiles(migrationsDir) {
|
|
|
87
87
|
.sort();
|
|
88
88
|
}
|
|
89
89
|
async function getAppliedMigrations(connection, tableName) {
|
|
90
|
-
const { Pool } = await import("../../addon/pg.js");
|
|
90
|
+
const { Pool } = await import("../../addon/pg/index.js");
|
|
91
91
|
const pool = new Pool({
|
|
92
92
|
host: connection.host,
|
|
93
93
|
port: connection.port || 5432,
|
|
@@ -5,6 +5,7 @@ import { generateTypeScript } from "../utils/type-generator.js";
|
|
|
5
5
|
import { saveSnapshot, loadSnapshot, isInitialized, initRepository, stageChanges, addUnstagedChanges } from "../utils/repo-manager.js";
|
|
6
6
|
import { compareSchemas } from "../utils/schema-comparator.js";
|
|
7
7
|
import { getChangeDisplayName } from "../utils/change-tracker.js";
|
|
8
|
+
import { loadRelqignore, validateIgnoreDependencies, isTableIgnored, isColumnIgnored, isIndexIgnored, isConstraintIgnored, isEnumIgnored, isDomainIgnored, isSequenceIgnored, isCompositeTypeIgnored, isFunctionIgnored, } from "../utils/relqignore.js";
|
|
8
9
|
import { colors, error, fatal, warning, hint, getWorkingTreeStatus, printDirtyWorkingTreeError, printMergeStrategyHelp, readSQLFile, createSpinner, formatBytes, } from "../utils/git-utils.js";
|
|
9
10
|
export async function importCommand(sqlFilePath, options = {}) {
|
|
10
11
|
const { includeFunctions = false, includeTriggers = false, force = false, dryRun = false } = options;
|
|
@@ -108,26 +109,143 @@ export async function importCommand(sqlFilePath, options = {}) {
|
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
console.log('');
|
|
112
|
+
const ignorePatterns = loadRelqignore(projectRoot);
|
|
113
|
+
let ignoredCount = 0;
|
|
114
|
+
const filteredTables = parsedSchema.tables.filter(table => {
|
|
115
|
+
if (isTableIgnored(table.name, ignorePatterns).ignored) {
|
|
116
|
+
ignoredCount++;
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
}).map(table => {
|
|
121
|
+
const filteredColumns = table.columns.filter(col => {
|
|
122
|
+
if (isColumnIgnored(table.name, col.name, ignorePatterns).ignored) {
|
|
123
|
+
ignoredCount++;
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
});
|
|
128
|
+
const filteredIndexes = table.indexes.filter(idx => {
|
|
129
|
+
if (isIndexIgnored(table.name, idx.name, ignorePatterns).ignored) {
|
|
130
|
+
ignoredCount++;
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
});
|
|
135
|
+
const filteredConstraints = table.constraints.filter(con => {
|
|
136
|
+
if (isConstraintIgnored(table.name, con.name, ignorePatterns).ignored) {
|
|
137
|
+
ignoredCount++;
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
...table,
|
|
144
|
+
columns: filteredColumns,
|
|
145
|
+
indexes: filteredIndexes,
|
|
146
|
+
constraints: filteredConstraints,
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
const filteredEnums = parsedSchema.enums.filter(e => {
|
|
150
|
+
if (isEnumIgnored(e.name, ignorePatterns).ignored) {
|
|
151
|
+
ignoredCount++;
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
return true;
|
|
155
|
+
});
|
|
156
|
+
const filteredDomains = parsedSchema.domains.filter(d => {
|
|
157
|
+
if (isDomainIgnored(d.name, ignorePatterns).ignored) {
|
|
158
|
+
ignoredCount++;
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
return true;
|
|
162
|
+
});
|
|
163
|
+
const filteredCompositeTypes = parsedSchema.compositeTypes.filter(c => {
|
|
164
|
+
if (isCompositeTypeIgnored(c.name, ignorePatterns).ignored) {
|
|
165
|
+
ignoredCount++;
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
});
|
|
170
|
+
const filteredSequences = parsedSchema.sequences.filter(s => {
|
|
171
|
+
if (isSequenceIgnored(s.name, ignorePatterns).ignored) {
|
|
172
|
+
ignoredCount++;
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
return true;
|
|
176
|
+
});
|
|
177
|
+
const filteredFunctions = functions.filter(f => {
|
|
178
|
+
if (isFunctionIgnored(f.name, ignorePatterns).ignored) {
|
|
179
|
+
ignoredCount++;
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
});
|
|
184
|
+
const filteredSchema = {
|
|
185
|
+
tables: filteredTables,
|
|
186
|
+
enums: filteredEnums,
|
|
187
|
+
domains: filteredDomains,
|
|
188
|
+
compositeTypes: filteredCompositeTypes,
|
|
189
|
+
sequences: filteredSequences,
|
|
190
|
+
collations: parsedSchema.collations,
|
|
191
|
+
foreignTables: parsedSchema.foreignTables,
|
|
192
|
+
views: parsedSchema.views,
|
|
193
|
+
materializedViews: parsedSchema.materializedViews,
|
|
194
|
+
foreignServers: parsedSchema.foreignServers,
|
|
195
|
+
extensions: parsedSchema.extensions,
|
|
196
|
+
partitions: parsedSchema.partitions,
|
|
197
|
+
};
|
|
198
|
+
if (ignoredCount > 0) {
|
|
199
|
+
console.log(`${colors.gray(`ℹ ${ignoredCount} object(s) ignored by .relqignore`)}`);
|
|
200
|
+
}
|
|
201
|
+
const dependencyErrors = validateIgnoreDependencies({
|
|
202
|
+
tables: filteredTables.map(t => ({
|
|
203
|
+
name: t.name,
|
|
204
|
+
columns: t.columns.map(c => ({
|
|
205
|
+
name: c.name,
|
|
206
|
+
type: c.dataType,
|
|
207
|
+
default: c.defaultValue,
|
|
208
|
+
})),
|
|
209
|
+
})),
|
|
210
|
+
enums: parsedSchema.enums,
|
|
211
|
+
domains: parsedSchema.domains,
|
|
212
|
+
sequences: parsedSchema.sequences,
|
|
213
|
+
compositeTypes: parsedSchema.compositeTypes,
|
|
214
|
+
}, ignorePatterns);
|
|
215
|
+
if (dependencyErrors.length > 0) {
|
|
216
|
+
spinner.stop();
|
|
217
|
+
error('Dependency validation failed:');
|
|
218
|
+
console.log('');
|
|
219
|
+
for (const depError of dependencyErrors) {
|
|
220
|
+
console.log(` ${colors.red('✗')} ${depError.message}`);
|
|
221
|
+
}
|
|
222
|
+
console.log('');
|
|
223
|
+
hint('Either un-ignore the type or add the column to .relqignore');
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
111
226
|
spinner.start('Generating TypeScript schema');
|
|
112
|
-
const dbSchema = convertToDbSchema(
|
|
227
|
+
const dbSchema = convertToDbSchema(filteredSchema, filteredFunctions, triggers, comments);
|
|
113
228
|
const typescriptContent = generateTypeScript(dbSchema, {
|
|
114
229
|
camelCase: true,
|
|
115
230
|
includeFunctions,
|
|
116
231
|
includeTriggers,
|
|
117
232
|
});
|
|
118
233
|
spinner.succeed('Generated TypeScript schema');
|
|
119
|
-
const
|
|
234
|
+
const incomingSchema = convertToNormalizedSchema(filteredSchema, filteredFunctions, triggers);
|
|
120
235
|
const existingSnapshot = loadSnapshot(projectRoot);
|
|
236
|
+
const replaceAll = options.theirs === true;
|
|
237
|
+
let mergedSchema;
|
|
121
238
|
let changes = [];
|
|
122
239
|
if (existingSnapshot) {
|
|
123
240
|
spinner.start('Detecting changes');
|
|
241
|
+
mergedSchema = mergeSchemas(existingSnapshot, incomingSchema, replaceAll);
|
|
124
242
|
const beforeSchema = snapshotToDbSchema(existingSnapshot);
|
|
125
|
-
const afterSchema = snapshotToDbSchema(
|
|
243
|
+
const afterSchema = snapshotToDbSchema(mergedSchema);
|
|
126
244
|
changes = compareSchemas(beforeSchema, afterSchema);
|
|
127
245
|
spinner.stop();
|
|
128
246
|
if (changes.length > 0) {
|
|
129
247
|
console.log('');
|
|
130
|
-
console.log(`${colors.bold('Changes detected:')}`);
|
|
248
|
+
console.log(`${colors.bold('Changes detected:')}${replaceAll ? colors.yellow(' (--theirs: full replacement)') : ''}`);
|
|
131
249
|
console.log('');
|
|
132
250
|
const creates = changes.filter(c => c.type === 'CREATE');
|
|
133
251
|
const alters = changes.filter(c => c.type === 'ALTER');
|
|
@@ -149,11 +267,17 @@ export async function importCommand(sqlFilePath, options = {}) {
|
|
|
149
267
|
}
|
|
150
268
|
}
|
|
151
269
|
if (drops.length > 0) {
|
|
152
|
-
|
|
153
|
-
|
|
270
|
+
if (replaceAll) {
|
|
271
|
+
for (const change of drops.slice(0, 10)) {
|
|
272
|
+
console.log(`\t${colors.red(getChangeDisplayName(change))}`);
|
|
273
|
+
}
|
|
274
|
+
if (drops.length > 10) {
|
|
275
|
+
console.log(`\t${colors.gray(`... and ${drops.length - 10} more deletions`)}`);
|
|
276
|
+
}
|
|
154
277
|
}
|
|
155
|
-
|
|
156
|
-
console.log(
|
|
278
|
+
else {
|
|
279
|
+
console.log('');
|
|
280
|
+
console.log(`${colors.gray(`ℹ ${drops.length} object(s) only in existing schema (preserved)`)}`);
|
|
157
281
|
}
|
|
158
282
|
}
|
|
159
283
|
console.log('');
|
|
@@ -165,15 +289,22 @@ export async function importCommand(sqlFilePath, options = {}) {
|
|
|
165
289
|
}
|
|
166
290
|
}
|
|
167
291
|
else {
|
|
292
|
+
mergedSchema = incomingSchema;
|
|
168
293
|
changes = [];
|
|
169
294
|
console.log(`${colors.cyan('First import')} - creating initial snapshot`);
|
|
170
295
|
}
|
|
296
|
+
const mergedDbSchema = snapshotToDbSchemaForGeneration(mergedSchema);
|
|
297
|
+
const finalTypescriptContent = generateTypeScript(mergedDbSchema, {
|
|
298
|
+
camelCase: true,
|
|
299
|
+
includeFunctions,
|
|
300
|
+
includeTriggers,
|
|
301
|
+
});
|
|
171
302
|
if (dryRun) {
|
|
172
303
|
console.log('');
|
|
173
304
|
console.log(`${colors.yellow('Dry run mode')} - no files written`);
|
|
174
305
|
console.log('');
|
|
175
306
|
console.log('Would write:');
|
|
176
|
-
console.log(` ${colors.cyan(options.output || './db/schema.ts')} ${colors.gray(`(${formatBytes(
|
|
307
|
+
console.log(` ${colors.cyan(options.output || './db/schema.ts')} ${colors.gray(`(${formatBytes(finalTypescriptContent.length)})`)}`);
|
|
177
308
|
console.log(` ${colors.cyan('.relq/snapshot.json')}`);
|
|
178
309
|
if (changes.length > 0) {
|
|
179
310
|
console.log(` Stage ${changes.length} change(s)`);
|
|
@@ -187,9 +318,9 @@ export async function importCommand(sqlFilePath, options = {}) {
|
|
|
187
318
|
if (!fs.existsSync(outputDir)) {
|
|
188
319
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
189
320
|
}
|
|
190
|
-
fs.writeFileSync(absoluteOutputPath,
|
|
191
|
-
console.log(`Written ${colors.cyan(outputPath)} ${colors.gray(`(${formatBytes(
|
|
192
|
-
saveSnapshot(
|
|
321
|
+
fs.writeFileSync(absoluteOutputPath, finalTypescriptContent, 'utf-8');
|
|
322
|
+
console.log(`Written ${colors.cyan(outputPath)} ${colors.gray(`(${formatBytes(finalTypescriptContent.length)})`)}`);
|
|
323
|
+
saveSnapshot(mergedSchema, projectRoot);
|
|
193
324
|
if (changes.length > 0) {
|
|
194
325
|
addUnstagedChanges(changes, projectRoot);
|
|
195
326
|
stageChanges(['.'], projectRoot);
|
|
@@ -406,6 +537,17 @@ function convertToNormalizedSchema(parsed, functions = [], triggers = []) {
|
|
|
406
537
|
forEach: 'STATEMENT',
|
|
407
538
|
functionName: t.functionName || '',
|
|
408
539
|
})),
|
|
540
|
+
views: parsed.views.map(v => ({
|
|
541
|
+
name: v.name,
|
|
542
|
+
schema: v.schema || 'public',
|
|
543
|
+
definition: v.definition,
|
|
544
|
+
})),
|
|
545
|
+
materializedViews: parsed.materializedViews.map(mv => ({
|
|
546
|
+
name: mv.name,
|
|
547
|
+
schema: mv.schema || 'public',
|
|
548
|
+
definition: mv.definition,
|
|
549
|
+
withData: mv.withData ?? true,
|
|
550
|
+
})),
|
|
409
551
|
};
|
|
410
552
|
}
|
|
411
553
|
function snapshotToDbSchema(snapshot) {
|
|
@@ -485,4 +627,133 @@ function snapshotToDbSchema(snapshot) {
|
|
|
485
627
|
sequences: [],
|
|
486
628
|
});
|
|
487
629
|
}
|
|
630
|
+
function snapshotToDbSchemaForGeneration(snapshot) {
|
|
631
|
+
return {
|
|
632
|
+
tables: snapshot.tables.map(t => ({
|
|
633
|
+
name: t.name,
|
|
634
|
+
schema: t.schema || 'public',
|
|
635
|
+
comment: null,
|
|
636
|
+
columns: t.columns.map(c => ({
|
|
637
|
+
name: c.name,
|
|
638
|
+
dataType: c.type,
|
|
639
|
+
udtName: c.type,
|
|
640
|
+
isNullable: c.nullable,
|
|
641
|
+
defaultValue: c.default ?? null,
|
|
642
|
+
isPrimaryKey: c.primaryKey || false,
|
|
643
|
+
isUnique: c.unique || false,
|
|
644
|
+
maxLength: null,
|
|
645
|
+
precision: null,
|
|
646
|
+
scale: null,
|
|
647
|
+
references: null,
|
|
648
|
+
comment: null,
|
|
649
|
+
isGenerated: c.isGenerated || false,
|
|
650
|
+
generationExpression: c.generationExpression || null,
|
|
651
|
+
identityGeneration: c.identity?.type || null,
|
|
652
|
+
})),
|
|
653
|
+
indexes: t.indexes.map(i => ({
|
|
654
|
+
name: i.name,
|
|
655
|
+
columns: Array.isArray(i.columns) ? i.columns : [i.columns],
|
|
656
|
+
isUnique: i.unique || false,
|
|
657
|
+
isPrimary: false,
|
|
658
|
+
type: i.type || 'btree',
|
|
659
|
+
definition: i.definition,
|
|
660
|
+
whereClause: i.whereClause,
|
|
661
|
+
comment: null,
|
|
662
|
+
})),
|
|
663
|
+
constraints: t.constraints?.map(c => ({
|
|
664
|
+
name: c.name,
|
|
665
|
+
type: c.type,
|
|
666
|
+
columns: [],
|
|
667
|
+
definition: c.definition,
|
|
668
|
+
})) || [],
|
|
669
|
+
rowCount: 0,
|
|
670
|
+
isPartitioned: t.isPartitioned || false,
|
|
671
|
+
partitionType: t.partitionType,
|
|
672
|
+
partitionKey: t.partitionKey ? [t.partitionKey].flat() : undefined,
|
|
673
|
+
})),
|
|
674
|
+
enums: snapshot.enums?.map(e => ({
|
|
675
|
+
name: e.name,
|
|
676
|
+
values: e.values,
|
|
677
|
+
})) || [],
|
|
678
|
+
domains: snapshot.domains?.map(d => ({
|
|
679
|
+
name: d.name,
|
|
680
|
+
baseType: d.baseType,
|
|
681
|
+
isNotNull: d.notNull || false,
|
|
682
|
+
defaultValue: d.default || null,
|
|
683
|
+
checkExpression: d.check || null,
|
|
684
|
+
check: d.check,
|
|
685
|
+
default: d.default,
|
|
686
|
+
notNull: d.notNull,
|
|
687
|
+
})) || [],
|
|
688
|
+
compositeTypes: snapshot.compositeTypes?.map(c => ({
|
|
689
|
+
name: c.name,
|
|
690
|
+
attributes: c.attributes || [],
|
|
691
|
+
})) || [],
|
|
692
|
+
sequences: snapshot.sequences?.map(s => ({
|
|
693
|
+
name: s.name,
|
|
694
|
+
dataType: s.dataType,
|
|
695
|
+
start: s.startValue,
|
|
696
|
+
increment: s.increment,
|
|
697
|
+
minValue: s.minValue,
|
|
698
|
+
maxValue: s.maxValue,
|
|
699
|
+
cache: s.cache,
|
|
700
|
+
cycle: s.cycle,
|
|
701
|
+
ownedBy: s.ownedBy ?? undefined,
|
|
702
|
+
})) || [],
|
|
703
|
+
extensions: (snapshot.extensions || []).map(e => typeof e === 'string' ? e : e.name),
|
|
704
|
+
partitions: [],
|
|
705
|
+
functions: snapshot.functions?.map(f => ({
|
|
706
|
+
name: f.name,
|
|
707
|
+
schema: 'public',
|
|
708
|
+
returnType: f.returnType,
|
|
709
|
+
argTypes: f.argTypes || [],
|
|
710
|
+
language: f.language,
|
|
711
|
+
definition: '',
|
|
712
|
+
isAggregate: false,
|
|
713
|
+
volatility: 'VOLATILE',
|
|
714
|
+
})) || [],
|
|
715
|
+
triggers: snapshot.triggers?.map(t => ({
|
|
716
|
+
name: t.name,
|
|
717
|
+
tableName: t.table,
|
|
718
|
+
timing: t.timing,
|
|
719
|
+
event: t.events?.[0] || '',
|
|
720
|
+
functionName: t.functionName || '',
|
|
721
|
+
definition: '',
|
|
722
|
+
isEnabled: t.isEnabled ?? true,
|
|
723
|
+
})) || [],
|
|
724
|
+
policies: [],
|
|
725
|
+
foreignServers: [],
|
|
726
|
+
foreignTables: [],
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
function mergeSchemas(existing, incoming, replaceAll = false) {
|
|
730
|
+
if (replaceAll) {
|
|
731
|
+
return incoming;
|
|
732
|
+
}
|
|
733
|
+
function mergeByName(existingArr, incomingArr) {
|
|
734
|
+
const incomingMap = new Map(incomingArr.map(item => [item.name, item]));
|
|
735
|
+
const resultMap = new Map();
|
|
736
|
+
for (const item of existingArr) {
|
|
737
|
+
resultMap.set(item.name, item);
|
|
738
|
+
}
|
|
739
|
+
for (const item of incomingArr) {
|
|
740
|
+
resultMap.set(item.name, item);
|
|
741
|
+
}
|
|
742
|
+
return Array.from(resultMap.values());
|
|
743
|
+
}
|
|
744
|
+
return {
|
|
745
|
+
extensions: mergeByName(existing.extensions || [], incoming.extensions || []),
|
|
746
|
+
enums: mergeByName(existing.enums || [], incoming.enums || []),
|
|
747
|
+
domains: mergeByName(existing.domains || [], incoming.domains || []),
|
|
748
|
+
compositeTypes: mergeByName(existing.compositeTypes || [], incoming.compositeTypes || []),
|
|
749
|
+
sequences: mergeByName(existing.sequences || [], incoming.sequences || []),
|
|
750
|
+
collations: mergeByName(existing.collations || [], incoming.collations || []),
|
|
751
|
+
tables: mergeByName(existing.tables || [], incoming.tables || []),
|
|
752
|
+
functions: mergeByName(existing.functions || [], incoming.functions || []),
|
|
753
|
+
triggers: mergeByName(existing.triggers || [], incoming.triggers || []),
|
|
754
|
+
views: mergeByName(existing.views || [], incoming.views || []),
|
|
755
|
+
materializedViews: mergeByName(existing.materializedViews || [], incoming.materializedViews || []),
|
|
756
|
+
foreignTables: mergeByName(existing.foreignTables || [], incoming.foreignTables || []),
|
|
757
|
+
};
|
|
758
|
+
}
|
|
488
759
|
export default importCommand;
|
|
@@ -56,4 +56,78 @@ export async function logCommand(context) {
|
|
|
56
56
|
console.log('');
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
+
export async function showCommand(context) {
|
|
60
|
+
const { args } = context;
|
|
61
|
+
const projectRoot = process.cwd();
|
|
62
|
+
const target = args[0];
|
|
63
|
+
console.log('');
|
|
64
|
+
if (!isInitialized(projectRoot)) {
|
|
65
|
+
console.log(`${colors.red('fatal:')} not a relq repository`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!target) {
|
|
69
|
+
console.log(`${colors.red('error:')} Please specify a commit or tag`);
|
|
70
|
+
console.log('');
|
|
71
|
+
console.log(`Usage: ${colors.cyan('relq show <commit|tag>')}`);
|
|
72
|
+
console.log('');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const fs = require('fs');
|
|
76
|
+
const path = require('path');
|
|
77
|
+
const { resolveRef, loadCommit } = require('../utils/repo-manager');
|
|
78
|
+
const hash = resolveRef(target, projectRoot);
|
|
79
|
+
if (!hash) {
|
|
80
|
+
console.log(`${colors.red('error:')} Commit or tag not found: ${target}`);
|
|
81
|
+
console.log('');
|
|
82
|
+
console.log('Available commits:');
|
|
83
|
+
const commitsDir = path.join(projectRoot, '.relq', 'commits');
|
|
84
|
+
if (fs.existsSync(commitsDir)) {
|
|
85
|
+
const files = fs.readdirSync(commitsDir);
|
|
86
|
+
for (const f of files.slice(0, 5)) {
|
|
87
|
+
console.log(` ${colors.yellow(shortHash(f.replace('.json', '')))}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const commitPath = path.join(projectRoot, '.relq', 'commits', `${hash}.json`);
|
|
93
|
+
if (!fs.existsSync(commitPath)) {
|
|
94
|
+
console.log(`${colors.red('error:')} Commit not found: ${hash}`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const commitData = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
|
|
98
|
+
console.log(`${colors.yellow(`commit ${commitData.hash}`)}`);
|
|
99
|
+
console.log(`Author: ${commitData.author}`);
|
|
100
|
+
console.log(`Date: ${formatDate(commitData.timestamp)}`);
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log(` ${commitData.message}`);
|
|
103
|
+
console.log('');
|
|
104
|
+
if (commitData.stats) {
|
|
105
|
+
console.log(`${colors.bold('Stats:')}`);
|
|
106
|
+
console.log(` Tables: ${commitData.stats.tables}`);
|
|
107
|
+
console.log(` Columns: ${commitData.stats.columns}`);
|
|
108
|
+
console.log(` Indexes: ${commitData.stats.indexes || 0}`);
|
|
109
|
+
console.log('');
|
|
110
|
+
}
|
|
111
|
+
if (commitData.changes && commitData.changes.length > 0) {
|
|
112
|
+
console.log(`${colors.bold('Changes:')}`);
|
|
113
|
+
for (const change of commitData.changes.slice(0, 20)) {
|
|
114
|
+
const symbol = change.action === 'CREATE'
|
|
115
|
+
? colors.green('+')
|
|
116
|
+
: change.action === 'DROP'
|
|
117
|
+
? colors.red('-')
|
|
118
|
+
: colors.yellow('~');
|
|
119
|
+
console.log(` ${symbol} ${change.objectType.toLowerCase()}: ${change.name}`);
|
|
120
|
+
}
|
|
121
|
+
if (commitData.changes.length > 20) {
|
|
122
|
+
console.log(` ${colors.muted(`... and ${commitData.changes.length - 20} more`)}`);
|
|
123
|
+
}
|
|
124
|
+
console.log('');
|
|
125
|
+
}
|
|
126
|
+
if (commitData.sql && commitData.sql.trim()) {
|
|
127
|
+
console.log(`${colors.bold('SQL:')}`);
|
|
128
|
+
console.log('');
|
|
129
|
+
console.log(`${colors.cyan(commitData.sql)}`);
|
|
130
|
+
console.log('');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
59
133
|
export { logCommand as historyCommand };
|