@memberjunction/generic-database-provider 5.36.0 → 5.38.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.
@@ -15,7 +15,7 @@
15
15
  *
16
16
  * @module @memberjunction/generic-database-provider
17
17
  */
18
- import { DatabaseProviderBase, EntityFieldTSType, EntityPermissionType, InMemoryLocalStorageProvider, Metadata, RunView, QueryInfo, LocalCacheManager, LogError, LogStatus, LogStatusEx, StripStopWords, QueryCacheManager, AfterKeyNotSupportedError, IsKeysetPaginationOrderableType, } from '@memberjunction/core';
18
+ import { DatabaseProviderBase, EntityFieldTSType, EntityPermissionType, InMemoryLocalStorageProvider, Metadata, RunView, LocalCacheManager, LogError, LogStatus, LogStatusEx, StripStopWords, AfterKeyNotSupportedError, IsKeysetPaginationOrderableType, } from '@memberjunction/core';
19
19
  import { MJGlobal, SQLExpressionValidator, UUIDsEqual } from '@memberjunction/global';
20
20
  import { QueryPagingEngine } from './queryPagingEngine.js';
21
21
  // QueryParameterProcessor is now called internally by RenderPipeline
@@ -43,14 +43,6 @@ const GEO_EXTENDED_TYPES = new Set([
43
43
  'GeoCountry', 'GeoPostalCode', 'GeoLatitude', 'GeoLongitude'
44
44
  ]);
