Skip to content

Commit 8726ce6

Browse files
committed
fix: strip quotes and uppercase EXTRACT field name in deparser
The EXTRACT SQL syntax handler was passing the A_Const sval value directly, which gets single-quoted by QuoteUtils.formatEString(). This produced EXTRACT('epoch' FROM x) instead of the correct EXTRACT(EPOCH FROM x). Strip surrounding quotes and uppercase the field name to match PostgreSQL's expected EXTRACT syntax. Ref: constructive-io/constructive-db#748
1 parent cdd732b commit 8726ce6

2 files changed

Lines changed: 42 additions & 1 deletion

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { expectParseDeparse } from '../../test-utils';
2+
3+
it('should deparse EXTRACT(EPOCH FROM ...) correctly', async () => {
4+
const sql = `SELECT EXTRACT(EPOCH FROM now())`;
5+
const result = await expectParseDeparse(sql);
6+
expect(result).toContain('EXTRACT(EPOCH FROM');
7+
expect(result).not.toContain("'epoch'");
8+
});
9+
10+
it('should deparse EXTRACT(YEAR FROM ...) correctly', async () => {
11+
const sql = `SELECT EXTRACT(YEAR FROM TIMESTAMP '2001-02-16 20:38:40')`;
12+
const result = await expectParseDeparse(sql);
13+
expect(result).toContain('EXTRACT(YEAR FROM');
14+
expect(result).not.toContain("'year'");
15+
});
16+
17+
it('should deparse EXTRACT(EPOCH FROM ...) with pretty option', async () => {
18+
const sql = `SELECT EXTRACT(EPOCH FROM now())`;
19+
const result = await expectParseDeparse(sql, { pretty: true });
20+
expect(result).toContain('EXTRACT(EPOCH FROM');
21+
expect(result).not.toContain("'epoch'");
22+
});
23+
24+
it('should deparse EXTRACT(CENTURY FROM ...) correctly', async () => {
25+
const sql = `SELECT EXTRACT(CENTURY FROM DATE '2001-01-01')`;
26+
const result = await expectParseDeparse(sql);
27+
expect(result).toContain('EXTRACT(CENTURY FROM');
28+
expect(result).not.toContain("'century'");
29+
});
30+
31+
it('should deparse EXTRACT(MILLENNIUM FROM ...) correctly', async () => {
32+
const sql = `SELECT EXTRACT(MILLENNIUM FROM DATE '2001-01-01')`;
33+
const result = await expectParseDeparse(sql);
34+
expect(result).toContain('EXTRACT(MILLENNIUM FROM');
35+
expect(result).not.toContain("'millennium'");
36+
});

packages/deparser/src/deparser.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1446,8 +1446,13 @@ export class Deparser implements DeparserVisitor {
14461446

14471447
// Handle EXTRACT function with SQL syntax
14481448
if (node.funcformat === 'COERCE_SQL_SYNTAX' && name === 'pg_catalog.extract' && args.length >= 2) {
1449-
const field = this.visit(args[0], context);
1449+
let field = this.visit(args[0], context);
14501450
const source = this.visit(args[1], context);
1451+
// EXTRACT requires an unquoted uppercase keyword (EPOCH, YEAR, etc.)
1452+
// but A_Const sval deparsing wraps it in single quotes — strip and uppercase
1453+
if (field.startsWith("'") && field.endsWith("'")) {
1454+
field = field.slice(1, -1).toUpperCase();
1455+
}
14511456
return `EXTRACT(${field} FROM ${source})`;
14521457
}
14531458

0 commit comments

Comments
 (0)