@memberjunction/core-entities-server 3.4.0 → 4.1.0
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/README.md +147 -11
- package/dist/custom/AIAgentExampleEntity.server.d.ts +0 -4
- package/dist/custom/AIAgentExampleEntity.server.d.ts.map +1 -1
- package/dist/custom/AIAgentExampleEntity.server.js +13 -20
- package/dist/custom/AIAgentExampleEntity.server.js.map +1 -1
- package/dist/custom/AIAgentNoteEntity.server.d.ts +0 -4
- package/dist/custom/AIAgentNoteEntity.server.d.ts.map +1 -1
- package/dist/custom/AIAgentNoteEntity.server.js +13 -20
- package/dist/custom/AIAgentNoteEntity.server.js.map +1 -1
- package/dist/custom/AIPromptEntityExtended.server.d.ts +0 -1
- package/dist/custom/AIPromptEntityExtended.server.d.ts.map +1 -1
- package/dist/custom/AIPromptEntityExtended.server.js +16 -20
- package/dist/custom/AIPromptEntityExtended.server.js.map +1 -1
- package/dist/custom/AIPromptRunEntity.server.d.ts +0 -4
- package/dist/custom/AIPromptRunEntity.server.d.ts.map +1 -1
- package/dist/custom/AIPromptRunEntity.server.js +19 -27
- package/dist/custom/AIPromptRunEntity.server.js.map +1 -1
- package/dist/custom/ActionEntity.server.d.ts +0 -1
- package/dist/custom/ActionEntity.server.d.ts.map +1 -1
- package/dist/custom/ActionEntity.server.js +26 -35
- package/dist/custom/ActionEntity.server.js.map +1 -1
- package/dist/custom/ApplicationEntity.server.d.ts +9 -1
- package/dist/custom/ApplicationEntity.server.d.ts.map +1 -1
- package/dist/custom/ApplicationEntity.server.js +41 -31
- package/dist/custom/ApplicationEntity.server.js.map +1 -1
- package/dist/custom/ArtifactVersionExtended.server.d.ts +0 -4
- package/dist/custom/ArtifactVersionExtended.server.d.ts.map +1 -1
- package/dist/custom/ArtifactVersionExtended.server.js +19 -29
- package/dist/custom/ArtifactVersionExtended.server.js.map +1 -1
- package/dist/custom/ComponentEntity.server.d.ts +0 -1
- package/dist/custom/ComponentEntity.server.d.ts.map +1 -1
- package/dist/custom/ComponentEntity.server.js +9 -14
- package/dist/custom/ComponentEntity.server.js.map +1 -1
- package/dist/custom/ConversationDetailEntity.server.d.ts +0 -1
- package/dist/custom/ConversationDetailEntity.server.d.ts.map +1 -1
- package/dist/custom/ConversationDetailEntity.server.js +7 -12
- package/dist/custom/ConversationDetailEntity.server.js.map +1 -1
- package/dist/custom/DuplicateRunEntity.server.d.ts +0 -1
- package/dist/custom/DuplicateRunEntity.server.d.ts.map +1 -1
- package/dist/custom/DuplicateRunEntity.server.js +10 -15
- package/dist/custom/DuplicateRunEntity.server.js.map +1 -1
- package/dist/custom/QueryEntity.server.d.ts +0 -23
- package/dist/custom/QueryEntity.server.d.ts.map +1 -1
- package/dist/custom/QueryEntity.server.js +53 -326
- package/dist/custom/QueryEntity.server.js.map +1 -1
- package/dist/custom/TemplateContentEntity.server.d.ts +0 -1
- package/dist/custom/TemplateContentEntity.server.d.ts.map +1 -1
- package/dist/custom/TemplateContentEntity.server.js +19 -24
- package/dist/custom/TemplateContentEntity.server.js.map +1 -1
- package/dist/custom/reportEntity.server.d.ts +0 -1
- package/dist/custom/reportEntity.server.d.ts.map +1 -1
- package/dist/custom/reportEntity.server.js +9 -14
- package/dist/custom/reportEntity.server.js.map +1 -1
- package/dist/custom/sql-parser.d.ts +95 -0
- package/dist/custom/sql-parser.d.ts.map +1 -0
- package/dist/custom/sql-parser.js +313 -0
- package/dist/custom/sql-parser.js.map +1 -0
- package/dist/custom/userViewEntity.server.d.ts +0 -1
- package/dist/custom/userViewEntity.server.d.ts.map +1 -1
- package/dist/custom/userViewEntity.server.js +20 -23
- package/dist/custom/userViewEntity.server.js.map +1 -1
- package/dist/custom/util.js +4 -8
- package/dist/custom/util.js.map +1 -1
- package/dist/index.d.ts +16 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -65
- package/dist/index.js.map +1 -1
- package/package.json +20 -19
|
@@ -1,45 +1,18 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
1
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
2
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
3
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
6
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const core_1 = require("@memberjunction/core");
|
|
34
|
-
const core_entities_1 = require("@memberjunction/core-entities");
|
|
35
|
-
const global_1 = require("@memberjunction/global");
|
|
36
|
-
const aiengine_1 = require("@memberjunction/aiengine");
|
|
37
|
-
const ai_prompts_1 = require("@memberjunction/ai-prompts");
|
|
38
|
-
const ai_core_plus_1 = require("@memberjunction/ai-core-plus");
|
|
39
|
-
const util_1 = require("./util");
|
|
40
|
-
const node_sql_parser_1 = require("node-sql-parser");
|
|
41
|
-
const nunjucks = __importStar(require("nunjucks"));
|
|
42
|
-
let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.QueryEntity {
|
|
7
|
+
import { BaseEntity, LogError, Metadata } from "@memberjunction/core";
|
|
8
|
+
import { QueryEntity } from "@memberjunction/core-entities";
|
|
9
|
+
import { RegisterClass } from "@memberjunction/global";
|
|
10
|
+
import { AIEngine } from "@memberjunction/aiengine";
|
|
11
|
+
import { AIPromptRunner } from "@memberjunction/ai-prompts";
|
|
12
|
+
import { AIPromptParams } from "@memberjunction/ai-core-plus";
|
|
13
|
+
import { EmbedTextLocalHelper } from "./util.js";
|
|
14
|
+
import { SQLParser } from "./sql-parser.js";
|
|
15
|
+
let QueryEntityExtended = class QueryEntityExtended extends QueryEntity {
|
|
43
16
|
constructor() {
|
|
44
17
|
super(...arguments);
|
|
45
18
|
this._queryEntities = [];
|
|
@@ -65,7 +38,7 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
65
38
|
* @returns
|
|
66
39
|
*/
|
|
67
40
|
async EmbedTextLocal(textToEmbed) {
|
|
68
|
-
return
|
|
41
|
+
return EmbedTextLocalHelper(this, textToEmbed);
|
|
69
42
|
}
|
|
70
43
|
async Save(options) {
|
|
71
44
|
try {
|
|
@@ -104,7 +77,7 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
104
77
|
return true;
|
|
105
78
|
}
|
|
106
79
|
catch (e) {
|
|
107
|
-
|
|
80
|
+
LogError('Failed to save query:', e);
|
|
108
81
|
this.LatestResult?.Errors.push(e);
|
|
109
82
|
return false;
|
|
110
83
|
}
|
|
@@ -123,7 +96,7 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
123
96
|
return true;
|
|
124
97
|
}
|
|
125
98
|
catch (e) {
|
|
126
|
-
|
|
99
|
+
LogError('Failed to delete query:', e);
|
|
127
100
|
this.LatestResult?.Errors.push(e);
|
|
128
101
|
return false;
|
|
129
102
|
}
|
|
@@ -139,11 +112,11 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
139
112
|
// This is a separate, fast operation that doesn't involve AI
|
|
140
113
|
const updateResult = await super.Save();
|
|
141
114
|
if (!updateResult) {
|
|
142
|
-
|
|
115
|
+
LogError('Failed to save query after AI processing completed');
|
|
143
116
|
}
|
|
144
117
|
}
|
|
145
118
|
catch (e) {
|
|
146
|
-
|
|
119
|
+
LogError('Error in async AI processing:', e);
|
|
147
120
|
// Set UsesTemplate to false on error and save
|
|
148
121
|
// This ensures the query is still usable even if AI extraction fails
|
|
149
122
|
this.UsesTemplate = false;
|
|
@@ -151,7 +124,7 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
151
124
|
await super.Save();
|
|
152
125
|
}
|
|
153
126
|
catch (saveError) {
|
|
154
|
-
|
|
127
|
+
LogError('Failed to save query after AI processing error:', saveError);
|
|
155
128
|
}
|
|
156
129
|
}
|
|
157
130
|
}
|
|
@@ -168,11 +141,11 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
168
141
|
// Save the updated UsesTemplate flag
|
|
169
142
|
const updateResult = await super.Save();
|
|
170
143
|
if (!updateResult) {
|
|
171
|
-
|
|
144
|
+
LogError('Failed to save query after cleanup');
|
|
172
145
|
}
|
|
173
146
|
}
|
|
174
147
|
catch (e) {
|
|
175
|
-
|
|
148
|
+
LogError('Error in async cleanup:', e);
|
|
176
149
|
}
|
|
177
150
|
}
|
|
178
151
|
async extractAndSyncData() {
|
|
@@ -182,9 +155,9 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
182
155
|
this.SQL.includes('{%') ||
|
|
183
156
|
this.SQL.includes('{#'));
|
|
184
157
|
// Ensure AIEngine is configured
|
|
185
|
-
await
|
|
158
|
+
await AIEngine.Instance.Config(false, this.ContextCurrentUser, this.ProviderToUse);
|
|
186
159
|
// Find the SQL Query Parameter Extraction prompt
|
|
187
|
-
const aiPrompt =
|
|
160
|
+
const aiPrompt = AIEngine.Instance.Prompts.find(p => p.Name === 'SQL Query Parameter Extraction' &&
|
|
188
161
|
p.Category === 'MJ: System');
|
|
189
162
|
if (!aiPrompt) {
|
|
190
163
|
// Prompt not configured, non-fatal, just warn and return
|
|
@@ -202,8 +175,8 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
202
175
|
entities: entityMetadata
|
|
203
176
|
};
|
|
204
177
|
// Execute the prompt using AIPromptRunner
|
|
205
|
-
const promptRunner = new
|
|
206
|
-
const params = new
|
|
178
|
+
const promptRunner = new AIPromptRunner();
|
|
179
|
+
const params = new AIPromptParams();
|
|
207
180
|
params.prompt = aiPrompt;
|
|
208
181
|
params.data = promptData;
|
|
209
182
|
params.contextUser = this.ContextCurrentUser;
|
|
@@ -248,7 +221,7 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
248
221
|
}
|
|
249
222
|
catch (e) {
|
|
250
223
|
// Unexpected error during extraction - log for debugging but don't fail the save
|
|
251
|
-
|
|
224
|
+
LogError(`Query "${this.Name}" AI extraction error:`, e);
|
|
252
225
|
this.UsesTemplate = false;
|
|
253
226
|
}
|
|
254
227
|
}
|
|
@@ -262,69 +235,41 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
262
235
|
if (!this.SQL)
|
|
263
236
|
return results;
|
|
264
237
|
try {
|
|
265
|
-
//
|
|
266
|
-
const
|
|
267
|
-
//
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
catch (parseError) {
|
|
274
|
-
// If parsing fails, fall back to regex-based extraction
|
|
275
|
-
console.warn(`SQL parsing failed, falling back to regex: ${parseError.message}`);
|
|
276
|
-
return this.extractEntityMetadataFromSQLRegex();
|
|
277
|
-
}
|
|
278
|
-
// Extract table references and column references from AST
|
|
279
|
-
const tableAliasMap = new Map();
|
|
280
|
-
const referencedColumns = new Set();
|
|
281
|
-
// Process AST (handle both single statement and array of statements)
|
|
282
|
-
const statements = Array.isArray(ast) ? ast : [ast];
|
|
283
|
-
for (const statement of statements) {
|
|
284
|
-
this.extractFromAST(statement, tableAliasMap, referencedColumns);
|
|
285
|
-
}
|
|
286
|
-
// Build entity metadata with only referenced fields
|
|
287
|
-
const processedEntities = new Set();
|
|
288
|
-
for (const [alias, tableInfo] of tableAliasMap) {
|
|
289
|
-
const schemaName = tableInfo.schemaName || 'dbo';
|
|
290
|
-
const tableName = tableInfo.tableName;
|
|
291
|
-
const key = `${schemaName.toLowerCase()}.${tableName.toLowerCase()}`;
|
|
292
|
-
if (processedEntities.has(key))
|
|
293
|
-
continue;
|
|
294
|
-
processedEntities.add(key);
|
|
295
|
-
// Find matching entity in metadata
|
|
296
|
-
const matchingEntity = md.Entities.find(e => (e.BaseView.toLowerCase() === tableName.toLowerCase() ||
|
|
297
|
-
e.BaseTable.toLowerCase() === tableName.toLowerCase()) &&
|
|
298
|
-
e.SchemaName.toLowerCase() === schemaName.toLowerCase());
|
|
238
|
+
// Use the shared SQLParser with Nunjucks preprocessing
|
|
239
|
+
const parseResult = SQLParser.ParseWithTemplatePreprocessing(this.SQL);
|
|
240
|
+
// Cross-reference parsed tables against entity metadata
|
|
241
|
+
for (const tableRef of parseResult.Tables) {
|
|
242
|
+
const matchingEntity = md.Entities.find(e => (e.BaseView.toLowerCase() === tableRef.TableName.toLowerCase() ||
|
|
243
|
+
e.BaseTable.toLowerCase() === tableRef.TableName.toLowerCase()) &&
|
|
244
|
+
e.SchemaName.toLowerCase() === tableRef.SchemaName.toLowerCase());
|
|
299
245
|
if (matchingEntity) {
|
|
300
|
-
// Get fields from the entity's Fields property
|
|
301
|
-
const entityFields = matchingEntity.Fields;
|
|
302
246
|
// Filter to fields that are actually referenced in the SQL
|
|
303
|
-
|
|
304
|
-
const relevantFields = entityFields
|
|
247
|
+
const relevantFields = matchingEntity.Fields
|
|
305
248
|
.filter(f => {
|
|
306
249
|
const fieldLower = f.Name.toLowerCase();
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
// Match: fieldName, alias.fieldName, tableName.fieldName
|
|
311
|
-
if (colLower === fieldLower ||
|
|
312
|
-
colLower === `${alias.toLowerCase()}.${fieldLower}` ||
|
|
313
|
-
colLower === `${tableName.toLowerCase()}.${fieldLower}` ||
|
|
314
|
-
colLower.endsWith(`.${fieldLower}`)) {
|
|
250
|
+
for (const colRef of parseResult.Columns) {
|
|
251
|
+
const colLower = colRef.ColumnName.toLowerCase();
|
|
252
|
+
if (colLower === fieldLower)
|
|
315
253
|
return true;
|
|
254
|
+
// Check if column qualifier matches table alias or name
|
|
255
|
+
if (colRef.TableQualifier) {
|
|
256
|
+
const qualLower = colRef.TableQualifier.toLowerCase();
|
|
257
|
+
if ((qualLower === tableRef.Alias.toLowerCase() ||
|
|
258
|
+
qualLower === tableRef.TableName.toLowerCase()) &&
|
|
259
|
+
colLower === fieldLower) {
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
316
262
|
}
|
|
317
263
|
}
|
|
318
264
|
// Also include primary keys as they're always useful context
|
|
319
265
|
return f.IsPrimaryKey;
|
|
320
266
|
})
|
|
321
|
-
.slice(0, 20)
|
|
267
|
+
.slice(0, 20)
|
|
322
268
|
.map(f => ({
|
|
323
269
|
name: f.Name,
|
|
324
270
|
type: f.Type,
|
|
325
271
|
isPrimaryKey: f.IsPrimaryKey
|
|
326
272
|
}));
|
|
327
|
-
// Only include entity if we found relevant fields
|
|
328
273
|
if (relevantFields.length > 0) {
|
|
329
274
|
results.push({
|
|
330
275
|
name: matchingEntity.Name,
|
|
@@ -338,225 +283,9 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
338
283
|
return results;
|
|
339
284
|
}
|
|
340
285
|
catch (error) {
|
|
341
|
-
console.warn(`Error in extractEntityMetadataFromSQL: ${error
|
|
342
|
-
return this.extractEntityMetadataFromSQLRegex();
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Pre-processes Nunjucks templates in SQL to create valid SQL for parsing.
|
|
347
|
-
* Replaces Nunjucks syntax with placeholder values.
|
|
348
|
-
*/
|
|
349
|
-
preProcessNunjucksForParsing(sql) {
|
|
350
|
-
// Create a Nunjucks environment that won't throw on undefined
|
|
351
|
-
const env = new nunjucks.Environment(null, {
|
|
352
|
-
autoescape: false,
|
|
353
|
-
throwOnUndefined: false
|
|
354
|
-
});
|
|
355
|
-
// Add placeholder filters that return safe SQL values
|
|
356
|
-
env.addFilter('sqlString', () => "'placeholder'");
|
|
357
|
-
env.addFilter('sqlNumber', () => '0');
|
|
358
|
-
env.addFilter('sqlDate', () => "'2000-01-01'");
|
|
359
|
-
env.addFilter('sqlIn', () => "('placeholder')");
|
|
360
|
-
env.addFilter('sqlIdentifier', (val) => val || 'placeholder');
|
|
361
|
-
env.addFilter('sqlNoKeywordsExpression', (val) => val || 'placeholder');
|
|
362
|
-
env.addFilter('default', (val, defaultVal) => val || defaultVal || 'placeholder');
|
|
363
|
-
env.addFilter('safe', (val) => val || 'placeholder');
|
|
364
|
-
env.addFilter('dump', (val) => JSON.stringify(val || {}));
|
|
365
|
-
try {
|
|
366
|
-
// Try to render the template with placeholder data
|
|
367
|
-
// This handles most Nunjucks constructs
|
|
368
|
-
const rendered = env.renderString(sql, {});
|
|
369
|
-
return rendered;
|
|
370
|
-
}
|
|
371
|
-
catch (e) {
|
|
372
|
-
// If rendering fails, do manual regex replacement
|
|
373
|
-
let processed = sql;
|
|
374
|
-
// Replace {{ variable | filter }} patterns
|
|
375
|
-
processed = processed.replace(/\{\{\s*[\w.]+\s*\|\s*sqlString\s*\}\}/g, "'placeholder'");
|
|
376
|
-
processed = processed.replace(/\{\{\s*[\w.]+\s*\|\s*sqlNumber\s*\}\}/g, '0');
|
|
377
|
-
processed = processed.replace(/\{\{\s*[\w.]+\s*\|\s*sqlDate\s*\}\}/g, "'2000-01-01'");
|
|
378
|
-
processed = processed.replace(/\{\{\s*[\w.]+\s*\|\s*sqlIn\s*\}\}/g, "('placeholder')");
|
|
379
|
-
processed = processed.replace(/\{\{\s*[\w.]+\s*\|\s*sqlIdentifier\s*\}\}/g, 'placeholder');
|
|
380
|
-
processed = processed.replace(/\{\{\s*[\w.]+\s*[^}]*\}\}/g, "'placeholder'");
|
|
381
|
-
// Remove {% if %}, {% endif %}, {% for %}, {% endfor %} blocks
|
|
382
|
-
// Keep the content between them
|
|
383
|
-
processed = processed.replace(/\{%\s*if\s+[^%]+%\}/g, '');
|
|
384
|
-
processed = processed.replace(/\{%\s*endif\s*%\}/g, '');
|
|
385
|
-
processed = processed.replace(/\{%\s*else\s*%\}/g, '');
|
|
386
|
-
processed = processed.replace(/\{%\s*elif\s+[^%]+%\}/g, '');
|
|
387
|
-
processed = processed.replace(/\{%\s*for\s+[^%]+%\}/g, '');
|
|
388
|
-
processed = processed.replace(/\{%\s*endfor\s*%\}/g, '');
|
|
389
|
-
processed = processed.replace(/\{%\s*set\s+[^%]+%\}/g, '');
|
|
390
|
-
processed = processed.replace(/\{#[^#]*#\}/g, ''); // Comments
|
|
391
|
-
return processed;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
/**
|
|
395
|
-
* Recursively extracts table references and column references from SQL AST.
|
|
396
|
-
*/
|
|
397
|
-
extractFromAST(node, tableAliasMap, referencedColumns) {
|
|
398
|
-
if (!node || typeof node !== 'object')
|
|
399
|
-
return;
|
|
400
|
-
// Handle CTEs (WITH clause) - process these first as they define tables used later
|
|
401
|
-
if (node.with) {
|
|
402
|
-
for (const cte of Array.isArray(node.with) ? node.with : [node.with]) {
|
|
403
|
-
// CTE structure can vary - check multiple possible locations
|
|
404
|
-
if (cte.stmt) {
|
|
405
|
-
// stmt might be the AST directly or contain ast property
|
|
406
|
-
const cteAst = cte.stmt.ast || cte.stmt;
|
|
407
|
-
this.extractFromAST(cteAst, tableAliasMap, referencedColumns);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
// Extract FROM clause tables (including JOINs)
|
|
412
|
-
if (node.from) {
|
|
413
|
-
for (const fromItem of Array.isArray(node.from) ? node.from : [node.from]) {
|
|
414
|
-
this.extractTableFromItem(fromItem, tableAliasMap, referencedColumns);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
// Extract column references from SELECT
|
|
418
|
-
if (node.columns) {
|
|
419
|
-
for (const col of Array.isArray(node.columns) ? node.columns : [node.columns]) {
|
|
420
|
-
if (col === '*')
|
|
421
|
-
continue;
|
|
422
|
-
if (col.expr) {
|
|
423
|
-
this.extractColumnReferences(col.expr, referencedColumns, tableAliasMap);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
// Extract column references from WHERE (and tables from subqueries like EXISTS, IN)
|
|
428
|
-
if (node.where) {
|
|
429
|
-
this.extractColumnReferences(node.where, referencedColumns, tableAliasMap);
|
|
430
|
-
}
|
|
431
|
-
// Extract column references from GROUP BY
|
|
432
|
-
if (node.groupby) {
|
|
433
|
-
const groupItems = node.groupby.columns || node.groupby;
|
|
434
|
-
for (const group of Array.isArray(groupItems) ? groupItems : [groupItems]) {
|
|
435
|
-
this.extractColumnReferences(group, referencedColumns, tableAliasMap);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
// Extract column references from ORDER BY
|
|
439
|
-
if (node.orderby) {
|
|
440
|
-
for (const order of Array.isArray(node.orderby) ? node.orderby : [node.orderby]) {
|
|
441
|
-
this.extractColumnReferences(order.expr || order, referencedColumns, tableAliasMap);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
// Handle UNION/INTERSECT/EXCEPT - the next query is in _next
|
|
445
|
-
if (node._next) {
|
|
446
|
-
this.extractFromAST(node._next, tableAliasMap, referencedColumns);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
/**
|
|
450
|
-
* Extracts table information from a FROM clause item, handling tables and JOINs.
|
|
451
|
-
*/
|
|
452
|
-
extractTableFromItem(fromItem, tableAliasMap, referencedColumns) {
|
|
453
|
-
if (!fromItem)
|
|
454
|
-
return;
|
|
455
|
-
// Direct table reference
|
|
456
|
-
if (fromItem.table) {
|
|
457
|
-
const alias = fromItem.as || fromItem.table;
|
|
458
|
-
tableAliasMap.set(alias, {
|
|
459
|
-
schemaName: fromItem.db || 'dbo',
|
|
460
|
-
tableName: fromItem.table
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
// Handle subqueries in FROM (derived tables)
|
|
464
|
-
if (fromItem.expr) {
|
|
465
|
-
// The subquery AST is inside expr.ast
|
|
466
|
-
const subqueryAst = fromItem.expr.ast || fromItem.expr;
|
|
467
|
-
this.extractFromAST(subqueryAst, tableAliasMap, referencedColumns);
|
|
468
|
-
}
|
|
469
|
-
// Note: In node-sql-parser, JOINs are represented as separate items in the from array
|
|
470
|
-
// with a 'join' property that's a string (e.g., "INNER JOIN", "LEFT JOIN")
|
|
471
|
-
// So no need to recurse into join - each table is already in the from array
|
|
472
|
-
// Extract column references from ON conditions
|
|
473
|
-
if (fromItem.on) {
|
|
474
|
-
this.extractColumnReferences(fromItem.on, referencedColumns, tableAliasMap);
|
|
475
|
-
}
|
|
476
|
-
// Some parsers use 'using' for join conditions
|
|
477
|
-
if (fromItem.using) {
|
|
478
|
-
for (const col of Array.isArray(fromItem.using) ? fromItem.using : [fromItem.using]) {
|
|
479
|
-
if (typeof col === 'string') {
|
|
480
|
-
referencedColumns.add(col);
|
|
481
|
-
}
|
|
482
|
-
else if (col.column) {
|
|
483
|
-
referencedColumns.add(col.column);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
/**
|
|
489
|
-
* Extracts column references from an expression node.
|
|
490
|
-
* Also extracts tables from subqueries found in expressions (e.g., EXISTS, IN subqueries).
|
|
491
|
-
*/
|
|
492
|
-
extractColumnReferences(expr, referencedColumns, tableAliasMap) {
|
|
493
|
-
if (!expr || typeof expr !== 'object')
|
|
494
|
-
return;
|
|
495
|
-
if (expr.type === 'column_ref') {
|
|
496
|
-
const colName = expr.table ? `${expr.table}.${expr.column}` : expr.column;
|
|
497
|
-
referencedColumns.add(colName);
|
|
498
|
-
}
|
|
499
|
-
// Handle subqueries in expressions (EXISTS, IN, scalar subqueries)
|
|
500
|
-
if (expr.ast && tableAliasMap) {
|
|
501
|
-
this.extractFromAST(expr.ast, tableAliasMap, referencedColumns);
|
|
502
|
-
}
|
|
503
|
-
// Recursively process nested expressions
|
|
504
|
-
if (expr.left)
|
|
505
|
-
this.extractColumnReferences(expr.left, referencedColumns, tableAliasMap);
|
|
506
|
-
if (expr.right)
|
|
507
|
-
this.extractColumnReferences(expr.right, referencedColumns, tableAliasMap);
|
|
508
|
-
if (expr.args) {
|
|
509
|
-
const args = expr.args.value || expr.args;
|
|
510
|
-
for (const arg of Array.isArray(args) ? args : [args]) {
|
|
511
|
-
this.extractColumnReferences(arg, referencedColumns, tableAliasMap);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
if (expr.expr)
|
|
515
|
-
this.extractColumnReferences(expr.expr, referencedColumns, tableAliasMap);
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* Fallback regex-based extraction for when SQL parsing fails.
|
|
519
|
-
*/
|
|
520
|
-
extractEntityMetadataFromSQLRegex() {
|
|
521
|
-
const md = this.ProviderToUse;
|
|
522
|
-
const results = [];
|
|
523
|
-
if (!this.SQL)
|
|
286
|
+
console.warn(`Error in extractEntityMetadataFromSQL: ${error}`);
|
|
524
287
|
return results;
|
|
525
|
-
// Simple regex to find schema.table or schema.view references in SQL
|
|
526
|
-
const tablePattern = /(?:FROM|JOIN|INTO)\s+(?:\[?(\w+)\]?\.)?\[?(\w+)\]?(?:\s+(?:AS\s+)?(\w+))?/gi;
|
|
527
|
-
const matches = [...this.SQL.matchAll(tablePattern)];
|
|
528
|
-
const processedViews = new Set();
|
|
529
|
-
for (const match of matches) {
|
|
530
|
-
const schemaName = match[1] || 'dbo';
|
|
531
|
-
const viewOrTable = match[2];
|
|
532
|
-
const key = `${schemaName.toLowerCase()}.${viewOrTable.toLowerCase()}`;
|
|
533
|
-
if (processedViews.has(key))
|
|
534
|
-
continue;
|
|
535
|
-
processedViews.add(key);
|
|
536
|
-
const matchingEntity = md.Entities.find(e => (e.BaseView.toLowerCase() === viewOrTable.toLowerCase() ||
|
|
537
|
-
e.BaseTable.toLowerCase() === viewOrTable.toLowerCase()) &&
|
|
538
|
-
e.SchemaName.toLowerCase() === schemaName.toLowerCase());
|
|
539
|
-
if (matchingEntity) {
|
|
540
|
-
// Get fields from the entity's Fields property
|
|
541
|
-
const entityFields = matchingEntity.Fields;
|
|
542
|
-
// Include primary keys and fields ending in ID for fallback
|
|
543
|
-
const relevantFields = entityFields
|
|
544
|
-
.filter(f => f.IsPrimaryKey || f.Name.endsWith('ID'))
|
|
545
|
-
.slice(0, 15)
|
|
546
|
-
.map(f => ({
|
|
547
|
-
name: f.Name,
|
|
548
|
-
type: f.Type,
|
|
549
|
-
isPrimaryKey: f.IsPrimaryKey
|
|
550
|
-
}));
|
|
551
|
-
results.push({
|
|
552
|
-
name: matchingEntity.Name,
|
|
553
|
-
schemaName: matchingEntity.SchemaName,
|
|
554
|
-
baseView: matchingEntity.BaseView,
|
|
555
|
-
fields: relevantFields
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
288
|
}
|
|
559
|
-
return results;
|
|
560
289
|
}
|
|
561
290
|
async syncQueryParameters(extractedParams) {
|
|
562
291
|
// Use the entity's provider instead of creating new Metadata instance
|
|
@@ -679,7 +408,7 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
679
408
|
}
|
|
680
409
|
}
|
|
681
410
|
catch (e) {
|
|
682
|
-
|
|
411
|
+
LogError(`Query "${this.Name}" - Failed to sync parameters:`, e);
|
|
683
412
|
throw e;
|
|
684
413
|
}
|
|
685
414
|
}
|
|
@@ -705,7 +434,7 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
705
434
|
}
|
|
706
435
|
}
|
|
707
436
|
catch (e) {
|
|
708
|
-
|
|
437
|
+
LogError('Failed to remove query parameters:', e);
|
|
709
438
|
throw e; // Re-throw since we're in a transaction
|
|
710
439
|
}
|
|
711
440
|
}
|
|
@@ -895,7 +624,7 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
895
624
|
}
|
|
896
625
|
}
|
|
897
626
|
catch (e) {
|
|
898
|
-
|
|
627
|
+
LogError(`Query "${this.Name}" - Failed to sync fields:`, e);
|
|
899
628
|
throw e;
|
|
900
629
|
}
|
|
901
630
|
}
|
|
@@ -959,7 +688,7 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
959
688
|
}
|
|
960
689
|
}
|
|
961
690
|
catch (e) {
|
|
962
|
-
|
|
691
|
+
LogError(`Query "${this.Name}" - Failed to sync entities:`, e);
|
|
963
692
|
throw e;
|
|
964
693
|
}
|
|
965
694
|
}
|
|
@@ -983,7 +712,7 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
983
712
|
}
|
|
984
713
|
}
|
|
985
714
|
catch (e) {
|
|
986
|
-
|
|
715
|
+
LogError('Failed to remove query fields:', e);
|
|
987
716
|
throw e;
|
|
988
717
|
}
|
|
989
718
|
}
|
|
@@ -1007,7 +736,7 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
1007
736
|
}
|
|
1008
737
|
}
|
|
1009
738
|
catch (e) {
|
|
1010
|
-
|
|
739
|
+
LogError('Failed to remove query entities:', e);
|
|
1011
740
|
throw e;
|
|
1012
741
|
}
|
|
1013
742
|
}
|
|
@@ -1035,7 +764,7 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
1035
764
|
async RefreshRelatedMetadata(refreshFromDB) {
|
|
1036
765
|
const md = this.ProviderToUse;
|
|
1037
766
|
if (refreshFromDB) {
|
|
1038
|
-
const globalMetadataProvider =
|
|
767
|
+
const globalMetadataProvider = Metadata.Provider;
|
|
1039
768
|
await globalMetadataProvider.Refresh(md); // we pass in our metadata provider because that is the connection we want to use if we are in the midst of a transaction
|
|
1040
769
|
if (globalMetadataProvider !== md) {
|
|
1041
770
|
// If the global metadata provider is different, we need to refresh it
|
|
@@ -1048,10 +777,8 @@ let QueryEntityExtended = class QueryEntityExtended extends core_entities_1.Quer
|
|
|
1048
777
|
this._queryParameters = md.QueryParameters.filter(p => p.QueryID === this.ID);
|
|
1049
778
|
}
|
|
1050
779
|
};
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
(0, global_1.RegisterClass)(core_1.BaseEntity, 'Queries')
|
|
780
|
+
QueryEntityExtended = __decorate([
|
|
781
|
+
RegisterClass(BaseEntity, 'Queries')
|
|
1054
782
|
], QueryEntityExtended);
|
|
1055
|
-
|
|
1056
|
-
exports.LoadQueryEntityServerSubClass = LoadQueryEntityServerSubClass;
|
|
783
|
+
export { QueryEntityExtended };
|
|
1057
784
|
//# sourceMappingURL=QueryEntity.server.js.map
|