diff --git a/core/actions/assertion_test.ts b/core/actions/assertion_test.ts index 9deeedad8..54cc5d943 100644 --- a/core/actions/assertion_test.ts +++ b/core/actions/assertion_test.ts @@ -306,6 +306,38 @@ actions: ); }); + test(`multiline rowConditions compile to single-line failing_row_condition literals`, () => { + // Regression test for #2201: a multiline rowConditions entry must not produce a + // multiline single-quoted string literal, which BigQuery rejects. + const projectDir = tmpDirFixture.createNewTmpDir(); + fs.writeFileSync(path.join(projectDir, "workflow_settings.yaml"), VALID_WORKFLOW_SETTINGS_YAML); + fs.mkdirSync(path.join(projectDir, "definitions")); + fs.writeFileSync( + path.join(projectDir, "definitions/test.sqlx"), + `config { + type: "table", + assertions: { + rowConditions: ["a > 0\n AND b > 0"] + } + } + SELECT 1 AS a, 1 AS b` + ); + + const result = runMainInVm(coreExecutionRequestFromPath(projectDir)); + + expect(result.compile.compiledGraph.graphErrors.compilationErrors).deep.equals([]); + const rowConditionsAssertion = result.compile.compiledGraph.assertions.find( + assertion => assertion.target.name === "defaultDataset_test_assertions_rowConditions" + ); + // The failing_row_condition string literal has its newline escaped, so the + // single-quoted literal stays on one line... + expect(rowConditionsAssertion.query).contains( + "'a > 0\\n AND b > 0' AS failing_row_condition" + ); + // ...while the WHERE NOT (...) clause keeps the raw, multiline expression. + expect(rowConditionsAssertion.query).contains("WHERE NOT (a > 0\n AND b > 0)"); + }); + suite("Assertions as dependencies", ({ beforeEach }) => { [ WorkflowSettingsTemplates.bigquery, diff --git a/core/compilation_sql/index.ts b/core/compilation_sql/index.ts index 4639a5453..37c2bd851 100644 --- a/core/compilation_sql/index.ts +++ b/core/compilation_sql/index.ts @@ -15,8 +15,17 @@ export class CompilationSql { } public sqlString(stringContents: string) { - // Escape escape characters, then escape single quotes, then wrap the string in single quotes. - return `'${stringContents.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`; + // Escape escape characters, then single quotes, then newlines/carriage-returns, + // and wrap the string in single quotes. BigQuery single-quoted string literals + // cannot span multiple lines, so a raw newline becomes the two-char \n escape + // (which parses back to a newline, keeping the literal single-line). Backslash + // escaping runs first so the escape sequences introduced here are not themselves + // doubled. + return `'${stringContents + .replace(/\\/g, "\\\\") + .replace(/'/g, "\\'") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r")}'`; } public indexAssertion(dataset: string, indexCols: string[]) {