Skip to content

Commit 45a40c2

Browse files
committed
fix: some runtime bugs
1 parent b9f403b commit 45a40c2

File tree

4 files changed

+118
-75
lines changed

4 files changed

+118
-75
lines changed

packages/cli/src/actions/db.ts

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -69,59 +69,64 @@ async function runPush(options: PushOptions) {
6969
}
7070

7171
async function runPull(options: PullOptions) {
72-
const schemaFile = getSchemaFile(options.schema);
73-
const { model, services } = await loadSchemaDocumentWithServices(schemaFile);
74-
config();
75-
const SUPPORTED_PROVIDERS = ['sqlite', 'postgresql'];
76-
const datasource = getDatasource(model);
77-
78-
if (!datasource) {
79-
throw new Error('No datasource found in the schema.');
80-
}
72+
try {
73+
const schemaFile = getSchemaFile(options.schema);
74+
const { model, services } = await loadSchemaDocumentWithServices(schemaFile);
75+
config();
76+
const SUPPORTED_PROVIDERS = ['sqlite', 'postgresql'];
77+
const datasource = getDatasource(model);
78+
79+
if (!datasource) {
80+
throw new Error('No datasource found in the schema.');
81+
}
8182

82-
if (!SUPPORTED_PROVIDERS.includes(datasource.provider)) {
83-
throw new Error(`Unsupported datasource provider: ${datasource.provider}`);
84-
}
83+
if (!SUPPORTED_PROVIDERS.includes(datasource.provider)) {
84+
throw new Error(`Unsupported datasource provider: ${datasource.provider}`);
85+
}
8586

86-
const provider = providers[datasource.provider];
87+
const provider = providers[datasource.provider];
8788

88-
if (!provider) {
89-
throw new Error(`No introspection provider found for: ${datasource.provider}`);
90-
}
89+
if (!provider) {
90+
throw new Error(`No introspection provider found for: ${datasource.provider}`);
91+
}
9192

92-
const { enums, tables } = await provider.introspect(datasource.url);
93+
const { enums, tables } = await provider.introspect(datasource.url);
9394

94-
const newModel: Model = {
95-
$type: 'Model',
96-
$container: undefined,
97-
$containerProperty: undefined,
98-
$containerIndex: undefined,
99-
declarations: [...model.declarations.filter((d) => ['DataSource'].includes(d.$type))],
100-
imports: [],
101-
};
95+
const newModel: Model = {
96+
$type: 'Model',
97+
$container: undefined,
98+
$containerProperty: undefined,
99+
$containerIndex: undefined,
100+
declarations: [...model.declarations.filter((d) => ['DataSource'].includes(d.$type))],
101+
imports: [],
102+
};
102103

103-
syncEnums({ dbEnums: enums, model: newModel, services, options });
104+
syncEnums({ dbEnums: enums, model: newModel, services, options });
104105

105-
const resolvedRelations: Relation[] = [];
106-
for (const table of tables) {
107-
const relations = syncTable({ table, model: newModel, provider, services, options });
108-
resolvedRelations.push(...relations);
109-
}
106+
const resolvedRelations: Relation[] = [];
107+
for (const table of tables) {
108+
const relations = syncTable({ table, model: newModel, provider, services, options });
109+
resolvedRelations.push(...relations);
110+
}
110111

111-
for (const relation of resolvedRelations) {
112-
syncRelation({ model: newModel, relation, services, options });
113-
}
112+
for (const relation of resolvedRelations) {
113+
syncRelation({ model: newModel, relation, services, options });
114+
}
114115

115-
//TODO: diff models and apply changes only
116+
//TODO: diff models and apply changes only
116117

117-
const generator = new ZModelCodeGenerator();
118+
const generator = new ZModelCodeGenerator();
118119

119-
const zmodelSchema = generator.generate(newModel);
120+
const zmodelSchema = generator.generate(newModel);
120121

121-
console.log(options.out ? `Writing to ${options.out}` : schemaFile);
122+
console.log(options.out ? `Writing to ${options.out}` : schemaFile);
122123

123-
const outPath = options.out ? path.resolve(options.out) : schemaFile;
124-
console.log(outPath);
124+
const outPath = options.out ? path.resolve(options.out) : schemaFile;
125+
console.log(outPath);
125126

126-
fs.writeFileSync(outPath, zmodelSchema);
127+
fs.writeFileSync(outPath, zmodelSchema);
128+
} catch (error) {
129+
console.log(error);
130+
throw error;
131+
}
127132
}

