agentlang 0.5.0 → 0.5.1
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/out/language/generated/ast.d.ts +21 -3
- package/out/language/generated/ast.d.ts.map +1 -1
- package/out/language/generated/ast.js +26 -2
- package/out/language/generated/ast.js.map +1 -1
- package/out/language/generated/grammar.d.ts.map +1 -1
- package/out/language/generated/grammar.js +375 -202
- package/out/language/generated/grammar.js.map +1 -1
- package/out/language/main.cjs +392 -204
- package/out/language/main.cjs.map +2 -2
- package/out/runtime/defs.d.ts +10 -0
- package/out/runtime/defs.d.ts.map +1 -1
- package/out/runtime/interpreter.d.ts.map +1 -1
- package/out/runtime/interpreter.js +17 -2
- package/out/runtime/interpreter.js.map +1 -1
- package/out/runtime/module.d.ts +2 -0
- package/out/runtime/module.d.ts.map +1 -1
- package/out/runtime/module.js +33 -2
- package/out/runtime/module.js.map +1 -1
- package/out/runtime/modules/auth.js +1 -1
- package/out/runtime/resolvers/interface.d.ts +2 -1
- package/out/runtime/resolvers/interface.d.ts.map +1 -1
- package/out/runtime/resolvers/interface.js +2 -2
- package/out/runtime/resolvers/interface.js.map +1 -1
- package/out/runtime/resolvers/sqldb/database.d.ts +1 -0
- package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/database.js +56 -8
- package/out/runtime/resolvers/sqldb/database.js.map +1 -1
- package/out/runtime/resolvers/sqldb/dbutil.d.ts +2 -0
- package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/dbutil.js +26 -13
- package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.d.ts +3 -1
- package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.js +34 -5
- package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
- package/out/syntaxes/agentlang.monarch.js +1 -1
- package/out/syntaxes/agentlang.monarch.js.map +1 -1
- package/package.json +1 -1
- package/src/language/agentlang.langium +10 -3
- package/src/language/generated/ast.ts +54 -4
- package/src/language/generated/grammar.ts +375 -202
- package/src/runtime/defs.ts +11 -0
- package/src/runtime/interpreter.ts +25 -1
- package/src/runtime/module.ts +35 -2
- package/src/runtime/modules/auth.ts +1 -1
- package/src/runtime/resolvers/interface.ts +7 -3
- package/src/runtime/resolvers/sqldb/database.ts +60 -7
- package/src/runtime/resolvers/sqldb/dbutil.ts +114 -74
- package/src/runtime/resolvers/sqldb/impl.ts +53 -6
- package/src/syntaxes/agentlang.monarch.ts +1 -1
package/src/runtime/defs.ts
CHANGED
|
@@ -300,3 +300,14 @@ export class ExecGraphWalker {
|
|
|
300
300
|
return this;
|
|
301
301
|
}
|
|
302
302
|
}
|
|
303
|
+
|
|
304
|
+
export type FkSpec = {
|
|
305
|
+
moduleName: string;
|
|
306
|
+
entityName: string;
|
|
307
|
+
columnName: string;
|
|
308
|
+
targetModuleName: string;
|
|
309
|
+
targetEntityName: string;
|
|
310
|
+
targetColumnName: string;
|
|
311
|
+
onDelete: 'CASCADE' | 'SET NULL' | 'SET DEFAULT' | undefined;
|
|
312
|
+
onUpdate: 'CASCADE' | 'SET NULL' | 'SET DEFAULT' | undefined;
|
|
313
|
+
};
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
isNegExpr,
|
|
15
15
|
isNotExpr,
|
|
16
16
|
isReturn,
|
|
17
|
+
JoinSpec,
|
|
17
18
|
Literal,
|
|
18
19
|
MapKey,
|
|
19
20
|
MapLiteral,
|
|
@@ -1298,7 +1299,11 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
|
|
|
1298
1299
|
if (qattrs === undefined && !isQueryAll) {
|
|
1299
1300
|
throw new Error(`Pattern for ${entryName} with 'into' clause must be a query`);
|
|
1300
1301
|
}
|
|
1301
|
-
|
|
1302
|
+
if (crud.join) {
|
|
1303
|
+
await evaluateJoinQuery(crud.join, crud.into, inst, distinct, env);
|
|
1304
|
+
} else {
|
|
1305
|
+
await evaluateJoinQueryWithRelationships(crud.into, inst, crud.relationships, distinct, env);
|
|
1306
|
+
}
|
|
1302
1307
|
return;
|
|
1303
1308
|
}
|
|
1304
1309
|
if (isEntityInstance(inst) || isBetweenRelationship(inst.name, inst.moduleName)) {
|
|
@@ -1576,6 +1581,25 @@ async function computeExprAttributes(
|
|
|
1576
1581
|
}
|
|
1577
1582
|
|
|
1578
1583
|
async function evaluateJoinQuery(
|
|
1584
|
+
joinSpec: JoinSpec,
|
|
1585
|
+
intoSpec: SelectIntoSpec,
|
|
1586
|
+
inst: Instance,
|
|
1587
|
+
distinct: boolean,
|
|
1588
|
+
env: Environment
|
|
1589
|
+
): Promise<void> {
|
|
1590
|
+
const normIntoSpec = new Map<string, string>();
|
|
1591
|
+
intoSpec.entries.forEach((entry: SelectIntoEntry) => {
|
|
1592
|
+
normIntoSpec.set(entry.alias, entry.attribute);
|
|
1593
|
+
});
|
|
1594
|
+
const resolver = await getResolverForPath(inst.name, inst.moduleName, env);
|
|
1595
|
+
const result: Result = await resolver.queryByJoin(inst, [], normIntoSpec, distinct, joinSpec);
|
|
1596
|
+
|
|
1597
|
+
const transformedResult = transformDateFieldsInJoinResult(result);
|
|
1598
|
+
|
|
1599
|
+
env.setLastResult(transformedResult);
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
async function evaluateJoinQueryWithRelationships(
|
|
1579
1603
|
intoSpec: SelectIntoSpec,
|
|
1580
1604
|
inst: Instance,
|
|
1581
1605
|
relationships: RelationshipPattern[],
|
package/src/runtime/module.ts
CHANGED
|
@@ -43,10 +43,12 @@ import {
|
|
|
43
43
|
escapeFqName,
|
|
44
44
|
encryptPassword,
|
|
45
45
|
splitFqName,
|
|
46
|
+
splitRefs,
|
|
47
|
+
forceAsFqName,
|
|
46
48
|
} from './util.js';
|
|
47
49
|
import { parseStatement } from '../language/parser.js';
|
|
48
50
|
import { ActiveSessionInfo, AdminSession } from './auth/defs.js';
|
|
49
|
-
import { FetchModuleFn, PathAttributeName } from './defs.js';
|
|
51
|
+
import { FetchModuleFn, FkSpec, PathAttributeName } from './defs.js';
|
|
50
52
|
import { logger } from './logger.js';
|
|
51
53
|
import { CasePattern, FlowStepPattern } from '../language/syntax.js';
|
|
52
54
|
import {
|
|
@@ -323,7 +325,7 @@ export class Record extends ModuleEntry {
|
|
|
323
325
|
props = new Map();
|
|
324
326
|
}
|
|
325
327
|
props.set('ref', escapeFqName(fp));
|
|
326
|
-
t = '
|
|
328
|
+
t = a.refSpec.type === undefined ? 'Any' : a.refSpec.type;
|
|
327
329
|
}
|
|
328
330
|
const enumValues: string[] | undefined = a.enumSpec?.values;
|
|
329
331
|
const oneOfRef: string | undefined = a.oneOfSpec?.ref;
|
|
@@ -533,6 +535,37 @@ export class Record extends ModuleEntry {
|
|
|
533
535
|
return undefined;
|
|
534
536
|
}
|
|
535
537
|
|
|
538
|
+
getFkAttributeSpecs(): FkSpec[] {
|
|
539
|
+
const result = new Array<FkSpec>();
|
|
540
|
+
this.schema.forEach((attrSpec: AttributeSpec, columnName: string) => {
|
|
541
|
+
const refSpec = getRefSpec(attrSpec);
|
|
542
|
+
if (refSpec) {
|
|
543
|
+
const targetNames = forceAsFqName(refSpec, this.moduleName);
|
|
544
|
+
const parts = splitFqName(targetNames);
|
|
545
|
+
const targetModuleName = parts[0];
|
|
546
|
+
const refs = splitRefs(parts[1]);
|
|
547
|
+
const targetEntityName = refs[0];
|
|
548
|
+
let targetColumnName = '';
|
|
549
|
+
if (refs.length <= 1) {
|
|
550
|
+
targetColumnName = PathAttributeName;
|
|
551
|
+
} else {
|
|
552
|
+
targetColumnName = refs[1];
|
|
553
|
+
}
|
|
554
|
+
result.push({
|
|
555
|
+
moduleName: this.moduleName,
|
|
556
|
+
entityName: this.name,
|
|
557
|
+
columnName,
|
|
558
|
+
targetModuleName,
|
|
559
|
+
targetEntityName,
|
|
560
|
+
targetColumnName,
|
|
561
|
+
onDelete: 'CASCADE',
|
|
562
|
+
onUpdate: 'CASCADE',
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
return result;
|
|
567
|
+
}
|
|
568
|
+
|
|
536
569
|
override toString(): string {
|
|
537
570
|
return this.toString_();
|
|
538
571
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { JoinSpec } from '../../language/generated/ast.js';
|
|
1
2
|
import {
|
|
2
3
|
callPostEventOnSubscription,
|
|
3
4
|
Environment,
|
|
@@ -127,11 +128,14 @@ export class Resolver {
|
|
|
127
128
|
|
|
128
129
|
public async queryByJoin(
|
|
129
130
|
inst: Instance,
|
|
130
|
-
|
|
131
|
+
joinInfo: JoinInfo[],
|
|
131
132
|
intoSpec: Map<string, string>,
|
|
132
|
-
distinct: boolean = false
|
|
133
|
+
distinct: boolean = false,
|
|
134
|
+
rawJoinSpec?: JoinSpec
|
|
133
135
|
): Promise<any> {
|
|
134
|
-
return this.notImpl(
|
|
136
|
+
return this.notImpl(
|
|
137
|
+
`queryByJoin(${inst}, ${joinInfo}, ${intoSpec}, ${distinct} ${rawJoinSpec})`
|
|
138
|
+
);
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
/**
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
DataSource,
|
|
3
|
+
EntityManager,
|
|
4
|
+
EntitySchema,
|
|
5
|
+
QueryRunner,
|
|
6
|
+
SelectQueryBuilder,
|
|
7
|
+
TableForeignKey,
|
|
8
|
+
} from 'typeorm';
|
|
2
9
|
import { logger } from '../../logger.js';
|
|
3
10
|
import {
|
|
11
|
+
asTableReference,
|
|
4
12
|
DefaultVectorDimension,
|
|
5
13
|
modulesAsOrmSchema,
|
|
6
14
|
OwnersSuffix,
|
|
@@ -138,6 +146,7 @@ export type JoinClause = {
|
|
|
138
146
|
tableName: string;
|
|
139
147
|
queryObject?: object;
|
|
140
148
|
queryValues?: object;
|
|
149
|
+
joinType?: string; // 'join' | 'inner join' | 'left join' | 'right join' | 'full join'
|
|
141
150
|
joinOn: JoinOn | JoinOn[];
|
|
142
151
|
};
|
|
143
152
|
|
|
@@ -168,6 +177,10 @@ function makePostgresDataSource(
|
|
|
168
177
|
database: process.env.POSTGRES_DB || config?.dbname || 'postgres',
|
|
169
178
|
synchronize: synchronize,
|
|
170
179
|
entities: entities,
|
|
180
|
+
invalidWhereValuesBehavior: {
|
|
181
|
+
null: 'sql-null',
|
|
182
|
+
undefined: 'ignore',
|
|
183
|
+
},
|
|
171
184
|
});
|
|
172
185
|
}
|
|
173
186
|
|
|
@@ -190,6 +203,10 @@ function makeSqliteDataSource(
|
|
|
190
203
|
database: config?.dbname || mkDbName(),
|
|
191
204
|
synchronize: synchronize,
|
|
192
205
|
entities: entities,
|
|
206
|
+
invalidWhereValuesBehavior: {
|
|
207
|
+
null: 'sql-null',
|
|
208
|
+
undefined: 'ignore',
|
|
209
|
+
},
|
|
193
210
|
});
|
|
194
211
|
}
|
|
195
212
|
|
|
@@ -288,6 +305,20 @@ export async function initDatabase(config: DatabaseConfig | undefined) {
|
|
|
288
305
|
const ormScm = modulesAsOrmSchema();
|
|
289
306
|
defaultDataSource = mkds(ormScm.entities, config) as DataSource;
|
|
290
307
|
await defaultDataSource.initialize();
|
|
308
|
+
if (ormScm.fkSpecs.length > 0) {
|
|
309
|
+
const qr = defaultDataSource.createQueryRunner();
|
|
310
|
+
for (let i = 0; i < ormScm.fkSpecs.length; ++i) {
|
|
311
|
+
const fk = ormScm.fkSpecs[i];
|
|
312
|
+
const fkobj = new TableForeignKey({
|
|
313
|
+
columnNames: [fk.columnName],
|
|
314
|
+
referencedColumnNames: [fk.targetColumnName],
|
|
315
|
+
referencedTableName: asTableReference(fk.targetModuleName, fk.targetEntityName),
|
|
316
|
+
onDelete: fk.onDelete,
|
|
317
|
+
onUpdate: fk.onUpdate,
|
|
318
|
+
});
|
|
319
|
+
await qr.createForeignKey(asTableReference(fk.moduleName, fk.entityName), fkobj);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
291
322
|
const vectEnts = ormScm.vectorEntities.map((es: EntitySchema) => {
|
|
292
323
|
return es.options.name;
|
|
293
324
|
});
|
|
@@ -716,13 +747,25 @@ function mkBetweenClause(tableName: string | undefined, k: string, queryVals: an
|
|
|
716
747
|
function objectToWhereClause(queryObj: object, queryVals: any, tableName?: string): string {
|
|
717
748
|
const clauses: Array<string> = new Array<string>();
|
|
718
749
|
Object.entries(queryObj).forEach((value: [string, any]) => {
|
|
719
|
-
|
|
750
|
+
let op: string = value[1] as string;
|
|
751
|
+
const k = value[0];
|
|
752
|
+
const isnullcheck = queryVals[k] === null;
|
|
753
|
+
if (isnullcheck) {
|
|
754
|
+
if (op === '=') {
|
|
755
|
+
op = 'IS';
|
|
756
|
+
} else if (op === '<>' || op === '!=') {
|
|
757
|
+
op = 'IS NOT';
|
|
758
|
+
} else {
|
|
759
|
+
throw new Error(`Operator ${op} cannot be appplied to SQL NULL`);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
const v = isnullcheck ? 'NULL' : `:${k}`;
|
|
720
763
|
const clause =
|
|
721
764
|
op == 'between'
|
|
722
|
-
? mkBetweenClause(tableName,
|
|
765
|
+
? mkBetweenClause(tableName, k, queryVals)
|
|
723
766
|
: tableName
|
|
724
|
-
? `"${tableName}"."${
|
|
725
|
-
: `"${
|
|
767
|
+
? `"${tableName}"."${k}" ${op} ${v}`
|
|
768
|
+
: `"${k}" ${op} ${v}`;
|
|
726
769
|
clauses.push(clause);
|
|
727
770
|
});
|
|
728
771
|
return clauses.join(' AND ');
|
|
@@ -731,8 +774,17 @@ function objectToWhereClause(queryObj: object, queryVals: any, tableName?: strin
|
|
|
731
774
|
function objectToRawWhereClause(queryObj: object, queryVals: any, tableName?: string): string {
|
|
732
775
|
const clauses: Array<string> = new Array<string>();
|
|
733
776
|
Object.entries(queryObj).forEach((value: [string, any]) => {
|
|
734
|
-
|
|
777
|
+
let op: string = value[1] as string;
|
|
735
778
|
const k: string = value[0];
|
|
779
|
+
if (queryVals[k] === null) {
|
|
780
|
+
if (op === '=') {
|
|
781
|
+
op = 'IS';
|
|
782
|
+
} else if (op === '<>' || op === '!=') {
|
|
783
|
+
op = 'IS NOT';
|
|
784
|
+
} else {
|
|
785
|
+
throw new Error(`Operator ${op} cannot be appplied to SQL NULL`);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
736
788
|
let clause = '';
|
|
737
789
|
if (op == 'between') {
|
|
738
790
|
clause = mkBetweenClause(tableName, k, queryVals);
|
|
@@ -843,8 +895,9 @@ export async function getManyByJoin(
|
|
|
843
895
|
}
|
|
844
896
|
const joinSql = new Array<string>();
|
|
845
897
|
joinClauses.forEach((jc: JoinClause) => {
|
|
898
|
+
const joinType = jc.joinType ? jc.joinType : 'inner join';
|
|
846
899
|
joinSql.push(
|
|
847
|
-
|
|
900
|
+
`${joinType} ${jc.tableName} as ${jc.tableName} on ${joinOnAsSql(jc.joinOn)} AND ${jc.tableName}.${DeletedFlagAttributeName} = false`
|
|
848
901
|
);
|
|
849
902
|
if (jc.queryObject) {
|
|
850
903
|
const q = objectToRawWhereClause(jc.queryObject, jc.queryValues, jc.tableName);
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ColumnType,
|
|
3
|
+
EntitySchema,
|
|
4
|
+
EntitySchemaColumnOptions,
|
|
5
|
+
EntitySchemaOptions,
|
|
6
|
+
TableColumnOptions,
|
|
7
|
+
TableForeignKey,
|
|
8
|
+
TableIndexOptions,
|
|
9
|
+
} from 'typeorm';
|
|
2
10
|
import {
|
|
3
11
|
AttributeSpec,
|
|
4
12
|
fetchModule,
|
|
@@ -21,9 +29,9 @@ import {
|
|
|
21
29
|
} from '../../module.js';
|
|
22
30
|
import { buildGraph } from '../../relgraph.js';
|
|
23
31
|
import { makeFqName } from '../../util.js';
|
|
24
|
-
import { DeletedFlagAttributeName, ParentAttributeName, PathAttributeName } from '../../defs.js';
|
|
32
|
+
import { DeletedFlagAttributeName, FkSpec, ParentAttributeName, PathAttributeName } from '../../defs.js';
|
|
25
33
|
|
|
26
|
-
export const DefaultVectorDimension = 1536
|
|
34
|
+
export const DefaultVectorDimension = 1536;
|
|
27
35
|
|
|
28
36
|
export type TableSchema = {
|
|
29
37
|
name: string;
|
|
@@ -31,14 +39,14 @@ export type TableSchema = {
|
|
|
31
39
|
};
|
|
32
40
|
|
|
33
41
|
export function asTableReference(moduleName: string, ref: string): string {
|
|
34
|
-
const modName = moduleName.replace('.', '_')
|
|
42
|
+
const modName = moduleName.replace('.', '_');
|
|
35
43
|
if (ref.indexOf('.') > 0) {
|
|
36
|
-
const parts = ref.split('.')
|
|
37
|
-
const r = `${modName}_${parts[0]}`.toLowerCase()
|
|
38
|
-
const colref = parts.slice(1).join('.')
|
|
44
|
+
const parts = ref.split('.');
|
|
45
|
+
const r = `${modName}_${parts[0]}`.toLowerCase();
|
|
46
|
+
const colref = parts.slice(1).join('.');
|
|
39
47
|
return `"${r}"."${colref}"`;
|
|
40
48
|
} else {
|
|
41
|
-
return `${modName}_${ref}`.toLowerCase()
|
|
49
|
+
return `${modName}_${ref}`.toLowerCase();
|
|
42
50
|
}
|
|
43
51
|
}
|
|
44
52
|
|
|
@@ -64,123 +72,148 @@ export function modulesAsDbSchema(): TableSchema[] {
|
|
|
64
72
|
}
|
|
65
73
|
|
|
66
74
|
export type OrmSchema = {
|
|
67
|
-
entities: EntitySchema[]
|
|
68
|
-
vectorEntities: EntitySchema[]
|
|
69
|
-
|
|
75
|
+
entities: EntitySchema[];
|
|
76
|
+
vectorEntities: EntitySchema[];
|
|
77
|
+
fkSpecs: FkSpec[];
|
|
78
|
+
};
|
|
70
79
|
|
|
71
80
|
export function modulesAsOrmSchema(): OrmSchema {
|
|
72
81
|
const ents: EntitySchema[] = [];
|
|
73
|
-
const vects: EntitySchema[] = []
|
|
82
|
+
const vects: EntitySchema[] = [];
|
|
83
|
+
const fkSpecs: FkSpec[] = [];
|
|
74
84
|
getModuleNames().forEach((n: string) => {
|
|
75
85
|
buildGraph(n);
|
|
76
86
|
const mod: Module = fetchModule(n);
|
|
77
|
-
const entities: Record[] = mod.getEntityEntries()
|
|
87
|
+
const entities: Record[] = mod.getEntityEntries();
|
|
78
88
|
const rels: Record[] = mod.getBetweenRelationshipEntriesThatNeedStore();
|
|
79
89
|
entities.concat(rels).forEach((entry: Record) => {
|
|
80
|
-
ents.push(new EntitySchema<any>(ormSchemaFromRecordSchema(n, entry)))
|
|
81
|
-
const ownerEntry = createOwnersEntity(entry)
|
|
82
|
-
ents.push(new EntitySchema<any>(ormSchemaFromRecordSchema(n, ownerEntry, true)))
|
|
90
|
+
ents.push(new EntitySchema<any>(ormSchemaFromRecordSchema(n, entry)));
|
|
91
|
+
const ownerEntry = createOwnersEntity(entry);
|
|
92
|
+
ents.push(new EntitySchema<any>(ormSchemaFromRecordSchema(n, ownerEntry, true)));
|
|
83
93
|
if (entry.getFullTextSearchAttributes()) {
|
|
84
|
-
const vectorEntry = createVectorEntity(entry)
|
|
85
|
-
vects.push(new EntitySchema<any>(ormSchemaFromRecordSchema(n, vectorEntry, true)))
|
|
94
|
+
const vectorEntry = createVectorEntity(entry);
|
|
95
|
+
vects.push(new EntitySchema<any>(ormSchemaFromRecordSchema(n, vectorEntry, true)));
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
entities.forEach((r: Record) => {
|
|
99
|
+
const fks = r.getFkAttributeSpecs()
|
|
100
|
+
if (fks.length > 0) {
|
|
101
|
+
fkSpecs.push(...fks)
|
|
86
102
|
}
|
|
87
103
|
})
|
|
88
|
-
})
|
|
89
|
-
return { entities: ents, vectorEntities: vects }
|
|
104
|
+
});
|
|
105
|
+
return { entities: ents, vectorEntities: vects, fkSpecs };
|
|
90
106
|
}
|
|
91
107
|
|
|
92
|
-
function ormSchemaFromRecordSchema(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
108
|
+
function ormSchemaFromRecordSchema(
|
|
109
|
+
moduleName: string,
|
|
110
|
+
entry: Record,
|
|
111
|
+
hasOwnPk?: boolean
|
|
112
|
+
): EntitySchemaOptions<any> {
|
|
113
|
+
const entityName = entry.name;
|
|
114
|
+
const scm: RecordSchema = entry.schema;
|
|
115
|
+
const result = new EntitySchemaOptions<any>();
|
|
116
|
+
result.tableName = asTableReference(moduleName, entityName);
|
|
117
|
+
result.name = result.tableName;
|
|
118
|
+
const cols = new Map<string, any>();
|
|
119
|
+
const indices = new Array<any>();
|
|
120
|
+
const chkforpk: boolean = hasOwnPk == undefined ? false : true;
|
|
121
|
+
let needPath = true;
|
|
102
122
|
scm.forEach((attrSpec: AttributeSpec, attrName: string) => {
|
|
103
123
|
let d: any = getAttributeDefaultValue(attrSpec);
|
|
104
124
|
const autoUuid: boolean = d && d == 'uuid()' ? true : false;
|
|
105
125
|
const autoIncr: boolean = !autoUuid && d && d == 'autoincrement()' ? true : false;
|
|
106
126
|
if (autoUuid || autoIncr) d = undefined;
|
|
107
|
-
let genStrat: 'uuid' | 'increment' | undefined = undefined
|
|
127
|
+
let genStrat: 'uuid' | 'increment' | undefined = undefined;
|
|
108
128
|
if (autoIncr) genStrat = 'increment';
|
|
109
129
|
else if (autoUuid) genStrat = 'uuid';
|
|
110
|
-
const isuq: boolean = isUniqueAttribute(attrSpec)
|
|
111
|
-
const ispk: boolean = chkforpk && isIdAttribute(attrSpec)
|
|
130
|
+
const isuq: boolean = isUniqueAttribute(attrSpec);
|
|
131
|
+
const ispk: boolean = chkforpk && isIdAttribute(attrSpec);
|
|
112
132
|
const colDef: EntitySchemaColumnOptions = {
|
|
113
133
|
type: asSqlType(attrSpec.type),
|
|
114
134
|
generated: genStrat,
|
|
115
135
|
default: d,
|
|
116
136
|
unique: isuq,
|
|
117
137
|
primary: ispk,
|
|
118
|
-
nullable: isOptionalAttribute(attrSpec)
|
|
138
|
+
nullable: isOptionalAttribute(attrSpec),
|
|
119
139
|
};
|
|
120
140
|
if (ispk) {
|
|
121
|
-
needPath = false
|
|
141
|
+
needPath = false;
|
|
122
142
|
}
|
|
123
143
|
if (isIndexedAttribute(attrSpec)) {
|
|
124
|
-
indices.push(
|
|
125
|
-
.
|
|
126
|
-
|
|
127
|
-
|
|
144
|
+
indices.push(
|
|
145
|
+
Object.fromEntries(
|
|
146
|
+
new Map()
|
|
147
|
+
.set('name', `${result.tableName}_${attrName}_index`)
|
|
148
|
+
.set('columns', [attrName])
|
|
149
|
+
.set('unique', isuq)
|
|
150
|
+
)
|
|
151
|
+
);
|
|
128
152
|
}
|
|
129
|
-
cols.set(attrName, colDef)
|
|
153
|
+
cols.set(attrName, colDef);
|
|
130
154
|
});
|
|
131
155
|
if (needPath) {
|
|
132
|
-
cols.set(PathAttributeName, { type:
|
|
133
|
-
cols.set(ParentAttributeName, { type:
|
|
156
|
+
cols.set(PathAttributeName, { type: 'varchar', primary: true });
|
|
157
|
+
cols.set(ParentAttributeName, { type: 'varchar', default: '', indexed: true });
|
|
134
158
|
}
|
|
135
|
-
cols.set(DeletedFlagAttributeName, { type:
|
|
136
|
-
const allBetRels = getAllBetweenRelationships()
|
|
137
|
-
const relsSpec = new Map()
|
|
138
|
-
const fqName = makeFqName(moduleName, entityName)
|
|
139
|
-
getAllOneToOneRelationshipsForEntity(moduleName, entityName, allBetRels)
|
|
140
|
-
|
|
141
|
-
const colName = re.getInverseAliasForName(fqName)
|
|
159
|
+
cols.set(DeletedFlagAttributeName, { type: 'boolean', default: false });
|
|
160
|
+
const allBetRels = getAllBetweenRelationships();
|
|
161
|
+
const relsSpec = new Map();
|
|
162
|
+
const fqName = makeFqName(moduleName, entityName);
|
|
163
|
+
getAllOneToOneRelationshipsForEntity(moduleName, entityName, allBetRels).forEach(
|
|
164
|
+
(re: Relationship) => {
|
|
165
|
+
const colName = re.getInverseAliasForName(fqName);
|
|
142
166
|
if (cols.has(colName)) {
|
|
143
|
-
throw new Error(
|
|
167
|
+
throw new Error(
|
|
168
|
+
`Cannot establish relationship ${re.name}, ${entityName}.${colName} already exists`
|
|
169
|
+
);
|
|
144
170
|
}
|
|
145
|
-
cols.set(colName, { type:
|
|
146
|
-
}
|
|
171
|
+
cols.set(colName, { type: 'varchar', unique: true });
|
|
172
|
+
}
|
|
173
|
+
);
|
|
147
174
|
if (relsSpec.size > 0) {
|
|
148
|
-
result.relations = Object.fromEntries(relsSpec)
|
|
175
|
+
result.relations = Object.fromEntries(relsSpec);
|
|
149
176
|
}
|
|
150
|
-
result.columns = Object.fromEntries(cols)
|
|
151
|
-
const compUqs = entry.getCompositeUniqueAttributes()
|
|
177
|
+
result.columns = Object.fromEntries(cols);
|
|
178
|
+
const compUqs = entry.getCompositeUniqueAttributes();
|
|
152
179
|
if (compUqs) {
|
|
153
|
-
indices.push(
|
|
154
|
-
.
|
|
155
|
-
|
|
156
|
-
|
|
180
|
+
indices.push(
|
|
181
|
+
Object.fromEntries(
|
|
182
|
+
new Map()
|
|
183
|
+
.set('name', `${result.tableName}__comp__index`)
|
|
184
|
+
.set('columns', compUqs)
|
|
185
|
+
.set('unique', true)
|
|
186
|
+
)
|
|
187
|
+
);
|
|
157
188
|
}
|
|
158
189
|
if (indices.length > 0) {
|
|
159
|
-
result.indices = indices
|
|
190
|
+
result.indices = indices;
|
|
160
191
|
}
|
|
161
|
-
return result
|
|
192
|
+
return result;
|
|
162
193
|
}
|
|
163
194
|
|
|
164
|
-
export const OwnersSuffix = '_owners'
|
|
165
|
-
export const VectorSuffix = '_vector'
|
|
195
|
+
export const OwnersSuffix = '_owners';
|
|
196
|
+
export const VectorSuffix = '_vector';
|
|
166
197
|
|
|
167
198
|
function createOwnersEntity(entry: Record): Record {
|
|
168
|
-
const ownersEntry = new Record(`${entry.name}${OwnersSuffix}`, entry.moduleName)
|
|
169
|
-
const permProps = new Map().set('default', true)
|
|
170
|
-
return ownersEntry
|
|
199
|
+
const ownersEntry = new Record(`${entry.name}${OwnersSuffix}`, entry.moduleName);
|
|
200
|
+
const permProps = new Map().set('default', true);
|
|
201
|
+
return ownersEntry
|
|
202
|
+
.addAttribute('id', { type: 'UUID', properties: new Map().set('id', true) })
|
|
171
203
|
.addAttribute('user_id', { type: 'String' })
|
|
172
204
|
.addAttribute('type', { type: 'String', properties: new Map().set('default', 'o') })
|
|
173
205
|
.addAttribute('c', { type: 'Boolean', properties: permProps })
|
|
174
206
|
.addAttribute('r', { type: 'Boolean', properties: permProps })
|
|
175
207
|
.addAttribute('u', { type: 'Boolean', properties: permProps })
|
|
176
208
|
.addAttribute('d', { type: 'Boolean', properties: permProps })
|
|
177
|
-
.addAttribute('path', { type: 'String', properties: new Map().set('indexed', true) })
|
|
209
|
+
.addAttribute('path', { type: 'String', properties: new Map().set('indexed', true) });
|
|
178
210
|
}
|
|
179
211
|
|
|
180
212
|
function createVectorEntity(entry: Record): Record {
|
|
181
|
-
const ownersEntry = new Record(`${entry.name}${VectorSuffix}`, entry.moduleName)
|
|
182
|
-
return ownersEntry
|
|
183
|
-
.addAttribute('
|
|
213
|
+
const ownersEntry = new Record(`${entry.name}${VectorSuffix}`, entry.moduleName);
|
|
214
|
+
return ownersEntry
|
|
215
|
+
.addAttribute('id', { type: 'String', properties: new Map().set('id', true) })
|
|
216
|
+
.addAttribute('embedding', { type: `vector(${DefaultVectorDimension})` });
|
|
184
217
|
}
|
|
185
218
|
|
|
186
219
|
export type TableSpec = {
|
|
@@ -250,12 +283,19 @@ function entitySchemaToTable(scm: RecordSchema): TableSpec {
|
|
|
250
283
|
}
|
|
251
284
|
|
|
252
285
|
export function asSqlType(type: string): ColumnType {
|
|
253
|
-
const t = type.toLowerCase()
|
|
254
|
-
if (
|
|
255
|
-
|
|
286
|
+
const t = type.toLowerCase();
|
|
287
|
+
if (
|
|
288
|
+
t == 'string' ||
|
|
289
|
+
t == 'datetime' ||
|
|
290
|
+
t == 'email' ||
|
|
291
|
+
t == 'url' ||
|
|
292
|
+
t == 'map' ||
|
|
293
|
+
t == 'any' ||
|
|
294
|
+
t == 'path'
|
|
295
|
+
)
|
|
256
296
|
return 'varchar';
|
|
257
297
|
else if (t == 'int') return 'integer';
|
|
258
|
-
else if (t == 'number') return 'double precision'
|
|
298
|
+
else if (t == 'number') return 'double precision';
|
|
259
299
|
else if (!isBuiltInType(type)) return 'varchar';
|
|
260
300
|
else return t as ColumnType;
|
|
261
301
|
}
|
|
@@ -13,7 +13,15 @@ import {
|
|
|
13
13
|
newInstanceAttributes,
|
|
14
14
|
Relationship,
|
|
15
15
|
} from '../../module.js';
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
escapeFqName,
|
|
18
|
+
escapeQueryName,
|
|
19
|
+
isFqName,
|
|
20
|
+
makeFqName,
|
|
21
|
+
nameToPath,
|
|
22
|
+
splitFqName,
|
|
23
|
+
splitRefs,
|
|
24
|
+
} from '../../util.js';
|
|
17
25
|
import { JoinInfo, Resolver } from '../interface.js';
|
|
18
26
|
import { asTableReference } from './dbutil.js';
|
|
19
27
|
import {
|
|
@@ -41,6 +49,7 @@ import { OpenAIEmbeddings } from '@langchain/openai';
|
|
|
41
49
|
import { Embeddings } from '@langchain/core/embeddings';
|
|
42
50
|
import { DeletedFlagAttributeName, ParentAttributeName, PathAttributeName } from '../../defs.js';
|
|
43
51
|
import { logger } from '../../logger.js';
|
|
52
|
+
import { JoinSpec } from '../../../language/generated/ast.js';
|
|
44
53
|
|
|
45
54
|
function maybeFindIdAttributeName(inst: Instance): string | undefined {
|
|
46
55
|
const attrEntry: AttributeEntry | undefined = findIdAttribute(inst);
|
|
@@ -251,13 +260,18 @@ export class SqlDbResolver extends Resolver {
|
|
|
251
260
|
|
|
252
261
|
public override async queryByJoin(
|
|
253
262
|
inst: Instance,
|
|
254
|
-
|
|
263
|
+
joinInfo: JoinInfo[],
|
|
255
264
|
intoSpec: Map<string, string>,
|
|
256
|
-
distinct: boolean = false
|
|
265
|
+
distinct: boolean = false,
|
|
266
|
+
rawJoinSpec?: JoinSpec
|
|
257
267
|
): Promise<any> {
|
|
258
268
|
const tableName = asTableReference(inst.moduleName, inst.name);
|
|
259
269
|
const joinClauses: JoinClause[] = [];
|
|
260
|
-
|
|
270
|
+
if (rawJoinSpec) {
|
|
271
|
+
this.processRawJoinSpec(tableName, inst, rawJoinSpec, joinClauses);
|
|
272
|
+
} else {
|
|
273
|
+
this.processJoinInfo(tableName, inst, joinInfo, joinClauses);
|
|
274
|
+
}
|
|
261
275
|
intoSpec.forEach((v: string, k: string) => {
|
|
262
276
|
const p = nameToPath(v);
|
|
263
277
|
const mn = p.hasModule() ? p.getModuleName() : inst.moduleName;
|
|
@@ -275,13 +289,46 @@ export class SqlDbResolver extends Resolver {
|
|
|
275
289
|
return rslt;
|
|
276
290
|
}
|
|
277
291
|
|
|
292
|
+
private processRawJoinSpec(
|
|
293
|
+
tableName: string,
|
|
294
|
+
inst: Instance,
|
|
295
|
+
rawJoinSpec: JoinSpec,
|
|
296
|
+
joinClauses: JoinClause[]
|
|
297
|
+
) {
|
|
298
|
+
const n = rawJoinSpec.name;
|
|
299
|
+
let joinTableName = '';
|
|
300
|
+
if (isFqName(n)) {
|
|
301
|
+
const parts = splitFqName(n);
|
|
302
|
+
joinTableName = asTableReference(parts[0], parts[1]);
|
|
303
|
+
} else {
|
|
304
|
+
joinTableName = asTableReference(inst.moduleName, n);
|
|
305
|
+
}
|
|
306
|
+
const refParts = splitRefs(rawJoinSpec.rhs);
|
|
307
|
+
if (refParts.length != 2) {
|
|
308
|
+
throw new Error(`Invalid join referene - ${rawJoinSpec.rhs}`);
|
|
309
|
+
}
|
|
310
|
+
if (refParts[0] !== inst.name) {
|
|
311
|
+
throw new Error(`Invalid table name in join reference - ${rawJoinSpec.rhs}`);
|
|
312
|
+
}
|
|
313
|
+
const joinOn = makeJoinOn(
|
|
314
|
+
`"${joinTableName}"."${escapeQueryName(rawJoinSpec.lhs)}"`,
|
|
315
|
+
`"${tableName}"."${escapeQueryName(refParts[1])}"`,
|
|
316
|
+
rawJoinSpec.op
|
|
317
|
+
);
|
|
318
|
+
joinClauses.push({
|
|
319
|
+
tableName: joinTableName,
|
|
320
|
+
joinOn: joinOn,
|
|
321
|
+
joinType: rawJoinSpec.type.substring(1).replace('_', ' '),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
278
325
|
private processJoinInfo(
|
|
279
326
|
joinParentTable: string,
|
|
280
327
|
joinInst: Instance,
|
|
281
|
-
|
|
328
|
+
joinInfo: JoinInfo[],
|
|
282
329
|
joinClauses: JoinClause[]
|
|
283
330
|
) {
|
|
284
|
-
|
|
331
|
+
joinInfo.forEach((ji: JoinInfo) => {
|
|
285
332
|
const rel: Relationship = ji.relationship;
|
|
286
333
|
const joinTableName = asTableReference(ji.queryInstance.moduleName, ji.queryInstance.name);
|
|
287
334
|
let joinOn: JoinOn | JoinOn[] | undefined;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Monarch syntax highlighting for the agentlang language.
|
|
2
2
|
export default {
|
|
3
3
|
keywords: [
|
|
4
|
-
'@actions','@after','@as','@async','@before','@catch','@distinct','@enum','@expr','@from','@into','@meta','@oneof','@public','@rbac','@ref','@then','@upsert','@with_unique','agent','agentlang/retry','allow','and','attempts','await','backoff','between','case','commitTransaction','contains','create','decision','delete','directive','else','entity','error','event','extends','false','flow','for','glossaryEntry','if','import','in','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','return','roles','rollbackTransaction','scenario','startTransaction','subscribe','true','update','upsert','where','workflow'
|
|
4
|
+
'@actions','@after','@as','@async','@before','@catch','@distinct','@enum','@expr','@from','@full_join','@inner_join','@into','@join','@left_join','@meta','@oneof','@public','@rbac','@ref','@right_join','@then','@upsert','@with_unique','agent','agentlang/retry','allow','and','attempts','await','backoff','between','case','commitTransaction','contains','create','decision','delete','directive','else','entity','error','event','extends','false','flow','for','glossaryEntry','if','import','in','like','module','not','not_found','onSubscription','or','purge','query','read','record','relationship','resolver','return','roles','rollbackTransaction','scenario','startTransaction','subscribe','true','update','upsert','where','workflow'
|
|
5
5
|
],
|
|
6
6
|
operators: [
|
|
7
7
|
'!=','*','+',',','-','-->','.','/',':',';','<','<=','<>','=','==','>','>=','?','@'
|