UNPKG

@itwin/core-backend

Version:
177 lines • 7.63 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { assert } from "chai"; import { QueryBinder, QueryRowFormat } from "@itwin/core-common"; import { SnapshotDb } from "../../core-backend"; import { IModelTestUtils } from "../IModelTestUtils"; import { SequentialLogMatcher } from "../SequentialLogMatcher"; // cspell:ignore mirukuru ibim async function executeQuery(iModel, ecsql, bindings, abbreviateBlobs) { const rows = []; for await (const queryRow of iModel.createQueryReader(ecsql, QueryBinder.from(bindings), { rowFormat: QueryRowFormat.UseJsPropertyNames, abbreviateBlobs })) { rows.push(queryRow.toRow()); } return rows; } describe("Common table expression support in ECSQL", () => { let imodel1; before(async () => { imodel1 = SnapshotDb.openFile(IModelTestUtils.resolveAssetFile("test.bim")); }); after(async () => { imodel1.close(); }); it("collect base properties recursively", async () => { const query = ` WITH RECURSIVE base_classes (aId, aParentId, aPath, aDepth) AS ( SELECT c.ECInstanceId, null, c.Name, 0 FROM meta.ECClassDef c WHERE c.Name=? UNION ALL SELECT c.ECInstanceId, cbc.TargetECInstanceId, aPath || '/' || c.Name, aDepth + 1 FROM meta.ECClassDef c JOIN meta.ClassHasBaseClasses cbc ON cbc.SourceECInstanceId = c.ECInstanceId JOIN base_classes ON aId = cbc.TargetECInstanceId ORDER BY 1 ) SELECT p.Name prop from base_classes join meta.ECPropertyDef p on p.Class.id = aId GROUP BY p.Name`; const rows = await executeQuery(imodel1, query, ["Element"]); const expected = ["Angle", "BaseModel", "BBoxHigh", "BBoxLow", "Border", "BorderTemplate", "Category", "CategorySelector", "CodeScope", "CodeSpec", "CodeValue", "Data", "Description", "DisplayStyle", "DrawingModel", "Enabled", "Extents", "EyePoint", "FederationGuid", "Flags", "FocusDistance", "Format", "GeometryStream", "Height", "InSpatialIndex", "IsCameraOn", "IsPrivate", "JsonProperties", "LastMod", "LensAngle", "Model", "ModelSelector", "Name", "Origin", "PaletteName", "Parent", "Pitch", "Properties", "Rank", "Recipe", "RepositoryGuid", "Roll", "Rotation", "RotationAngle", "Scale", "SheetTemplate", "Type", "TypeDefinition", "Url", "UserLabel", "View", "ViewAttachment", "Width", "Yaw"]; const actual = rows.map((r) => r.prop); assert.sameOrderedMembers(actual, expected); }); it("generate mandelbrot set", async () => { const rows = await executeQuery(imodel1, ` WITH RECURSIVE [xaxis]([x]) AS( VALUES (- 2.0) UNION ALL SELECT [x] + 0.05 FROM [xaxis] WHERE [x] < 1.2 ), [yaxis]([y]) AS( VALUES (- 1.0) UNION ALL SELECT [y] + 0.1 FROM [yaxis] WHERE [y] < 1.0 ), [m]([iter], [cx], [cy], [x], [y]) AS( SELECT 0, [x], [y], 0.0, 0.0 FROM [xaxis], [yaxis] UNION ALL SELECT [iter] + 1, [cx], [cy], [x] * [x] - [y] * [y] + [cx], 2.0 * [x] * [y] + [cy] FROM [m] WHERE ([x] * [x] + [y] * [y]) < 4.0 AND [iter] < 28 ), [m2]([iter], [cx], [cy]) AS( SELECT MAX ([iter]), [cx], [cy] FROM [m] GROUP BY [cx], [cy] ), [a]([t]) AS( SELECT GROUP_CONCAT (SUBSTR (' .+*#', 1 + (CASE WHEN [iter] / 7 > 4 THEN 4 ELSE [iter] / 7 END), 1), '') FROM [m2] GROUP BY [cy] ) SELECT GROUP_CONCAT (RTRIM ([t]), CHAR (0xa)) mandelbrot_set FROM [a]; `); const expected = " ....#\n" + " ..#*..\n" + " ..+####+.\n" + " .......+####.... +\n" + " ..##+*##########+.++++\n" + " .+.##################+.\n" + " .............+###################+.+\n" + " ..++..#.....*#####################+.\n" + " ...+#######++#######################.\n" + " ....+*################################.\n" + " #############################################...\n" + " ....+*################################.\n" + " ...+#######++#######################.\n" + " ..++..#.....*#####################+.\n" + " .............+###################+.+\n" + " .+.##################+.\n" + " ..##+*##########+.++++\n" + " .......+####.... +\n" + " ..+####+.\n" + " ..#*..\n" + " ....#\n" + " +."; assert(rows[0].mandelbrot_set === expected); }); it("basic cte test", async () => { let rows = []; rows = await executeQuery(imodel1, ` WITH RECURSIVE cnt (x,y) AS ( SELECT 100, 200 UNION ALL SELECT x+1, 200 FROM cnt WHERE x<210 ) SELECT * from cnt`); assert(rows.length === 111); rows = await executeQuery(imodel1, ` WITH RECURSIVE cnt (x,y) AS ( SELECT 100, 200 ) SELECT * from cnt`); let slm = new SequentialLogMatcher(); // these two are generated by sqlite slm.append().error().category("ECDb").message(/BE_SQLITE_ERROR duplicate WITH table name/gm); assert(rows.length === 1); try { rows = await executeQuery(imodel1, ` WITH cte_1 (a,b,c) AS ( SELECT 100, 400, 300 ), cte_1 (a,b,c) AS ( SELECT 100, 400, 300 ) SELECT * from cte_1`); assert(false); } catch { assert(true); // should fail as cte_1 is used for two ct expression. } assert.isTrue(slm.finishAndDispose()); slm = new SequentialLogMatcher(); // these two are generated by ECSQL. Its not clear why this message is logged twice. slm.append().error().category("ECDb").message(/Common table 'cte_1' has 3 values for columns 2/gm); try { rows = await executeQuery(imodel1, ` WITH cte_1 (a,b,c) AS ( SELECT 100, 400 ) SELECT * from cte_1`); assert(false); } catch { assert(true); // number are to ct expression does not match select } assert.isTrue(slm.finishAndDispose()); }); }); //# sourceMappingURL=CTE.test.js.map