From 275ed59bd13baa617979cfcebd86865928df97de Mon Sep 17 00:00:00 2001 From: vdelacruzb Date: Thu, 3 Sep 2026 15:10:34 +0200 Subject: [PATCH 1/6] fix(sf): insert library bundles literally when inlining them [sc-572077] build_modules.js inlined each library with text.replace(new RegExp(library, 'g'), libraryContent) and String.replace interprets $ sequences in the replacement string, so any $&, $` or $' inside a bundle was expanded rather than inserted. $& means the matched substring, which here is the placeholder itself. One occurrence across the 39 Snowflake bundles: h3_polyfill, in bignumber.js's number formatter (bundled via polyclip-ts), shipped as f.replace(new RegExp("\\d{"+l+"}\\B","g"),"@@SF_LIBRARY_H3_POLYFILL@@"+...) instead of "$&"+... . The branch needs toFormat with fractionGroupSize, which nothing in the toolbox calls, so there is no known runtime impact -- but it is corrupted code in a shipped bundle, and it breaks the correspondence between the source maps added in sc-571986 and the code they map to: the map's sourcesContent has "$&" where modules.sql has the placeholder. $1-$9 are left literal because the pattern has no capture groups, so the $N sequence in the statistics bundle was never affected. Fixed with a replacer function in both scripts that inline libraries. build_share.js does not inline libraries and is unaffected. Verified: h3_polyfill as inlined in the generated modules.sql is now byte-identical to libraries/javascript/build/h3_polyfill.js, "$&" is present, no placeholder leaked into a JS body, and no library placeholder is left unresolved anywhere in modules.sql. Co-Authored-By: Claude Opus 5 (1M context) --- clouds/snowflake/common/build_modules.js | 4 +++- clouds/snowflake/common/build_native_app_setup_script.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/clouds/snowflake/common/build_modules.js b/clouds/snowflake/common/build_modules.js index 06dcf05da..a2abfbd33 100755 --- a/clouds/snowflake/common/build_modules.js +++ b/clouds/snowflake/common/build_modules.js @@ -166,7 +166,9 @@ function apply_replacements (text) { const libraryPath = path.join(libsBuildDir, libraryName); if (fs.existsSync(libraryPath)) { const libraryContent = fs.readFileSync(libraryPath).toString(); - text = text.replace(new RegExp(library, 'g'), libraryContent); + // A replacer function, so $&, $` and $' inside a bundle are inserted + // literally instead of being expanded as replacement patterns. + text = text.replace(new RegExp(library, 'g'), () => libraryContent); } else { console.log(`Warning: library "${libraryName}" does not exist. Run "make build-libraries" with the same filters.`); diff --git a/clouds/snowflake/common/build_native_app_setup_script.js b/clouds/snowflake/common/build_native_app_setup_script.js index cb0c0677e..1c8bdda94 100755 --- a/clouds/snowflake/common/build_native_app_setup_script.js +++ b/clouds/snowflake/common/build_native_app_setup_script.js @@ -28,7 +28,9 @@ function apply_replacements (text) { const libraryPath = path.join(libsBuildDir, libraryName); if (fs.existsSync(libraryPath)) { const libraryContent = fs.readFileSync(libraryPath).toString(); - text = text.replace(new RegExp(library, 'g'), libraryContent); + // A replacer function, so $&, $` and $' inside a bundle are inserted + // literally instead of being expanded as replacement patterns. + text = text.replace(new RegExp(library, 'g'), () => libraryContent); } else { console.log(`Warning: library "${libraryName}" does not exist. Run "make build-libraries" with the same filters.`); From ad47c1869df3649994d1e6dc6b969cd906aa93be Mon Sep 17 00:00:00 2001 From: vdelacruzb Date: Thu, 3 Sep 2026 16:02:24 +0200 Subject: [PATCH 2/6] fix(pg,ora): insert library bundles literally when inlining them [sc-572077] Postgres and Oracle inline JavaScript libraries into their SQL with the same String.replace(pattern, content) call as Snowflake, so they carry the same defect: $&, $` and $' inside a bundle are expanded as replacement patterns instead of being inserted literally. Neither manifests today. Postgres is on turf 6.3.0 with no bignumber.js in its tree and its only bundle (h3.js) has no $ sequences; Oracle's h3.js and quadbin.js are equally clean. But Snowflake's instance became real through a routine dependency bump, not a code change -- turf 7 pulled in polyclip-ts and bignumber.js -- so the same upgrade here would silently reintroduce it. Oracle's call was split across three lines, which is why it did not show up in the first sweep; it now reads the bundle into a named constant first, matching the other clouds. BigQuery deliberately unchanged: its substitution inserts a GCS bucket path rather than code, because BigQuery loads the JavaScript from a bucket instead of inlining it. Verified no behavioural change: modules.sql built before and after the fix is byte-identical for both clouds (postgres 2,232,427 B, oracle 195,122 B, same sha256 either way), and lint-common is clean for both. Co-Authored-By: Claude Opus 5 (1M context) --- clouds/oracle/common/build_modules.js | 8 ++++---- clouds/postgres/common/build_modules.js | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/clouds/oracle/common/build_modules.js b/clouds/oracle/common/build_modules.js index 4b866328d..da091c147 100755 --- a/clouds/oracle/common/build_modules.js +++ b/clouds/oracle/common/build_modules.js @@ -210,10 +210,10 @@ function apply_replacements (text) { ); process.exit(1); } - text = text.replace( - new RegExp(library, 'g'), - fs.readFileSync(file).toString() - ); + // A replacer function, so $&, $` and $' inside a bundle are inserted + // literally instead of being expanded as replacement patterns. + const libraryContent = fs.readFileSync(file).toString(); + text = text.replace(new RegExp(library, 'g'), () => libraryContent); } const replacements = process.env.REPLACEMENTS.split(' '); for (const replacement of replacements) { diff --git a/clouds/postgres/common/build_modules.js b/clouds/postgres/common/build_modules.js index 7ad0eb58d..0765b2152 100755 --- a/clouds/postgres/common/build_modules.js +++ b/clouds/postgres/common/build_modules.js @@ -188,7 +188,9 @@ function apply_replacements (text) { const libraryPath = path.join(libsBuildDir, libraryName); if (fs.existsSync(libraryPath)) { const libraryContent = fs.readFileSync(libraryPath).toString(); - text = text.replace(new RegExp(library, 'g'), libraryContent); + // A replacer function, so $&, $` and $' inside a bundle are inserted + // literally instead of being expanded as replacement patterns. + text = text.replace(new RegExp(library, 'g'), () => libraryContent); } else { console.log(`Warning: library "${libraryName}" does not exist. Run "make build-libraries" with the same filters.`); From e976988cbab7f35fdb59e0b7eecaea4755d9ebaf Mon Sep 17 00:00:00 2001 From: vdelacruzb Date: Thu, 3 Sep 2026 16:44:36 +0200 Subject: [PATCH 3/6] chore: trim the inlining comments to one line [sc-572077] Matching the surrounding comment density. Co-Authored-By: Claude Opus 5 (1M context) --- clouds/oracle/common/build_modules.js | 3 +-- clouds/postgres/common/build_modules.js | 3 +-- clouds/snowflake/common/build_modules.js | 3 +-- clouds/snowflake/common/build_native_app_setup_script.js | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/clouds/oracle/common/build_modules.js b/clouds/oracle/common/build_modules.js index da091c147..f0db08f37 100755 --- a/clouds/oracle/common/build_modules.js +++ b/clouds/oracle/common/build_modules.js @@ -210,8 +210,7 @@ function apply_replacements (text) { ); process.exit(1); } - // A replacer function, so $&, $` and $' inside a bundle are inserted - // literally instead of being expanded as replacement patterns. + // A replacer function, so $&, $` and $' in a bundle are inserted literally const libraryContent = fs.readFileSync(file).toString(); text = text.replace(new RegExp(library, 'g'), () => libraryContent); } diff --git a/clouds/postgres/common/build_modules.js b/clouds/postgres/common/build_modules.js index 0765b2152..44758ccc8 100755 --- a/clouds/postgres/common/build_modules.js +++ b/clouds/postgres/common/build_modules.js @@ -188,8 +188,7 @@ function apply_replacements (text) { const libraryPath = path.join(libsBuildDir, libraryName); if (fs.existsSync(libraryPath)) { const libraryContent = fs.readFileSync(libraryPath).toString(); - // A replacer function, so $&, $` and $' inside a bundle are inserted - // literally instead of being expanded as replacement patterns. + // A replacer function, so $&, $` and $' in a bundle are inserted literally text = text.replace(new RegExp(library, 'g'), () => libraryContent); } else { diff --git a/clouds/snowflake/common/build_modules.js b/clouds/snowflake/common/build_modules.js index a2abfbd33..88279d42e 100755 --- a/clouds/snowflake/common/build_modules.js +++ b/clouds/snowflake/common/build_modules.js @@ -166,8 +166,7 @@ function apply_replacements (text) { const libraryPath = path.join(libsBuildDir, libraryName); if (fs.existsSync(libraryPath)) { const libraryContent = fs.readFileSync(libraryPath).toString(); - // A replacer function, so $&, $` and $' inside a bundle are inserted - // literally instead of being expanded as replacement patterns. + // A replacer function, so $&, $` and $' in a bundle are inserted literally text = text.replace(new RegExp(library, 'g'), () => libraryContent); } else { diff --git a/clouds/snowflake/common/build_native_app_setup_script.js b/clouds/snowflake/common/build_native_app_setup_script.js index 1c8bdda94..9cbe14716 100755 --- a/clouds/snowflake/common/build_native_app_setup_script.js +++ b/clouds/snowflake/common/build_native_app_setup_script.js @@ -28,8 +28,7 @@ function apply_replacements (text) { const libraryPath = path.join(libsBuildDir, libraryName); if (fs.existsSync(libraryPath)) { const libraryContent = fs.readFileSync(libraryPath).toString(); - // A replacer function, so $&, $` and $' inside a bundle are inserted - // literally instead of being expanded as replacement patterns. + // A replacer function, so $&, $` and $' in a bundle are inserted literally text = text.replace(new RegExp(library, 'g'), () => libraryContent); } else { From 2a119db4599c9f584a757fde9d7cd6ca0d7d17f1 Mon Sep 17 00:00:00 2001 From: vdelacruzb Date: Mon, 7 Sep 2026 17:24:15 +0200 Subject: [PATCH 4/6] fix(sf,pg,ora,bq): insert every replacement literally, and guard it Addresses the review on #650. The live instance the first pass missed: SETUP_SCRIPT.sql line 38 replaces @@SF_SCHEMA@@ with the caller-supplied DESTINATION_SCHEMA, in the INSTALL procedure that runs in the consumer's account, so a $ directive there reaches SQL that snowflake.execute runs. The rest of the class, now consistent across clouds: the env-var replacements in snowflake, postgres, oracle and bigquery build_modules, build_share, and both run-scripts, plus bigquery's library inlining. Dropped the dead library loop from build_native_app_setup_script.js -- no .sql input in either repo carries a placeholder -- and replaced it with a guard, so adding one fails loudly instead of shipping it unresolved. Its --libs_build_dir flag went with it. New guards: a bundle containing $$ is rejected, because going literal means it would now close an AS $$ ... $$ body; and every inlining checks the inserted bytes, which catches a future revert to a string replacement. Bounded the snowflake and postgres placeholder patterns to match oracle's: the greedy .*@@ over-matches if two placeholders ever share a line. Co-Authored-By: Claude Opus 5 (1M context) --- clouds/bigquery/common/build_modules.js | 6 ++-- clouds/bigquery/common/run-script.js | 2 +- clouds/oracle/common/build_modules.js | 18 ++++++++++-- clouds/postgres/common/build_modules.js | 20 ++++++++++--- clouds/snowflake/common/build_modules.js | 29 ++++++++++++++++--- .../common/build_native_app_setup_script.js | 21 ++++---------- clouds/snowflake/common/build_share.js | 2 +- clouds/snowflake/common/run-script.js | 2 +- clouds/snowflake/modules/Makefile | 2 +- clouds/snowflake/native_app/SETUP_SCRIPT.sql | 2 +- 10 files changed, 70 insertions(+), 34 deletions(-) diff --git a/clouds/bigquery/common/build_modules.js b/clouds/bigquery/common/build_modules.js index bcc470f28..881b37d86 100755 --- a/clouds/bigquery/common/build_modules.js +++ b/clouds/bigquery/common/build_modules.js @@ -171,7 +171,7 @@ const outputSeparator = argv.production ? '\n' : internalSeparator; let content = output.map(f => f.content).join(internalSeparator); function apply_replacements (text) { - const libraries = [... new Set(content.match(new RegExp('@@BQ_LIBRARY_[^@]*?_BUCKET@@', 'g')))]; + const libraries = [... new Set(text.match(new RegExp('@@BQ_LIBRARY_[^@]*?_BUCKET@@', 'g')))]; for (let library of libraries) { let libraryName = library.replace('@@BQ_LIBRARY_', '').replace('_BUCKET@@', '').toLowerCase(); if (makelib == libraryName) { @@ -181,7 +181,7 @@ function apply_replacements (text) { const libraryPath = path.join(libsBuildDir, libraryName); if (fs.existsSync(libraryPath)) { const libraryBucketPath = libraryBucket + '_' + libraryName; - text = text.replace(new RegExp(library, 'g'), libraryBucketPath); + text = text.replace(new RegExp(library, 'g'), () => libraryBucketPath); } else { console.log(`Warning: library "${libraryName}" does not exist. Run "make build-libraries" with the same filters.`); @@ -192,7 +192,7 @@ function apply_replacements (text) { for (let replacement of replacements) { if (replacement) { const pattern = new RegExp(`@@${replacement}@@`, 'g'); - text = text.replace(pattern, process.env[replacement]); + text = text.replace(pattern, () => process.env[replacement]); } } text = text.replace(/@@SKIP_DEP@@/g, ''); diff --git a/clouds/bigquery/common/run-script.js b/clouds/bigquery/common/run-script.js index 6f5c04e23..8ca75edc0 100755 --- a/clouds/bigquery/common/run-script.js +++ b/clouds/bigquery/common/run-script.js @@ -22,7 +22,7 @@ function apply_replacements (text) { for (let replacement of replacements) { if (replacement) { const pattern = new RegExp(`@@${replacement}@@`, 'g'); - text = text.replace(pattern, process.env[replacement]); + text = text.replace(pattern, () => process.env[replacement]); } } } diff --git a/clouds/oracle/common/build_modules.js b/clouds/oracle/common/build_modules.js index f0db08f37..63e62d464 100755 --- a/clouds/oracle/common/build_modules.js +++ b/clouds/oracle/common/build_modules.js @@ -195,6 +195,19 @@ let content = output.map(f => f.content).join('\n'); // Inline @@ORA_LIBRARY_@@ → libraries/javascript/build/.js, then // apply env-var @@VAR@@ replacements. +// Guards against a future revert to a string replacement, where $& and $$ would expand +function insert_literally (text, placeholder, content) { + const pattern = new RegExp(placeholder, 'g'); + const count = (text.match(pattern) || []).length; + const expected = text.length + count * (content.length - placeholder.length); + const result = text.replace(pattern, () => content); + if (result.length !== expected) { + console.log(`ERROR: "${placeholder}" was not inserted literally`); + process.exit(1); + } + return result; +} + function apply_replacements (text) { const libraryDir = path.resolve( __dirname, '..', 'libraries', 'javascript', 'build' @@ -210,15 +223,14 @@ function apply_replacements (text) { ); process.exit(1); } - // A replacer function, so $&, $` and $' in a bundle are inserted literally const libraryContent = fs.readFileSync(file).toString(); - text = text.replace(new RegExp(library, 'g'), () => libraryContent); + text = insert_literally(text, library, libraryContent); } const replacements = process.env.REPLACEMENTS.split(' '); for (const replacement of replacements) { if (replacement) { const pattern = new RegExp(`@@${replacement}@@`, 'g'); - text = text.replace(pattern, process.env[replacement]); + text = text.replace(pattern, () => process.env[replacement]); } } return text; diff --git a/clouds/postgres/common/build_modules.js b/clouds/postgres/common/build_modules.js index 44758ccc8..25abf2eae 100755 --- a/clouds/postgres/common/build_modules.js +++ b/clouds/postgres/common/build_modules.js @@ -181,15 +181,27 @@ let content = anonymousBlockWrapping(postgisInstalledCheck) // Replace environment variables content += output.map(f => anonymousBlockWrapping(extensionFunctionWrapping(f.content, f.module))).join('\n'); +// Guards against a future revert to a string replacement, where $& and $$ would expand +function insert_literally (text, placeholder, content) { + const pattern = new RegExp(placeholder, 'g'); + const count = (text.match(pattern) || []).length; + const expected = text.length + count * (content.length - placeholder.length); + const result = text.replace(pattern, () => content); + if (result.length !== expected) { + console.log(`ERROR: "${placeholder}" was not inserted literally`); + process.exit(1); + } + return result; +} + function apply_replacements (text) { - const libraries = [... new Set(text.match(new RegExp('@@PG_LIBRARY_.*@@', 'g')))]; + const libraries = [... new Set(text.match(new RegExp('@@PG_LIBRARY_[A-Z0-9_]+@@', 'g')))]; for (let library of libraries) { const libraryName = library.replace('@@PG_LIBRARY_', '').replace('@@', '').toLowerCase() + '.js'; const libraryPath = path.join(libsBuildDir, libraryName); if (fs.existsSync(libraryPath)) { const libraryContent = fs.readFileSync(libraryPath).toString(); - // A replacer function, so $&, $` and $' in a bundle are inserted literally - text = text.replace(new RegExp(library, 'g'), () => libraryContent); + text = insert_literally(text, library, libraryContent); } else { console.log(`Warning: library "${libraryName}" does not exist. Run "make build-libraries" with the same filters.`); @@ -200,7 +212,7 @@ function apply_replacements (text) { for (let replacement of replacements) { if (replacement) { const pattern = new RegExp(`@@${replacement}@@`, 'g'); - text = text.replace(pattern, process.env[replacement]); + text = text.replace(pattern, () => process.env[replacement]); } } return text; diff --git a/clouds/snowflake/common/build_modules.js b/clouds/snowflake/common/build_modules.js index 88279d42e..e30537622 100755 --- a/clouds/snowflake/common/build_modules.js +++ b/clouds/snowflake/common/build_modules.js @@ -159,15 +159,36 @@ if (argv.production) { } let content = output.map(f => f.content).join(separator); +// Snowflake wraps each JS body in AS $$ ... $$, so a literal $$ in a bundle would close it early +function inline_library (text, placeholder, content) { + if (content.includes('$$')) { + console.log(`ERROR: library "${placeholder}" contains "$$", which would close the AS $$ ... $$ body`); + process.exit(1); + } + return insert_literally(text, placeholder, content); +} + +// Guards against a future revert to a string replacement, where $& and $$ would expand +function insert_literally (text, placeholder, content) { + const pattern = new RegExp(placeholder, 'g'); + const count = (text.match(pattern) || []).length; + const expected = text.length + count * (content.length - placeholder.length); + const result = text.replace(pattern, () => content); + if (result.length !== expected) { + console.log(`ERROR: "${placeholder}" was not inserted literally`); + process.exit(1); + } + return result; +} + function apply_replacements (text) { - const libraries = [... new Set(text.match(new RegExp('@@SF_LIBRARY_.*@@', 'g')))]; + const libraries = [... new Set(text.match(new RegExp('@@SF_LIBRARY_[A-Z0-9_]+@@', 'g')))]; for (let library of libraries) { const libraryName = library.replace('@@SF_LIBRARY_', '').replace('@@', '').toLowerCase() + '.js'; const libraryPath = path.join(libsBuildDir, libraryName); if (fs.existsSync(libraryPath)) { const libraryContent = fs.readFileSync(libraryPath).toString(); - // A replacer function, so $&, $` and $' in a bundle are inserted literally - text = text.replace(new RegExp(library, 'g'), () => libraryContent); + text = inline_library(text, library, libraryContent); } else { console.log(`Warning: library "${libraryName}" does not exist. Run "make build-libraries" with the same filters.`); @@ -178,7 +199,7 @@ function apply_replacements (text) { for (let replacement of replacements) { if (replacement) { const pattern = new RegExp(`@@${replacement}@@`, 'g'); - text = text.replace(pattern, process.env[replacement]); + text = text.replace(pattern, () => process.env[replacement]); } } return text; diff --git a/clouds/snowflake/common/build_native_app_setup_script.js b/clouds/snowflake/common/build_native_app_setup_script.js index 9cbe14716..c3a67ac77 100755 --- a/clouds/snowflake/common/build_native_app_setup_script.js +++ b/clouds/snowflake/common/build_native_app_setup_script.js @@ -9,7 +9,6 @@ const path = require('path'); const argv = require('minimist')(process.argv.slice(2)); const outputDir = argv.output || 'build'; -const libsBuildDir = argv.libs_build_dir || '../libraries/javascript/build'; const nativeAppDir = argv.native_app_dir || '../native_app'; // Replace environment variables @@ -22,25 +21,17 @@ if (argv.production) { let content = fs.readFileSync(path.resolve(nativeAppDir, 'SETUP_SCRIPT.sql')).toString(); function apply_replacements (text) { - const libraries = [... new Set(text.match(new RegExp('@@SF_LIBRARY_.*@@', 'g')))]; - for (let library of libraries) { - const libraryName = library.replace('@@SF_LIBRARY_', '').replace('@@', '').toLowerCase() + '.js'; - const libraryPath = path.join(libsBuildDir, libraryName); - if (fs.existsSync(libraryPath)) { - const libraryContent = fs.readFileSync(libraryPath).toString(); - // A replacer function, so $&, $` and $' in a bundle are inserted literally - text = text.replace(new RegExp(library, 'g'), () => libraryContent); - } - else { - console.log(`Warning: library "${libraryName}" does not exist. Run "make build-libraries" with the same filters.`); - process.exit(1); - } + // The setup script does not inline libraries: modules.sql is read from the stage at install time + const libraries = text.match(new RegExp('@@SF_LIBRARY_[A-Z0-9_]+@@', 'g')); + if (libraries) { + console.log(`ERROR: the setup script cannot inline libraries, found ${libraries.join(', ')}`); + process.exit(1); } const replacements = process.env.REPLACEMENTS.split(' '); for (let replacement of replacements) { if (replacement) { const pattern = new RegExp(`@@${replacement}@@`, 'g'); - text = text.replace(pattern, process.env[replacement]); + text = text.replace(pattern, () => process.env[replacement]); } } return text; diff --git a/clouds/snowflake/common/build_share.js b/clouds/snowflake/common/build_share.js index 8c410f811..91225cfe4 100755 --- a/clouds/snowflake/common/build_share.js +++ b/clouds/snowflake/common/build_share.js @@ -184,7 +184,7 @@ function apply_replacements (text) { for (let replacement of replacements) { if (replacement) { const pattern = new RegExp(`@@${replacement}@@`, 'g'); - text = text.replace(pattern, process.env[replacement]); + text = text.replace(pattern, () => process.env[replacement]); } } return text; diff --git a/clouds/snowflake/common/run-script.js b/clouds/snowflake/common/run-script.js index b39ec99f5..d7379dbe8 100755 --- a/clouds/snowflake/common/run-script.js +++ b/clouds/snowflake/common/run-script.js @@ -66,7 +66,7 @@ function apply_replacements (text) { for (let replacement of replacements) { if (replacement) { const pattern = new RegExp(`@@${replacement}@@`, 'g'); - text = text.replace(pattern, process.env[replacement]); + text = text.replace(pattern, () => process.env[replacement]); } } } diff --git a/clouds/snowflake/modules/Makefile b/clouds/snowflake/modules/Makefile index fc7dba780..744d7f168 100644 --- a/clouds/snowflake/modules/Makefile +++ b/clouds/snowflake/modules/Makefile @@ -93,7 +93,7 @@ build-native-app-setup-script: $(NODE_MODULES_DEV) SF_APP_SCHEMA=$(SF_UNQUALIFIED_SCHEMA) APP_ROLE=app_public \ REPLACEMENTS=$(NATIVE_APP_REPLACEMENTS) \ $(COMMON_DIR)/build_native_app_setup_script.js $(MODULES_DIRS) \ - --output=$(BUILD_DIR) --libs_build_dir=$(LIBS_BUILD_DIR) --native_app_dir=$(NATIVE_APP_DIR) \ + --output=$(BUILD_DIR) --native_app_dir=$(NATIVE_APP_DIR) \ --production=$(production) --dropfirst=1 deploy: check build diff --git a/clouds/snowflake/native_app/SETUP_SCRIPT.sql b/clouds/snowflake/native_app/SETUP_SCRIPT.sql index f42557c09..1c8cb6f0a 100644 --- a/clouds/snowflake/native_app/SETUP_SCRIPT.sql +++ b/clouds/snowflake/native_app/SETUP_SCRIPT.sql @@ -35,7 +35,7 @@ AS $$ ] for (const [variable, value] of replacements) { - modulesSql = modulesSql.replaceAll(variable, value); + modulesSql = modulesSql.replaceAll(variable, () => value); } snowflake.execute({ sqlText: modulesSql }); From 8f16492d48604049dc7c74f2d1ba40f7283bb499 Mon Sep 17 00:00:00 2001 From: vdelacruzb Date: Tue, 8 Sep 2026 14:54:27 +0200 Subject: [PATCH 5/6] fix(sf,pg,db,rs): finish the replacement class, and make the installer literal by construction Jesus was right on all three points. The two env-var sites the first pass missed: databricks/common/build_modules.js:167 and redshift/common/build_modules.js:156 carry the same string replacement as the six already fixed, so "all eight sites" was eight of ten. SETUP_SCRIPT.sql line 38 is the only runtime change in this PR and nothing exercised it: it runs in Snowflake's JavaScript engine at install time, not at build time. replaceAll with a string is already in production there, which proves the method exists but not that the function overload behaves, and a failure would surface as a broken install in a consumer account. split/join is literal by construction, needs no $-directive reasoning and no assumption about engine support. Verified identical output to the replacer for $$, $& and a plain name. The bounded placeholder pattern narrowed the character class as well as the greed: @@SF_LIBRARY_H3-POLY@@ matches .*@@ but not [A-Z0-9_]+@@, and a miss there is silent -- a placeholder that never enters the libraries list gets no "does not exist" warning and ships unresolved. [^@]+ fixes the two-on-one-line case without that, and is applied to all five scanners so build_modules, list_libraries and the setup-script guard agree. Co-Authored-By: Claude Opus 5 (1M context) --- clouds/databricks/common/build_modules.js | 2 +- clouds/postgres/common/build_modules.js | 2 +- clouds/postgres/common/list_libraries.js | 2 +- clouds/redshift/common/build_modules.js | 2 +- clouds/snowflake/common/build_modules.js | 2 +- clouds/snowflake/common/build_native_app_setup_script.js | 2 +- clouds/snowflake/common/list_libraries.js | 2 +- clouds/snowflake/native_app/SETUP_SCRIPT.sql | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clouds/databricks/common/build_modules.js b/clouds/databricks/common/build_modules.js index ffecbf972..79a024e42 100755 --- a/clouds/databricks/common/build_modules.js +++ b/clouds/databricks/common/build_modules.js @@ -164,7 +164,7 @@ function apply_replacements (text) { for (let replacement of replacements) { if (replacement) { const pattern = new RegExp(`@@${replacement}@@`, 'g'); - text = text.replace(pattern, process.env[replacement]); + text = text.replace(pattern, () => process.env[replacement]); } } text = text.replace(/\\`/g, '`'); diff --git a/clouds/postgres/common/build_modules.js b/clouds/postgres/common/build_modules.js index 25abf2eae..430149e37 100755 --- a/clouds/postgres/common/build_modules.js +++ b/clouds/postgres/common/build_modules.js @@ -195,7 +195,7 @@ function insert_literally (text, placeholder, content) { } function apply_replacements (text) { - const libraries = [... new Set(text.match(new RegExp('@@PG_LIBRARY_[A-Z0-9_]+@@', 'g')))]; + const libraries = [... new Set(text.match(new RegExp('@@PG_LIBRARY_[^@]+@@', 'g')))]; for (let library of libraries) { const libraryName = library.replace('@@PG_LIBRARY_', '').replace('@@', '').toLowerCase() + '.js'; const libraryPath = path.join(libsBuildDir, libraryName); diff --git a/clouds/postgres/common/list_libraries.js b/clouds/postgres/common/list_libraries.js index 8ab7ec806..385b836fc 100755 --- a/clouds/postgres/common/list_libraries.js +++ b/clouds/postgres/common/list_libraries.js @@ -129,7 +129,7 @@ function add (f, include) { functions.forEach(f => add(f)); const content = output.map(f => f.content).join('\n'); -const libraries = [... new Set(content.match(new RegExp('@@PG_LIBRARY_.*@@', 'g')))] +const libraries = [... new Set(content.match(new RegExp('@@PG_LIBRARY_[^@]+@@', 'g')))] .map(l => l.replace('@@PG_LIBRARY_', '').replace('@@', '').toLowerCase()); process.stdout.write(libraries.join(' ')); \ No newline at end of file diff --git a/clouds/redshift/common/build_modules.js b/clouds/redshift/common/build_modules.js index 228381d0b..9640fe7ff 100755 --- a/clouds/redshift/common/build_modules.js +++ b/clouds/redshift/common/build_modules.js @@ -153,7 +153,7 @@ function apply_replacements (text) { for (let replacement of replacements) { if (replacement) { const pattern = new RegExp(`@@${replacement}@@`, 'g'); - text = text.replace(pattern, process.env[replacement]); + text = text.replace(pattern, () => process.env[replacement]); } } return text; diff --git a/clouds/snowflake/common/build_modules.js b/clouds/snowflake/common/build_modules.js index e30537622..93e1ebb6e 100755 --- a/clouds/snowflake/common/build_modules.js +++ b/clouds/snowflake/common/build_modules.js @@ -182,7 +182,7 @@ function insert_literally (text, placeholder, content) { } function apply_replacements (text) { - const libraries = [... new Set(text.match(new RegExp('@@SF_LIBRARY_[A-Z0-9_]+@@', 'g')))]; + const libraries = [... new Set(text.match(new RegExp('@@SF_LIBRARY_[^@]+@@', 'g')))]; for (let library of libraries) { const libraryName = library.replace('@@SF_LIBRARY_', '').replace('@@', '').toLowerCase() + '.js'; const libraryPath = path.join(libsBuildDir, libraryName); diff --git a/clouds/snowflake/common/build_native_app_setup_script.js b/clouds/snowflake/common/build_native_app_setup_script.js index c3a67ac77..320533456 100755 --- a/clouds/snowflake/common/build_native_app_setup_script.js +++ b/clouds/snowflake/common/build_native_app_setup_script.js @@ -22,7 +22,7 @@ let content = fs.readFileSync(path.resolve(nativeAppDir, 'SETUP_SCRIPT.sql')).to function apply_replacements (text) { // The setup script does not inline libraries: modules.sql is read from the stage at install time - const libraries = text.match(new RegExp('@@SF_LIBRARY_[A-Z0-9_]+@@', 'g')); + const libraries = text.match(new RegExp('@@SF_LIBRARY_[^@]+@@', 'g')); if (libraries) { console.log(`ERROR: the setup script cannot inline libraries, found ${libraries.join(', ')}`); process.exit(1); diff --git a/clouds/snowflake/common/list_libraries.js b/clouds/snowflake/common/list_libraries.js index 6bd4e6ff6..6324d7925 100755 --- a/clouds/snowflake/common/list_libraries.js +++ b/clouds/snowflake/common/list_libraries.js @@ -130,7 +130,7 @@ function add (f, include) { functions.forEach(f => add(f)); const content = output.map(f => f.content).join('\n'); -const libraries = [... new Set(content.match(new RegExp('@@SF_LIBRARY_.*@@', 'g')))] +const libraries = [... new Set(content.match(new RegExp('@@SF_LIBRARY_[^@]+@@', 'g')))] .map(l => l.replace('@@SF_LIBRARY_', '').replace('@@', '').toLowerCase()); process.stdout.write(libraries.join(' ')); \ No newline at end of file diff --git a/clouds/snowflake/native_app/SETUP_SCRIPT.sql b/clouds/snowflake/native_app/SETUP_SCRIPT.sql index 1c8cb6f0a..c6c046f69 100644 --- a/clouds/snowflake/native_app/SETUP_SCRIPT.sql +++ b/clouds/snowflake/native_app/SETUP_SCRIPT.sql @@ -35,7 +35,7 @@ AS $$ ] for (const [variable, value] of replacements) { - modulesSql = modulesSql.replaceAll(variable, () => value); + modulesSql = modulesSql.split(variable).join(value); } snowflake.execute({ sqlText: modulesSql }); From f3943e0af1e57ea171da8c1c4958afe3317510a7 Mon Sep 17 00:00:00 2001 From: vdelacruzb Date: Tue, 8 Sep 2026 14:59:17 +0200 Subject: [PATCH 6/6] fix(ora): drop the narrowed placeholder class here too Oracle already had [A-Z0-9_]+, which is where the snowflake and postgres patterns were copied from. The same silent miss applies: a placeholder with a character outside that class is not matched, never enters the libraries list, gets no "does not exist" warning, and ships unresolved. Co-Authored-By: Claude Opus 5 (1M context) --- clouds/oracle/common/build_modules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clouds/oracle/common/build_modules.js b/clouds/oracle/common/build_modules.js index 63e62d464..0f06a79ee 100755 --- a/clouds/oracle/common/build_modules.js +++ b/clouds/oracle/common/build_modules.js @@ -212,7 +212,7 @@ function apply_replacements (text) { const libraryDir = path.resolve( __dirname, '..', 'libraries', 'javascript', 'build' ); - const libraries = [...new Set(text.match(/@@ORA_LIBRARY_[A-Z0-9_]+@@/g) || [])]; + const libraries = [...new Set(text.match(/@@ORA_LIBRARY_[^@]+@@/g) || [])]; for (const library of libraries) { const libName = library.replace('@@ORA_LIBRARY_', '').replace('@@', ''); const file = path.join(libraryDir, libName.toLowerCase() + '.js');