@pgsql/transform 18.15.1 → 18.15.3
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/esm/transform.js +52 -10
- package/package.json +7 -7
- package/transform.js +52 -10
package/esm/transform.js
CHANGED
|
@@ -197,7 +197,9 @@ export function transformSchemaRefsInString(str, schemaMapping, result) {
|
|
|
197
197
|
// References embedded in opaque strings carry no object identity, so only
|
|
198
198
|
// whole-schema (schema-level default) routes can be applied here.
|
|
199
199
|
for (const [oldSchema, newSchema] of schemaLevelMap(schemaMapping)) {
|
|
200
|
-
|
|
200
|
+
if (!out.includes(oldSchema))
|
|
201
|
+
continue;
|
|
202
|
+
const pattern = cachedRegExp(`(?<![\\w-])("?)${cachedEscapeRegexp(oldSchema)}\\1(?=\\.)`, 'g');
|
|
201
203
|
const before = out;
|
|
202
204
|
out = out.replace(pattern, `$1${newSchema}$1`);
|
|
203
205
|
if (out !== before) {
|
|
@@ -618,14 +620,19 @@ export function validateNoUntransformedSchemas(content, schemaMapping) {
|
|
|
618
620
|
if (moved.size === 0) {
|
|
619
621
|
return;
|
|
620
622
|
}
|
|
623
|
+
// Every pattern below requires a literal occurrence of the schema name, so a
|
|
624
|
+
// single case-insensitive substring scan rules out most schemas up front.
|
|
625
|
+
const lowerContent = content.toLowerCase();
|
|
621
626
|
for (const [oldSchema, newSchema] of moved) {
|
|
622
|
-
|
|
627
|
+
if (!lowerContent.includes(oldSchema.toLowerCase()))
|
|
628
|
+
continue;
|
|
629
|
+
const escapedSchema = cachedEscapeRegexp(oldSchema);
|
|
623
630
|
// Pattern 1: quoted or unquoted schema name followed by dot (schema-qualified)
|
|
624
|
-
const dotPattern =
|
|
631
|
+
const dotPattern = cachedRegExp(`(?:"${escapedSchema}"|\\b${escapedSchema})(?=\\.)`, 'g');
|
|
625
632
|
// Pattern 2: standalone schema name in known SQL contexts
|
|
626
633
|
// Note: We don't use trailing \b because it fails after closing quotes
|
|
627
634
|
// (both '"' and whitespace are non-word characters, so no boundary exists).
|
|
628
|
-
const standalonePattern =
|
|
635
|
+
const standalonePattern = cachedRegExp(`(?:ON\\s+SCHEMA\\s+|IN\\s+SCHEMA\\s+|CREATE\\s+SCHEMA\\s+|DROP\\s+SCHEMA\\s+(?:IF\\s+EXISTS\\s+)?|SET\\s+SCHEMA\\s+)` +
|
|
629
636
|
`(?:"${escapedSchema}"|\\b${escapedSchema}\\b)`, 'gi');
|
|
630
637
|
const dotMatches = content.match(dotPattern);
|
|
631
638
|
const standaloneMatches = content.match(standalonePattern);
|
|
@@ -633,7 +640,7 @@ export function validateNoUntransformedSchemas(content, schemaMapping) {
|
|
|
633
640
|
if (totalMatches > 0) {
|
|
634
641
|
const lines = content.split('\n');
|
|
635
642
|
const locations = [];
|
|
636
|
-
const combinedPattern =
|
|
643
|
+
const combinedPattern = cachedRegExp(`(?:"${escapedSchema}"|\\b${escapedSchema})(?=\\.)|` +
|
|
637
644
|
`(?:ON\\s+SCHEMA\\s+|IN\\s+SCHEMA\\s+|CREATE\\s+SCHEMA\\s+|DROP\\s+SCHEMA\\s+(?:IF\\s+EXISTS\\s+)?|SET\\s+SCHEMA\\s+)` +
|
|
638
645
|
`(?:"${escapedSchema}"|\\b${escapedSchema}\\b)`, 'gi');
|
|
639
646
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -878,6 +885,34 @@ function transformSchemasInContext(ctx, schemaMapping, result, visitorOptions) {
|
|
|
878
885
|
export function escapeRegexp(str) {
|
|
879
886
|
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
880
887
|
}
|
|
888
|
+
/**
|
|
889
|
+
* Compiling a pattern costs far more than running it, and the string-level
|
|
890
|
+
* passes below rebuild the same handful of per-schema patterns for every file
|
|
891
|
+
* they touch — on a large corpus that is hundreds of thousands of identical
|
|
892
|
+
* compiles. Patterns are keyed by source + flags and reused; each returned
|
|
893
|
+
* RegExp is stateful (`g`/`y` carry `lastIndex`), so callers must only use it
|
|
894
|
+
* with `String#replace`/`String#match`, which reset it, or reset it
|
|
895
|
+
* themselves, exactly as the throwaway instances required.
|
|
896
|
+
*/
|
|
897
|
+
const regExpCache = new Map();
|
|
898
|
+
function cachedRegExp(source, flags) {
|
|
899
|
+
const key = `${flags}\u0000${source}`;
|
|
900
|
+
let re = regExpCache.get(key);
|
|
901
|
+
if (!re) {
|
|
902
|
+
re = new RegExp(source, flags);
|
|
903
|
+
regExpCache.set(key, re);
|
|
904
|
+
}
|
|
905
|
+
return re;
|
|
906
|
+
}
|
|
907
|
+
const escapeRegexpCache = new Map();
|
|
908
|
+
function cachedEscapeRegexp(str) {
|
|
909
|
+
let escaped = escapeRegexpCache.get(str);
|
|
910
|
+
if (escaped === undefined) {
|
|
911
|
+
escaped = escapeRegexp(str);
|
|
912
|
+
escapeRegexpCache.set(str, escaped);
|
|
913
|
+
}
|
|
914
|
+
return escaped;
|
|
915
|
+
}
|
|
881
916
|
/**
|
|
882
917
|
* Extract pgpm header comments from the beginning of SQL content.
|
|
883
918
|
*/
|
|
@@ -936,7 +971,7 @@ export function transformComments(content, schemaMapping, result) {
|
|
|
936
971
|
if (!newName)
|
|
937
972
|
continue;
|
|
938
973
|
result.schemasFound.add(schema);
|
|
939
|
-
const pathPattern =
|
|
974
|
+
const pathPattern = cachedRegExp(`(schemas/)${cachedEscapeRegexp(schema)}(/|$)`, 'g');
|
|
940
975
|
const before = newPath;
|
|
941
976
|
newPath = newPath.replace(pathPattern, `$1${newName}$2`);
|
|
942
977
|
if (newPath !== before) {
|
|
@@ -953,15 +988,20 @@ export function transformComments(content, schemaMapping, result) {
|
|
|
953
988
|
export function transformVerifyCalls(content, schemaMapping, result) {
|
|
954
989
|
const schemas = Array.from(schemaMapping.keys()).sort((a, b) => b.length - a.length);
|
|
955
990
|
let newContent = content;
|
|
991
|
+
// The pattern needs the schema name spelled out, case-insensitively.
|
|
992
|
+
let lowerContent = content.toLowerCase();
|
|
956
993
|
for (const schema of schemas) {
|
|
957
994
|
const newName = schemaMapping.get(schema);
|
|
958
995
|
if (!newName)
|
|
959
996
|
continue;
|
|
960
|
-
|
|
961
|
-
|
|
997
|
+
if (!lowerContent.includes(schema.toLowerCase()))
|
|
998
|
+
continue;
|
|
999
|
+
const escapedSchema = cachedEscapeRegexp(schema);
|
|
1000
|
+
const verifyPattern = cachedRegExp(`(verify_(?:function|table|trigger|type|domain|view|index|constraint|schema|policy|table_grant|function_grant|sequence_grant|type_grant)\\s*\\(\\s*')${escapedSchema}(\\.|'\\s*\\))`, 'gi');
|
|
962
1001
|
const before = newContent;
|
|
963
1002
|
newContent = newContent.replace(verifyPattern, `$1${newName}$2`);
|
|
964
1003
|
if (newContent !== before) {
|
|
1004
|
+
lowerContent = newContent.toLowerCase();
|
|
965
1005
|
result.schemasFound.add(schema);
|
|
966
1006
|
result.schemasTransformed.set(schema, newName);
|
|
967
1007
|
}
|
|
@@ -978,8 +1018,10 @@ export function transformJsonStringValues(content, schemaMapping, result) {
|
|
|
978
1018
|
const newName = schemaMapping.get(schema);
|
|
979
1019
|
if (!newName)
|
|
980
1020
|
continue;
|
|
981
|
-
|
|
982
|
-
|
|
1021
|
+
if (!newContent.includes(schema))
|
|
1022
|
+
continue;
|
|
1023
|
+
const escapedSchema = cachedEscapeRegexp(schema);
|
|
1024
|
+
const jsonValuePattern = cachedRegExp(`(:")${escapedSchema}(")`, 'g');
|
|
983
1025
|
const before = newContent;
|
|
984
1026
|
newContent = newContent.replace(jsonValuePattern, `$1${newName}$2`);
|
|
985
1027
|
if (newContent !== before) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgsql/transform",
|
|
3
|
-
"version": "18.15.
|
|
3
|
+
"version": "18.15.3",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "AST-based SQL transformation, qualification, classification and closure analysis for PostgreSQL",
|
|
6
6
|
"main": "index.js",
|
|
@@ -32,13 +32,13 @@
|
|
|
32
32
|
"scan-corpus": "node scripts/scan-corpus.js"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@pgsql/quotes": "18.2.
|
|
36
|
-
"@pgsql/semantics": "18.1.
|
|
37
|
-
"@pgsql/traverse": "18.7.
|
|
38
|
-
"plpgsql-parser": "18.5.
|
|
35
|
+
"@pgsql/quotes": "18.2.1",
|
|
36
|
+
"@pgsql/semantics": "18.1.2",
|
|
37
|
+
"@pgsql/traverse": "18.7.3",
|
|
38
|
+
"plpgsql-parser": "18.5.3"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"makage": "^0.1
|
|
41
|
+
"makage": "^0.5.1"
|
|
42
42
|
},
|
|
43
43
|
"keywords": [
|
|
44
44
|
"sql",
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"classify",
|
|
51
51
|
"plpgsql"
|
|
52
52
|
],
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "69cdce673db035fb1ee3b529480d39fac01512ab"
|
|
54
54
|
}
|
package/transform.js
CHANGED
|
@@ -218,7 +218,9 @@ function transformSchemaRefsInString(str, schemaMapping, result) {
|
|
|
218
218
|
// References embedded in opaque strings carry no object identity, so only
|
|
219
219
|
// whole-schema (schema-level default) routes can be applied here.
|
|
220
220
|
for (const [oldSchema, newSchema] of schemaLevelMap(schemaMapping)) {
|
|
221
|
-
|
|
221
|
+
if (!out.includes(oldSchema))
|
|
222
|
+
continue;
|
|
223
|
+
const pattern = cachedRegExp(`(?<![\\w-])("?)${cachedEscapeRegexp(oldSchema)}\\1(?=\\.)`, 'g');
|
|
222
224
|
const before = out;
|
|
223
225
|
out = out.replace(pattern, `$1${newSchema}$1`);
|
|
224
226
|
if (out !== before) {
|
|
@@ -639,14 +641,19 @@ function validateNoUntransformedSchemas(content, schemaMapping) {
|
|
|
639
641
|
if (moved.size === 0) {
|
|
640
642
|
return;
|
|
641
643
|
}
|
|
644
|
+
// Every pattern below requires a literal occurrence of the schema name, so a
|
|
645
|
+
// single case-insensitive substring scan rules out most schemas up front.
|
|
646
|
+
const lowerContent = content.toLowerCase();
|
|
642
647
|
for (const [oldSchema, newSchema] of moved) {
|
|
643
|
-
|
|
648
|
+
if (!lowerContent.includes(oldSchema.toLowerCase()))
|
|
649
|
+
continue;
|
|
650
|
+
const escapedSchema = cachedEscapeRegexp(oldSchema);
|
|
644
651
|
// Pattern 1: quoted or unquoted schema name followed by dot (schema-qualified)
|
|
645
|
-
const dotPattern =
|
|
652
|
+
const dotPattern = cachedRegExp(`(?:"${escapedSchema}"|\\b${escapedSchema})(?=\\.)`, 'g');
|
|
646
653
|
// Pattern 2: standalone schema name in known SQL contexts
|
|
647
654
|
// Note: We don't use trailing \b because it fails after closing quotes
|
|
648
655
|
// (both '"' and whitespace are non-word characters, so no boundary exists).
|
|
649
|
-
const standalonePattern =
|
|
656
|
+
const standalonePattern = cachedRegExp(`(?:ON\\s+SCHEMA\\s+|IN\\s+SCHEMA\\s+|CREATE\\s+SCHEMA\\s+|DROP\\s+SCHEMA\\s+(?:IF\\s+EXISTS\\s+)?|SET\\s+SCHEMA\\s+)` +
|
|
650
657
|
`(?:"${escapedSchema}"|\\b${escapedSchema}\\b)`, 'gi');
|
|
651
658
|
const dotMatches = content.match(dotPattern);
|
|
652
659
|
const standaloneMatches = content.match(standalonePattern);
|
|
@@ -654,7 +661,7 @@ function validateNoUntransformedSchemas(content, schemaMapping) {
|
|
|
654
661
|
if (totalMatches > 0) {
|
|
655
662
|
const lines = content.split('\n');
|
|
656
663
|
const locations = [];
|
|
657
|
-
const combinedPattern =
|
|
664
|
+
const combinedPattern = cachedRegExp(`(?:"${escapedSchema}"|\\b${escapedSchema})(?=\\.)|` +
|
|
658
665
|
`(?:ON\\s+SCHEMA\\s+|IN\\s+SCHEMA\\s+|CREATE\\s+SCHEMA\\s+|DROP\\s+SCHEMA\\s+(?:IF\\s+EXISTS\\s+)?|SET\\s+SCHEMA\\s+)` +
|
|
659
666
|
`(?:"${escapedSchema}"|\\b${escapedSchema}\\b)`, 'gi');
|
|
660
667
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -899,6 +906,34 @@ function transformSchemasInContext(ctx, schemaMapping, result, visitorOptions) {
|
|
|
899
906
|
function escapeRegexp(str) {
|
|
900
907
|
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
901
908
|
}
|
|
909
|
+
/**
|
|
910
|
+
* Compiling a pattern costs far more than running it, and the string-level
|
|
911
|
+
* passes below rebuild the same handful of per-schema patterns for every file
|
|
912
|
+
* they touch — on a large corpus that is hundreds of thousands of identical
|
|
913
|
+
* compiles. Patterns are keyed by source + flags and reused; each returned
|
|
914
|
+
* RegExp is stateful (`g`/`y` carry `lastIndex`), so callers must only use it
|
|
915
|
+
* with `String#replace`/`String#match`, which reset it, or reset it
|
|
916
|
+
* themselves, exactly as the throwaway instances required.
|
|
917
|
+
*/
|
|
918
|
+
const regExpCache = new Map();
|
|
919
|
+
function cachedRegExp(source, flags) {
|
|
920
|
+
const key = `${flags}\u0000${source}`;
|
|
921
|
+
let re = regExpCache.get(key);
|
|
922
|
+
if (!re) {
|
|
923
|
+
re = new RegExp(source, flags);
|
|
924
|
+
regExpCache.set(key, re);
|
|
925
|
+
}
|
|
926
|
+
return re;
|
|
927
|
+
}
|
|
928
|
+
const escapeRegexpCache = new Map();
|
|
929
|
+
function cachedEscapeRegexp(str) {
|
|
930
|
+
let escaped = escapeRegexpCache.get(str);
|
|
931
|
+
if (escaped === undefined) {
|
|
932
|
+
escaped = escapeRegexp(str);
|
|
933
|
+
escapeRegexpCache.set(str, escaped);
|
|
934
|
+
}
|
|
935
|
+
return escaped;
|
|
936
|
+
}
|
|
902
937
|
/**
|
|
903
938
|
* Extract pgpm header comments from the beginning of SQL content.
|
|
904
939
|
*/
|
|
@@ -957,7 +992,7 @@ function transformComments(content, schemaMapping, result) {
|
|
|
957
992
|
if (!newName)
|
|
958
993
|
continue;
|
|
959
994
|
result.schemasFound.add(schema);
|
|
960
|
-
const pathPattern =
|
|
995
|
+
const pathPattern = cachedRegExp(`(schemas/)${cachedEscapeRegexp(schema)}(/|$)`, 'g');
|
|
961
996
|
const before = newPath;
|
|
962
997
|
newPath = newPath.replace(pathPattern, `$1${newName}$2`);
|
|
963
998
|
if (newPath !== before) {
|
|
@@ -974,15 +1009,20 @@ function transformComments(content, schemaMapping, result) {
|
|
|
974
1009
|
function transformVerifyCalls(content, schemaMapping, result) {
|
|
975
1010
|
const schemas = Array.from(schemaMapping.keys()).sort((a, b) => b.length - a.length);
|
|
976
1011
|
let newContent = content;
|
|
1012
|
+
// The pattern needs the schema name spelled out, case-insensitively.
|
|
1013
|
+
let lowerContent = content.toLowerCase();
|
|
977
1014
|
for (const schema of schemas) {
|
|
978
1015
|
const newName = schemaMapping.get(schema);
|
|
979
1016
|
if (!newName)
|
|
980
1017
|
continue;
|
|
981
|
-
|
|
982
|
-
|
|
1018
|
+
if (!lowerContent.includes(schema.toLowerCase()))
|
|
1019
|
+
continue;
|
|
1020
|
+
const escapedSchema = cachedEscapeRegexp(schema);
|
|
1021
|
+
const verifyPattern = cachedRegExp(`(verify_(?:function|table|trigger|type|domain|view|index|constraint|schema|policy|table_grant|function_grant|sequence_grant|type_grant)\\s*\\(\\s*')${escapedSchema}(\\.|'\\s*\\))`, 'gi');
|
|
983
1022
|
const before = newContent;
|
|
984
1023
|
newContent = newContent.replace(verifyPattern, `$1${newName}$2`);
|
|
985
1024
|
if (newContent !== before) {
|
|
1025
|
+
lowerContent = newContent.toLowerCase();
|
|
986
1026
|
result.schemasFound.add(schema);
|
|
987
1027
|
result.schemasTransformed.set(schema, newName);
|
|
988
1028
|
}
|
|
@@ -999,8 +1039,10 @@ function transformJsonStringValues(content, schemaMapping, result) {
|
|
|
999
1039
|
const newName = schemaMapping.get(schema);
|
|
1000
1040
|
if (!newName)
|
|
1001
1041
|
continue;
|
|
1002
|
-
|
|
1003
|
-
|
|
1042
|
+
if (!newContent.includes(schema))
|
|
1043
|
+
continue;
|
|
1044
|
+
const escapedSchema = cachedEscapeRegexp(schema);
|
|
1045
|
+
const jsonValuePattern = cachedRegExp(`(:")${escapedSchema}(")`, 'g');
|
|
1004
1046
|
const before = newContent;
|
|
1005
1047
|
newContent = newContent.replace(jsonValuePattern, `$1${newName}$2`);
|
|
1006
1048
|
if (newContent !== before) {
|