packages/cli/src/actions/pull/index.ts

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -151,26 +151,13 @@ export function syncTable({
151151

152152
const modelFactory = new DataModelFactory().setName(name).setIsView(table.type === 'view');
153153
modelFactory.setContainer(model);
154+
154155
if (modified) {
155156
modelFactory.addAttribute((builder) =>
156157
builder.setDecl(tableMapAttribute).addArg((argBuilder) => argBuilder.StringLiteral.setValue(table.name)),
157158
);
158159
}
159160

160-
if (multiPk) {
161-
const pkColumns = table.columns.filter((c) => c.pk).map((c) => c.name);
162-
modelFactory.addAttribute((builder) =>
163-
builder.setDecl(modelIdAttribute).addArg((argBuilder) => {
164-
const arrayExpr = argBuilder.ArrayExpr;
165-
pkColumns.map((c) => {
166-
const ref = modelFactory.node.fields.find((f) => getDbName(f) === c)!;
167-
arrayExpr.addItem((itemBuilder) => itemBuilder.ReferenceExpr.setTarget(ref));
168-
});
169-
return arrayExpr;
170-
}),
171-
);
172-
}
173-
174161
table.columns.forEach((column) => {
175162
if (column.foreign_key_table) {
176163
relations.push({
@@ -231,7 +218,7 @@ export function syncTable({
231218
enums: model.declarations.filter((d) => d.$type === 'Enum') as Enum[],
232219
})
233220
: [];
234-
defaultValuesAttrs.forEach(builder.addAttribute);
221+
defaultValuesAttrs.forEach(builder.addAttribute.bind(builder));
235222
}
236223

237224
if (column.pk && !multiPk) {
@@ -254,12 +241,12 @@ export function syncTable({
254241
});
255242
});
256243

257-
const uniqieColumns = table.columns.filter((c) => c.unique && !c.pk).map((c) => c.name);
258-
if (uniqieColumns.length > 0) {
244+
const pkColumns = table.columns.filter((c) => c.pk).map((c) => c.name);
245+
if (multiPk) {
259246
modelFactory.addAttribute((builder) =>
260-
builder.setDecl(modelUniqueAttribute).addArg((argBuilder) => {
247+
builder.setDecl(modelIdAttribute).addArg((argBuilder) => {
261248
const arrayExpr = argBuilder.ArrayExpr;
262-
uniqieColumns.map((c) => {
249+
pkColumns.map((c) => {
263250
const ref = modelFactory.node.fields.find((f) => getDbName(f) === c)!;
264251
arrayExpr.addItem((itemBuilder) => itemBuilder.ReferenceExpr.setTarget(ref));
265252
});
@@ -268,21 +255,65 @@ export function syncTable({
268255
);
269256
}
270257

271-
model.declarations.push(modelFactory.node);
272-
273-
table.indexes.forEach((index) => {
258+
const uniqieColumns = table.columns.filter((c) => c.unique && !c.pk).map((c) => c.name);
259+
if (uniqieColumns.length > 0) {
274260
modelFactory.addAttribute((builder) =>
275-
builder.setDecl(modelindexAttribute).addArg((argBuilder) => {
261+
builder.setDecl(modelUniqueAttribute).addArg((argBuilder) => {
276262
const arrayExpr = argBuilder.ArrayExpr;
277-
index.columns.map((c) => {
278-
const ref = modelFactory.node.fields.find((f) => getDbName(f) === c.name)!;
263+
uniqieColumns.map((c) => {
264+
const ref = modelFactory.node.fields.find((f) => getDbName(f) === c)!;
279265
arrayExpr.addItem((itemBuilder) => itemBuilder.ReferenceExpr.setTarget(ref));
280266
});
281267
return arrayExpr;
282268
}),
283269
);
270+
}
271+
272+
table.indexes.forEach((index) => {
273+
if (index.predicate) {
274+
//These constraints are not supported by Zenstack, because Zenstack currently does not fully support check constraints. Read more: https://pris.ly/d/check-constraints
275+
console.log(
276+
'These constraints are not supported by Zenstack. Read more: https://pris.ly/d/check-constraints',
277+
`- Model: "${table.name}", constraint: "${index.name}"`,
278+
);
279+
return;
280+
}
281+
if (index.columns.find((c) => c.expression)) {
282+
console.log(
283+
'These constraints are not supported by Zenstack. Read more: https://pris.ly/d/check-constraints',
284+
`- Model: "${table.name}", constraint: "${index.name}"`,
285+
);
286+
return;
287+
}
288+
289+
if (index.columns.length === 1 && index.columns.find((c) => pkColumns.includes(c.name))) {
290+
//skip primary key
291+
return;
292+
}
293+
294+
modelFactory.addAttribute((builder) =>
295+
builder
296+
.setDecl(index.unique ? modelUniqueAttribute : modelindexAttribute)
297+
.addArg((argBuilder) => {
298+
const arrayExpr = argBuilder.ArrayExpr;
299+
index.columns.map((c) => {
300+
const ref = modelFactory.node.fields.find((f) => getDbName(f) === c.name)!;
301+
if (!ref) console.log(c, table.name);
302+
arrayExpr.addItem((itemBuilder) => {
303+
const refExpr = itemBuilder.ReferenceExpr.setTarget(ref);
304+
if (c.order !== 'ASC') refExpr.addArg((ab) => ab.StringLiteral.setValue('DESC'), 'sort');
305+
306+
return refExpr;
307+
});
308+
});
309+
return arrayExpr;
310+
})
311+
.addArg((argBuilder) => argBuilder.StringLiteral.setValue(index.name), 'map'),
312+
);
284313
});
285314

315+
model.declarations.push(modelFactory.node);
316+
286317
return relations;
287318
}
288319

@@ -327,12 +358,15 @@ export function syncRelation({
327358
const fieldPrefix = /[0-9]/g.test(sourceModel.name.charAt(0)) ? '_' : '';
328359

329360
const relationName = `${sourceModel.name}_${relation.column}To${targetModel.name}_${relation.references.column}`;
361+
let sourceFieldName = `${fieldPrefix}${sourceModel.name.charAt(0).toLowerCase()}${sourceModel.name.slice(1)}_${relation.column}`;
362+
363+
if (sourceModel.fields.find((f) => f.name === sourceFieldName)) {
364+
sourceFieldName = `${sourceFieldName}To${targetModel.name.charAt(0).toLowerCase()}${targetModel.name.slice(1)}_${relation.references.column}`;
365+
}
330366

331367
const sourceFieldFactory = new DataFieldFactory()
332368
.setContainer(sourceModel)
333-
.setName(
334-
`${fieldPrefix}${sourceModel.name.charAt(0).toLowerCase()}${sourceModel.name.slice(1)}_${relation.column}`,
335-
)
369+
.setName(sourceFieldName)
336370
.setType((tb) =>
337371
tb
338372
.setOptional(relation.nullable)
@@ -345,7 +379,7 @@ export function syncRelation({
345379
.addArg((ab) => ab.StringLiteral.setValue(relationName))
346380
.addArg((ab) => ab.ArrayExpr.addItem((aeb) => aeb.ReferenceExpr.setTarget(sourceField)), 'fields')
347381
.addArg((ab) => ab.ArrayExpr.addItem((aeb) => aeb.ReferenceExpr.setTarget(targetField)), 'references')
348-
.addArg((ab) => ab.ArrayExpr.addItem((aeb) => aeb.StringLiteral.setValue(relation.fk_name)), 'map'),
382+
.addArg((ab) => ab.StringLiteral.setValue(relation.fk_name), 'map'),
349383
);
350384

351385
sourceModel.fields.push(sourceFieldFactory.node);

packages/language/src/factory/attribute.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export class DataFieldAttributeFactory extends AstFactory<DataFieldAttribute> {
1818
args: AttributeArgFactory[] = [];
1919
decl?: Reference<Attribute>;
2020
constructor() {
21-
super({ type: DataFieldAttribute });
21+
super({ type: DataFieldAttribute, node: { args: [] } });
2222
}
2323
setDecl(decl: Attribute) {
2424
this.decl = {
@@ -47,7 +47,7 @@ export class DataModelAttributeFactory extends AstFactory<DataModelAttribute> {
4747
args: AttributeArgFactory[] = [];
4848
decl?: Reference<Attribute>;
4949
constructor() {
50-
super({ type: DataModelAttribute });
50+
super({ type: DataModelAttribute, node: { args: [] } });
5151
}
5252
setDecl(decl: Attribute) {
5353
this.decl = {
@@ -102,7 +102,7 @@ export class InternalAttributeFactory extends AstFactory<InternalAttribute> {
102102
args: AttributeArgFactory[] = [];
103103

104104
constructor() {
105-
super({ type: InternalAttribute });
105+
super({ type: InternalAttribute, node: { args: [] } });
106106
}
107107

108108
setDecl(decl: Attribute) {

packages/language/src/factory/expression.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,12 @@ export class ReferenceExprFactory extends AstFactory<ReferenceExpr> {
110110
return this;
111111
}
112112

113-
addArg(builder: (a: ReferenceArgFactory) => ReferenceArgFactory) {
114-
this.args.push(builder(new ReferenceArgFactory()));
113+
addArg(builder: (a: ExpressionBuilder) => AstFactory<Expression>, name?: string) {
114+
const arg = new ReferenceArgFactory().setValue(builder);
115+
if (name) {
116+
arg.setName(name);
117+
}
118+
this.args.push(arg);
115119
this.update({
116120
args: this.args,
117121
});

0 commit comments

Comments
 (0)