Skip to content

Commit ba5bac2

Browse files
committed
refactor: unify reserved words and move Deparser.needsQuotes to QuoteUtils
- Remove Deparser.RESERVED_WORDS and Deparser.needsQuotes from deparser.ts - Add QuoteUtils.needsQuotesForString() and QuoteUtils.quoteString() methods - Update all call sites to use QuoteUtils instead of Deparser methods - Both QuoteUtils.needsQuotes and QuoteUtils.needsQuotesForString now use RESERVED_KEYWORDS from kwlist.ts as the single source of truth - Add Set<string> type annotations to kwlist.ts exports for TypeScript compatibility Co-Authored-By: Dan Lynch <[email protected]>
1 parent fb6d159 commit ba5bac2

File tree

2 files changed

+35
-36
lines changed

2 files changed

+35
-36
lines changed

packages/deparser/src/deparser.ts

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2457,36 +2457,8 @@ export class Deparser implements DeparserVisitor {
24572457
return output.join(' ');
24582458
}
24592459

2460-
private static readonly RESERVED_WORDS = new Set([
2461-
'all', 'analyse', 'analyze', 'and', 'any', 'array', 'as', 'asc', 'asymmetric', 'both',
2462-
'case', 'cast', 'check', 'collate', 'column', 'constraint', 'create', 'current_catalog',
2463-
'current_date', 'current_role', 'current_time', 'current_timestamp', 'current_user',
2464-
'default', 'deferrable', 'desc', 'distinct', 'do', 'else', 'end', 'except', 'false',
2465-
'fetch', 'for', 'foreign', 'from', 'grant', 'group', 'having', 'in', 'initially',
2466-
'intersect', 'into', 'lateral', 'leading', 'limit', 'localtime', 'localtimestamp',
2467-
'not', 'null', 'offset', 'on', 'only', 'or', 'order', 'placing', 'primary',
2468-
'references', 'returning', 'select', 'session_user', 'some', 'symmetric', 'table',
2469-
'then', 'to', 'trailing', 'true', 'union', 'unique', 'user', 'using', 'variadic',
2470-
'when', 'where', 'window', 'with'
2471-
]);
2472-
2473-
private static needsQuotes(value: string): boolean {
2474-
if (!value) return false;
2475-
2476-
const needsQuotesRegex = /[a-z]+[\W\w]*[A-Z]+|[A-Z]+[\W\w]*[a-z]+|\W/;
2477-
2478-
const isAllUppercase = /^[A-Z]+$/.test(value);
2479-
2480-
return needsQuotesRegex.test(value) ||
2481-
Deparser.RESERVED_WORDS.has(value.toLowerCase()) ||
2482-
isAllUppercase;
2483-
}
2484-
24852460
quoteIfNeeded(value: string): string {
2486-
if (Deparser.needsQuotes(value)) {
2487-
return `"${value}"`;
2488-
}
2489-
return value;
2461+
return QuoteUtils.quoteString(value);
24902462
}
24912463

24922464
preserveOperatorDefElemCase(defName: string): string {
@@ -2528,7 +2500,7 @@ export class Deparser implements DeparserVisitor {
25282500
}
25292501
}
25302502

2531-
return Deparser.needsQuotes(value) ? `"${value}"` : value;
2503+
return QuoteUtils.quoteString(value);
25322504
}
25332505

25342506
Integer(node: t.Integer, context: DeparserContext): string {
@@ -5715,7 +5687,7 @@ export class Deparser implements DeparserVisitor {
57155687
}
57165688

57175689
if (node.role) {
5718-
const roleName = Deparser.needsQuotes(node.role) ? `"${node.role}"` : node.role;
5690+
const roleName = QuoteUtils.quoteString(node.role);
57195691
output.push(roleName);
57205692
}
57215693

@@ -5788,7 +5760,7 @@ export class Deparser implements DeparserVisitor {
57885760
? `'${argValue}'`
57895761
: argValue;
57905762

5791-
const quotedDefname = node.defname.includes(' ') || node.defname.includes('-') || Deparser.needsQuotes(node.defname)
5763+
const quotedDefname = node.defname.includes(' ') || node.defname.includes('-') || QuoteUtils.needsQuotesForString(node.defname)
57925764
? `"${node.defname}"`
57935765
: node.defname;
57945766

@@ -5968,7 +5940,7 @@ export class Deparser implements DeparserVisitor {
59685940
if (this.getNodeType(item) === 'String') {
59695941
// Check if this identifier needs quotes to preserve case
59705942
const value = itemData.sval;
5971-
if (Deparser.needsQuotes(value)) {
5943+
if (QuoteUtils.needsQuotesForString(value)) {
59725944
return `"${value}"`;
59735945
}
59745946
return value;
@@ -6245,7 +6217,7 @@ export class Deparser implements DeparserVisitor {
62456217
}
62466218

62476219
// Handle CREATE AGGREGATE quoted identifiers - preserve quotes when needed
6248-
if (Deparser.needsQuotes(node.defname)) {
6220+
if (QuoteUtils.needsQuotesForString(node.defname)) {
62496221
const quotedDefname = `"${node.defname}"`;
62506222
if (node.arg) {
62516223
if (this.getNodeType(node.arg) === 'String') {
@@ -9848,7 +9820,7 @@ export class Deparser implements DeparserVisitor {
98489820

98499821
if (defName && defValue) {
98509822
let preservedDefName;
9851-
if (Deparser.needsQuotes(defName)) {
9823+
if (QuoteUtils.needsQuotesForString(defName)) {
98529824
preservedDefName = `"${defName}"`;
98539825
} else {
98549826
preservedDefName = this.preserveOperatorDefElemCase(defName);
@@ -10012,7 +9984,7 @@ export class Deparser implements DeparserVisitor {
100129984

100139985
if (defName && defValue) {
100149986
let preservedDefName;
10015-
if (Deparser.needsQuotes(defName)) {
9987+
if (QuoteUtils.needsQuotesForString(defName)) {
100169988
preservedDefName = `"${defName}"`;
100179989
} else {
100189990
preservedDefName = defName;

packages/deparser/src/utils/quote-utils.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
import { RESERVED_KEYWORDS, TYPE_FUNC_NAME_KEYWORDS } from '../kwlist';
22

33
export class QuoteUtils {
4+
/**
5+
* Checks if a value needs quoting for use in String nodes, DefElem, role names, etc.
6+
* Uses a different algorithm than needsQuotes - checks for mixed case and special characters.
7+
* This was previously Deparser.needsQuotes.
8+
*/
9+
static needsQuotesForString(value: string): boolean {
10+
if (!value) return false;
11+
12+
const needsQuotesRegex = /[a-z]+[\W\w]*[A-Z]+|[A-Z]+[\W\w]*[a-z]+|\W/;
13+
const isAllUppercase = /^[A-Z]+$/.test(value);
14+
15+
return needsQuotesRegex.test(value) ||
16+
RESERVED_KEYWORDS.has(value.toLowerCase()) ||
17+
isAllUppercase;
18+
}
19+
20+
/**
21+
* Quotes a string value if it needs quoting for String nodes.
22+
* Uses needsQuotesForString logic.
23+
*/
24+
static quoteString(value: string): string {
25+
if (QuoteUtils.needsQuotesForString(value)) {
26+
return `"${value}"`;
27+
}
28+
return value;
29+
}
30+
431
static needsQuotes(value: string): boolean {
532
if (!value || typeof value !== 'string') {
633
return false;

0 commit comments

Comments
 (0)