45
45
  export class GenericDatabaseProvider extends DatabaseProviderBase {
46
- constructor() {
47
- // Composition engine is now owned by RenderPipeline
48
- super(...arguments);
49
- /**************************************************************************/
50
- // InternalRunQuery — Shared Pipeline Implementation
51
- /**************************************************************************/
52
- this._queryCacheInitialized = false;
53
- }
54
46
  /**
55
47
  * Returns the active local storage provider, lazily creating an
56
48
  * {@link InMemoryLocalStorageProvider} if none has been set.
@@ -1128,13 +1120,11 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
1128
1120
  whereSQL = bHasWhere ? `${whereSQL} AND (${sExcludeSQL})` : `(${sExcludeSQL})`;
1129
1121
  bHasWhere = true;
1130
1122
  }
1131
- // 5. Row-Level Security
1132
- if (!entityInfo.UserExemptFromRowLevelSecurity(user, EntityPermissionType.Read)) {
1133
- const rlsWhereClause = entityInfo.GetUserRowLevelSecurityWhereClause(user, EntityPermissionType.Read, '');
1134
- if (rlsWhereClause && rlsWhereClause.length > 0) {
1135
- whereSQL = bHasWhere ? `${whereSQL} AND (${rlsWhereClause})` : `(${rlsWhereClause})`;
1136
- bHasWhere = true;
1137
- }
1123
+ // 5. Row-Level Security (exemption check is centralized in GetUserRowLevelSecurityWhereClause)
1124
+ const rlsWhereClause = entityInfo.GetUserRowLevelSecurityWhereClause(user, EntityPermissionType.Read, '');
1125
+ if (rlsWhereClause && rlsWhereClause.length > 0) {
1126
+ whereSQL = bHasWhere ? `${whereSQL} AND (${rlsWhereClause})` : `(${rlsWhereClause})`;
1127
+ bHasWhere = true;
1138
1128
  }
1139
1129
  // 6. Keyset (AfterKey) seek predicate — only on the data query, NOT the count query.
1140
1130
  // The count is the total matching the user-visible filters; the seek predicate is
@@ -1750,11 +1740,9 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
1750
1740
  bHasWhere = true;
1751
1741
  }
1752
1742
  }
1753
- if (!entityInfo.UserExemptFromRowLevelSecurity(user, EntityPermissionType.Read)) {
1754
- const rlsWhereClause = entityInfo.GetUserRowLevelSecurityWhereClause(user, EntityPermissionType.Read, '');
1755
- if (rlsWhereClause && rlsWhereClause.length > 0) {
1756
- whereSQL = bHasWhere ? `${whereSQL} AND (${rlsWhereClause})` : `(${rlsWhereClause})`;
1757
- }
1743
+ const rlsWhereClause = entityInfo.GetUserRowLevelSecurityWhereClause(user, EntityPermissionType.Read, '');
1744
+ if (rlsWhereClause && rlsWhereClause.length > 0) {
1745
+ whereSQL = bHasWhere ? `${whereSQL} AND (${rlsWhereClause})` : `(${rlsWhereClause})`;
1758
1746
  }
1759
1747
  return whereSQL;
1760
1748
  }
@@ -1960,45 +1948,45 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
1960
1948
  const errorResults = [];
1961
1949
  for (let i = 0; i < params.length; i++) {
1962
1950
  const item = params[i];
1963
- const queryInfo = this.resolveQueryInfo(item.params);
1964
- if (!queryInfo) {
1951
+ const query = this.resolveQuery(item.params);
1952
+ if (!query) {
1965
1953
  errorResults.push({ queryIndex: i, queryId: item.params.QueryID || '', status: 'error', errorMessage: `Query not found: ${item.params.QueryID || item.params.QueryName}` });
1966
1954
  continue;
1967
1955
  }
1968
- if (!queryInfo.UserCanRun(user)) {
1969
- errorResults.push({ queryIndex: i, queryId: queryInfo.ID, status: 'error', errorMessage: `User does not have permission to run query: ${queryInfo.Name}` });
1956
+ if (!query.UserCanRun(user)) {
1957
+ errorResults.push({ queryIndex: i, queryId: query.ID, status: 'error', errorMessage: `User does not have permission to run query: ${query.Name}` });
1970
1958
  continue;
1971
1959
  }
1972
1960
  if (!item.cacheStatus) {
1973
1961
  itemsWithoutCacheCheck.push({ index: i, item });
1974
1962
  continue;
1975
1963
  }
1976
- if (!queryInfo.CacheValidationSQL) {
1977
- itemsWithoutValidationSQL.push({ index: i, item, queryInfo });
1964
+ if (!query.CacheValidationSQL) {
1965
+ itemsWithoutValidationSQL.push({ index: i, item, query });
1978
1966
  continue;
1979
1967
  }
1980
- itemsNeedingCacheCheck.push({ index: i, item, queryInfo });
1968
+ itemsNeedingCacheCheck.push({ index: i, item, query });
1981
1969
  }
1982
1970
  const cacheStatusResults = await this.getBatchedQueryCacheStatus(itemsNeedingCacheCheck, contextUser);
1983
1971
  const staleItems = [];
1984
1972
  const currentResults = [];
1985
- for (const { index, item, queryInfo } of itemsNeedingCacheCheck) {
1973
+ for (const { index, item, query } of itemsNeedingCacheCheck) {
1986
1974
  const serverStatus = cacheStatusResults.get(index);
1987
1975
  if (!serverStatus || !serverStatus.success) {
1988
- errorResults.push({ queryIndex: index, queryId: queryInfo.ID, status: 'error', errorMessage: serverStatus?.errorMessage || 'Failed to get cache status' });
1976
+ errorResults.push({ queryIndex: index, queryId: query.ID, status: 'error', errorMessage: serverStatus?.errorMessage || 'Failed to get cache status' });
1989
1977
  continue;
1990
1978
  }
1991
1979
  if (this.isCacheCurrent(item.cacheStatus, serverStatus)) {
1992
- currentResults.push({ queryIndex: index, queryId: queryInfo.ID, status: 'current' });
1980
+ currentResults.push({ queryIndex: index, queryId: query.ID, status: 'current' });
1993
1981
  }
1994
1982
  else {
1995
- staleItems.push({ index, params: item.params, queryInfo });
1983
+ staleItems.push({ index, params: item.params, query });
1996
1984
  }
1997
1985
  }
1998
1986
  const fullQueryPromises = [
1999
1987
  ...itemsWithoutCacheCheck.map(({ index, item }) => this.runFullQueryAndReturnForQuery(item.params, index, 'stale', contextUser)),
2000
- ...itemsWithoutValidationSQL.map(({ index, item, queryInfo }) => this.runFullQueryAndReturnForQuery(item.params, index, 'no_validation', contextUser, queryInfo.ID)),
2001
- ...staleItems.map(({ index, params: queryParams, queryInfo }) => this.runFullQueryAndReturnForQuery(queryParams, index, 'stale', contextUser, queryInfo.ID)),
1988
+ ...itemsWithoutValidationSQL.map(({ index, item, query }) => this.runFullQueryAndReturnForQuery(item.params, index, 'no_validation', contextUser, query.ID)),
1989
+ ...staleItems.map(({ index, params: queryParams, query }) => this.runFullQueryAndReturnForQuery(queryParams, index, 'stale', contextUser, query.ID)),
2002
1990
  ];
2003
1991
  const fullQueryResults = await Promise.all(fullQueryPromises);
2004
1992
  const allResults = [...errorResults, ...currentResults, ...fullQueryResults];
@@ -2011,69 +1999,42 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2011
1999
  }
2012
2000
  }
2013
2001
  /**
2014
- * Resolves QueryInfo from RunQueryParams (by ID or Name+CategoryPath).
2015
- * Tries QueryEngine first for fresh data, falls back to ProviderBase cache.
2016
- */
2017
- resolveQueryInfo(params) {
2018
- const freshEntity = this.findQueryInEngine(params.QueryID, params.QueryName, params.CategoryID, params.CategoryPath);
2019
- if (freshEntity)
2020
- return this.refreshQueryInfoFromEntity(freshEntity);
2021
- if (params.QueryID)
2022
- return this.Queries.find(q => UUIDsEqual(q.ID, params.QueryID));
2023
- if (params.QueryName) {
2024
- const matchingQueries = this.Queries.filter(q => q.Name.trim().toLowerCase() === params.QueryName?.trim().toLowerCase());
2025
- if (matchingQueries.length === 0)
2026
- return undefined;
2027
- if (matchingQueries.length === 1)
2028
- return matchingQueries[0];
2029
- if (params.CategoryPath) {
2030
- const byPath = matchingQueries.find(q => q.CategoryPath.toLowerCase() === params.CategoryPath?.toLowerCase());
2031
- if (byPath)
2032
- return byPath;
2033
- }
2034
- if (params.CategoryID) {
2035
- const byId = matchingQueries.find(q => UUIDsEqual(q.CategoryID, params.CategoryID));
2036
- if (byId)
2037
- return byId;
2038
- }
2039
- return matchingQueries[0];
2040
- }
2041
- return undefined;
2042
- }
2043
- /**
2044
- * Searches QueryEngine for a fresh query entity.
2002
+ * Resolves a query from RunQueryParams (by ID or Name+CategoryPath).
2003
+ * Uses QueryEngine as the single source of truth for query metadata.
2045
2004
  */
2046
- findQueryInEngine(QueryID, QueryName, CategoryID, CategoryPath) {
2005
+ resolveQuery(params) {
2047
2006
  const engineQueries = QueryEngine.Instance?.Queries;
2048
2007
  if (!engineQueries || engineQueries.length === 0)
2049
- return null;
2050
- if (QueryID) {
2051
- const lower = QueryID.trim().toLowerCase();
2052
- return engineQueries.find(q => q.ID.trim().toLowerCase() === lower) ?? null;
2008
+ return undefined;
2009
+ if (params.QueryID) {
2010
+ return engineQueries.find(q => UUIDsEqual(q.ID, params.QueryID));
2053
2011
  }
2054
- if (QueryName) {
2055
- const lowerName = QueryName.trim().toLowerCase();
2012
+ if (params.QueryName) {
2013
+ const lowerName = params.QueryName.trim().toLowerCase();
2056
2014
  const matches = engineQueries.filter(q => q.Name.trim().toLowerCase() === lowerName);
2057
2015
  if (matches.length === 0)
2058
- return null;
2016
+ return undefined;
2059
2017
  if (matches.length === 1)
2060
2018
  return matches[0];
2061
- if (CategoryID) {
2062
- const byId = matches.find(q => q.CategoryID?.trim().toLowerCase() === CategoryID.trim().toLowerCase());
2019
+ if (params.CategoryPath) {
2020
+ const byPath = matches.find(q => q.CategoryPath.toLowerCase() === params.CategoryPath?.toLowerCase());
2021
+ if (byPath)
2022
+ return byPath;
2023
+ }
2024
+ if (params.CategoryID) {
2025
+ const byId = matches.find(q => UUIDsEqual(q.CategoryID, params.CategoryID));
2063
2026
  if (byId)
2064
2027
  return byId;
2065
- }
2066
- if (CategoryPath) {
2067
- const resolvedCategoryId = this.resolveCategoryPath(CategoryPath);
2028
+ const resolvedCategoryId = this.resolveCategoryPath(params.CategoryPath ?? '');
2068
2029
  if (resolvedCategoryId) {
2069
- const byPath = matches.find(q => UUIDsEqual(q.CategoryID, resolvedCategoryId));
2070
- if (byPath)
2071
- return byPath;
2030
+ const byResolvedPath = matches.find(q => UUIDsEqual(q.CategoryID, resolvedCategoryId));
2031
+ if (byResolvedPath)
2032
+ return byResolvedPath;
2072
2033
  }
2073
2034
  }
2074
2035
  return matches[0];
2075
2036
  }
2076
- return null;
2037
+ return undefined;
2077
2038
  }
2078
2039
  /**
2079
2040
  * Validates that a query can be executed by the given user. Checks both permissions
@@ -2081,7 +2042,7 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2081
2042
  * emits a console warning but allows execution to proceed, enabling query testing
2082
2043
  * before formal approval.
2083
2044
  *
2084
- * @param query - The resolved QueryInfo to validate
2045
+ * @param query - The resolved MJQueryEntityExtended to validate
2085
2046
  * @param contextUser - The user attempting to execute the query
2086
2047
  * @throws Error if the user does not have permission to run the query
2087
2048
  */
@@ -2094,20 +2055,6 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2094
2055
  LogStatus(`WARNING: Executing query '${query.Name}' (ID: ${query.ID}) with status '${query.Status}'. Query has not been approved.`);
2095
2056
  }
2096
2057
  }
2097
- /**
2098
- * Creates a fresh QueryInfo from a MJQueryEntity and patches the ProviderBase cache.
2099
- */
2100
- refreshQueryInfoFromEntity(entity) {
2101
- const freshInfo = new QueryInfo(entity.GetAll());
2102
- const existingIndex = this.Queries.findIndex(q => UUIDsEqual(q.ID, freshInfo.ID));
2103
- if (existingIndex >= 0) {
2104
- this.Queries[existingIndex] = freshInfo;
2105
- }
2106
- else {
2107
- this.Queries.push(freshInfo);
2108
- }
2109
- return freshInfo;
2110
- }
2111
2058
  /**
2112
2059
  * Resolves a category path string to a QueryCategoryInfo ID.
2113
2060
  */
@@ -2134,9 +2081,9 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2134
2081
  const results = new Map();
2135
2082
  if (items.length === 0)
2136
2083
  return results;
2137
- const promises = items.map(async ({ index, queryInfo }) => {
2084
+ const promises = items.map(async ({ index, query }) => {
2138
2085
  try {
2139
- const rows = await this.ExecuteSQL(queryInfo.CacheValidationSQL, undefined, undefined, contextUser);
2086
+ const rows = await this.ExecuteSQL(query.CacheValidationSQL, undefined, undefined, contextUser);
2140
2087
  if (rows && rows.length > 0) {
2141
2088
  const row = rows[0];
2142
2089
  results.set(index, {
@@ -2179,13 +2126,9 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2179
2126
  rowCount: result.Results.length,
2180
2127
  };
2181
2128
  }
2182
- get QueryCacheMgr() {
2183
- if (!this._queryCacheInitialized) {
2184
- QueryCacheManager.Instance.Init(this.InstanceConnectionString);
2185
- this._queryCacheInitialized = true;
2186
- }
2187
- return QueryCacheManager.Instance;
2188
- }
2129
+ /**************************************************************************/
2130
+ // InternalRunQuery — Shared Pipeline Implementation
2131
+ /**************************************************************************/
2189
2132
  /**
2190
2133
  * Full query execution pipeline: resolve → validate → compose → template → cache check →
2191
2134
  * execute → paginate → audit → cache store. Platform providers inherit this; only
@@ -2203,7 +2146,6 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2203
2146
  const { finalSQL, appliedParameters } = this.processQueryParameters(query, params.Parameters, contextUser);
2204
2147
  // Execute query — use SQL-level paging when requested, else fetch all rows
2205
2148
  const useSQLPaging = QueryPagingEngine.ShouldPage(params.StartRow, params.MaxRows);
2206
- const cacheConfig = query.CacheConfig;
2207
2149
  let paginatedResult;
2208
2150
  let totalRowCount;
2209
2151
  let executionTime;
@@ -2215,43 +2157,19 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2215
2157
  return pagedCacheHit;
2216
2158
  }
2217
2159
  const paging = QueryPagingEngine.WrapWithPaging(finalSQL, params.StartRow, params.MaxRows, this.PlatformKey);
2218
- // Check count cache — skip COUNT SQL if we have a cached total
2219
- const cachedCount = cacheConfig?.enabled
2220
- ? await this.QueryCacheMgr.GetTotalRowCount(query, params.Parameters || {})
2221
- : null;
2160
+ // Execute data + count queries in parallel
2222
2161
  const start = Date.now();
2223
- if (cachedCount != null) {
2224
- // Only execute data query — count is cached
2225
- const dataResult = await this.ExecuteSQL(paging.DataSQL, undefined, undefined, contextUser);
2226
- executionTime = Date.now() - start;
2227
- if (!dataResult)
2228
- throw new Error('Error executing paged query SQL');
2229
- paginatedResult = dataResult;
2230
- totalRowCount = cachedCount;
2231
- }
2232
- else {
2233
- // Execute data + count queries in parallel
2234
- const [dataResult, countResult] = await Promise.all([
2235
- this.ExecuteSQL(paging.DataSQL, undefined, undefined, contextUser),
2236
- this.ExecuteSQL(paging.CountSQL, undefined, undefined, contextUser),
2237
- ]);
2238
- executionTime = Date.now() - start;
2239
- if (!dataResult)
2240
- throw new Error('Error executing paged query SQL');
2241
- paginatedResult = dataResult;
2242
- totalRowCount = countResult?.[0]?.TotalRowCount != null
2243
- ? Number(countResult[0].TotalRowCount)
2244
- : paginatedResult.length;
2245
- // Cache the count for subsequent page requests (fire-and-forget)
2246
- if (cacheConfig?.enabled) {
2247
- void this.QueryCacheMgr.SetTotalRowCount(query, params.Parameters || {}, totalRowCount);
2248
- }
2249
- }
2250
- // Cache the paged results (fire-and-forget)
2251
- if (cacheConfig?.enabled) {
2252
- void this.QueryCacheMgr.SetPaged(query, params.Parameters || {}, params.StartRow, params.MaxRows, paginatedResult);
2253
- void this.QueryCacheMgr.InvalidateWithDependents(query);
2254
- }
2162
+ const [dataResult, countResult] = await Promise.all([
2163
+ this.ExecuteSQL(paging.DataSQL, undefined, undefined, contextUser),
2164
+ this.ExecuteSQL(paging.CountSQL, undefined, undefined, contextUser),
2165
+ ]);
2166
+ executionTime = Date.now() - start;
2167
+ if (!dataResult)
2168
+ throw new Error('Error executing paged query SQL');
2169
+ paginatedResult = dataResult;
2170
+ totalRowCount = countResult?.[0]?.TotalRowCount != null
2171
+ ? Number(countResult[0].TotalRowCount)
2172
+ : paginatedResult.length;
2255
2173
  }
2256
2174
  else {
2257
2175
  // Check full-result cache before executing
@@ -2327,30 +2245,7 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2327
2245
  ErrorMessage: validation.error || 'SQL validation failed',
2328
2246
  };
2329
2247
  }
2330
- // Check ad-hoc cache if opt-in TTL is provided
2331
- const adhocTTL = params.AdhocCacheTTLMinutes;
2332
- if (adhocTTL != null && adhocTTL > 0) {
2333
- const cached = await this.QueryCacheMgr.GetAdhoc(params.SQL, adhocTTL);
2334
- if (cached) {
2335
- const { paginatedResult, totalRowCount } = this.applyQueryPagination(cached.results, params);
2336
- return {
2337
- Success: true,
2338
- QueryID: '',
2339
- QueryName: 'Ad-Hoc Query',
2340
- Results: paginatedResult,
2341
- RowCount: paginatedResult.length,
2342
- TotalRowCount: totalRowCount,
2343
- ExecutionTime: 0,
2344
- ErrorMessage: '',
2345
- CacheHit: true,
2346
- };
2347
- }
2348
- }
2349
2248
  const { result, executionTime } = await this.executeQueryWithTiming(params.SQL, contextUser);
2350
- // Store in ad-hoc cache if opt-in (fire-and-forget)
2351
- if (adhocTTL != null && adhocTTL > 0) {
2352
- void this.QueryCacheMgr.SetAdhoc(params.SQL, adhocTTL, result);
2353
- }
2354
2249
  const { paginatedResult, totalRowCount } = this.applyQueryPagination(result, params);
2355
2250
  return {
2356
2251
  Success: true,
@@ -2380,10 +2275,10 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2380
2275
  }
2381
2276
  /**
2382
2277
  * Finds a query from RunQueryParams and validates user permissions.
2383
- * Uses `resolveQueryInfo()` for lookup and `ValidateQueryForExecution()` for permissions.
2278
+ * Uses `resolveQuery()` for lookup and `ValidateQueryForExecution()` for permissions.
2384
2279
  */
2385
2280
  findAndValidateQuery(params, contextUser) {
2386
- const query = this.resolveQueryInfo(params);
2281
+ const query = this.resolveQuery(params);
2387
2282
  if (!query) {
2388
2283
  let errorDetails = 'Query not found';
2389
2284
  if (params.QueryName) {
@@ -2415,8 +2310,12 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2415
2310
  Platform: this.PlatformKey,
2416
2311
  ContextUser: contextUser,
2417
2312
  Parameters: parameters,
2418
- UsesTemplate: query.UsesTemplate,
2419
- QueryInfo: query,
2313
+ UsesTemplate: query.UsesTemplate ?? false,
2314
+ QueryInfo: {
2315
+ SQL: query.SQL ?? '',
2316
+ UsesTemplate: query.UsesTemplate ?? false,
2317
+ Parameters: query.QueryParameters,
2318
+ },
2420
2319
  });
2421
2320
  if (!result.HasCompositions && !query.UsesTemplate && parameters && Object.keys(parameters).length > 0) {
2422
2321
  LogStatus('Warning: Parameters provided but query does not use templates. Parameters will be ignored.');
@@ -2474,11 +2373,15 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2474
2373
  * pipeline. Supports inline dependencies for transient query testing.
2475
2374
  */
2476
2375
  resolveSpecParameters(spec, contextUser) {
2376
+ // QueryExecutionSpec.ParameterDefinitions is QueryParameterInfo[] (MJCore),
2377
+ // while RenderContext expects MJQueryParameterEntity[] (core-entities).
2378
+ // Both share the structural shape the processor needs (Name, DataType, IsRequired).
2379
+ const paramDefs = spec.ParameterDefinitions;
2477
2380
  const result = RenderPipeline.Run(spec.SQL, {
2478
2381
  Platform: this.PlatformKey,
2479
2382
  ContextUser: contextUser,
2480
2383
  Parameters: spec.Parameters,
2481
- ParameterDefinitions: spec.ParameterDefinitions,
2384
+ ParameterDefinitions: paramDefs,
2482
2385
  UsesTemplate: spec.UsesTemplate,
2483
2386
  Dependencies: spec.Dependencies,
2484
2387
  OriginalSQL: spec.SQL,
@@ -2492,62 +2395,18 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2492
2395
  // wrapWithMaxRows is now handled by RenderPipeline.applyMaxRows
2493
2396
  /**
2494
2397
  * Checks the query cache for existing results and returns them if valid.
2398
+ * Currently always returns null (query caching is not active).
2495
2399
  */
2496
- async checkQueryCache(query, params, appliedParameters) {
2497
- const cacheConfig = query.CacheConfig;
2498
- if (!cacheConfig?.enabled) {
2499
- return null;
2500
- }
2501
- const cached = await this.QueryCacheMgr.Get(query, params.Parameters || {});
2502
- if (!cached) {
2503
- return null;
2504
- }
2505
- LogStatus(`Cache hit for query ${query.Name} (${query.ID})`);
2506
- const { paginatedResult, totalRowCount } = this.applyQueryPagination(cached.results, params);
2507
- return {
2508
- Success: true,
2509
- QueryID: query.ID,
2510
- QueryName: query.Name,
2511
- Results: paginatedResult,
2512
- RowCount: paginatedResult.length,
2513
- TotalRowCount: totalRowCount,
2514
- ExecutionTime: 0,
2515
- ErrorMessage: '',
2516
- AppliedParameters: appliedParameters,
2517
- CacheHit: true,
2518
- CacheTTLRemaining: cached.ttlRemainingMs,
2519
- };
2400
+ async checkQueryCache(_query, _params, _appliedParameters) {
2401
+ return null;
2520
2402
  }
2521
2403
  /**
2522
2404
  * Checks the paged cache for a specific page of query results.
2523
2405
  * Returns a full RunQueryResult on hit, null on miss.
2406
+ * Currently always returns null (query caching is not active).
2524
2407
  */
2525
- async checkPagedQueryCache(query, params, appliedParameters) {
2526
- const cacheConfig = query.CacheConfig;
2527
- if (!cacheConfig?.enabled)
2528
- return null;
2529
- const cached = await this.QueryCacheMgr.GetPaged(query, params.Parameters || {}, params.StartRow, params.MaxRows);
2530
- if (!cached)
2531
- return null;
2532
- // Also try to get the cached count
2533
- const cachedCount = await this.QueryCacheMgr.GetTotalRowCount(query, params.Parameters || {});
2534
- const totalRowCount = cachedCount ?? cached.results.length;
2535
- LogStatus(`Paged cache hit for query ${query.Name} (${query.ID}) page ${params.StartRow}+${params.MaxRows}`);
2536
- return {
2537
- Success: true,
2538
- QueryID: query.ID,
2539
- QueryName: query.Name,
2540
- Results: cached.results,
2541
- RowCount: cached.results.length,
2542
- TotalRowCount: totalRowCount,
2543
- PageNumber: Math.floor(params.StartRow / params.MaxRows) + 1,
2544
- PageSize: params.MaxRows,
2545
- ExecutionTime: 0,
2546
- ErrorMessage: '',
2547
- AppliedParameters: appliedParameters,
2548
- CacheHit: true,
2549
- CacheTTLRemaining: cached.ttlRemainingMs,
2550
- };
2408
+ async checkPagedQueryCache(_query, _params, _appliedParameters) {
2409
+ return null;
2551
2410
  }
2552
2411
  /**
2553
2412
  * Executes the query SQL and tracks execution time.
@@ -2603,16 +2462,10 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2603
2462
  }
2604
2463
  /**
2605
2464
  * Caches query results if caching is enabled for the query.
2606
- * Caches the full result set (before pagination).
2465
+ * Currently a no-op (query caching is not active).
2607
2466
  */
2608
- async cacheQueryResults(query, parameters, results) {
2609
- const cacheConfig = query.CacheConfig;
2610
- if (!cacheConfig?.enabled) {
2611
- return;
2612
- }
2613
- await this.QueryCacheMgr.Set(query, parameters, results);
2614
- await this.QueryCacheMgr.InvalidateWithDependents(query);
2615
- LogStatus(`Cached results for query ${query.Name} (${query.ID})`);
2467
+ async cacheQueryResults(_query, _parameters, _results) {
2468
+ // No-op: query caching has been removed
2616
2469
  }
2617
2470
  /**************************************************************************/
2618
2471
  // Load — Shared Implementation
@@ -2631,9 +2484,9 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2631
2484
  const quotes = pk.NeedsQuotes ? "'" : '';
2632
2485
  return `${this.QuoteIdentifier(pk.CodeName)}=${quotes}${val.Value}${quotes}`;
2633
2486
  }).join(' AND ');
2634
- // Append Read RLS filter if user is not exempt
2487
+ // Append Read RLS filter (exemption check is centralized in GetUserRowLevelSecurityWhereClause)
2635
2488
  let fullWhere = where;
2636
- if (user && !entityInfo.UserExemptFromRowLevelSecurity(user, EntityPermissionType.Read)) {
2489
+ if (user) {
2637
2490
  const rlsWhereClause = entityInfo.GetUserRowLevelSecurityWhereClause(user, EntityPermissionType.Read, '');
2638
2491
  if (rlsWhereClause && rlsWhereClause.length > 0) {
2639
2492
  fullWhere = `${where} AND (${rlsWhereClause})`;
@@ -2693,9 +2546,6 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2693
2546
  */
2694
2547
  async CheckRecordRLS(entity, user, type) {
2695
2548
  const entityInfo = entity.EntityInfo;
2696
- if (entityInfo.UserExemptFromRowLevelSecurity(user, type)) {
2697
- return true;
2698
- }
2699
2549
  const rlsWhereClause = entityInfo.GetUserRowLevelSecurityWhereClause(user, type, '');
2700
2550
  if (!rlsWhereClause || rlsWhereClause.length === 0) {
2701
2551
  return true;
@@ -2715,9 +2565,6 @@ export class GenericDatabaseProvider extends DatabaseProviderBase {
2715
2565
  */
2716
2566
  async CheckCreateRLS(entity, user) {
2717
2567
  const entityInfo = entity.EntityInfo;
2718
- if (entityInfo.UserExemptFromRowLevelSecurity(user, EntityPermissionType.Create)) {
2719
- return true;
2720
- }
2721
2568
  const rlsWhereClause = entityInfo.GetUserRowLevelSecurityWhereClause(user, EntityPermissionType.Create, '');
2722
2569
  if (!rlsWhereClause || rlsWhereClause.length === 0) {
2723
2570
  return true;