Skip to content

PG->Fabric (T-SQL): date_part with a typed text field literal drops the field -> DATEPART(DAY) #310

Description

@NivasSA

PG->Fabric (T-SQL): date_part with a typed text field literal drops the field -> DATEPART(DAY)

8<------------------------------------------------------------------------------8<

Description

When the first argument of date_part is a typed text literal ('year'::text, as emitted
by PostgreSQL's own deparser pg_get_viewdef/pg_get_querydef), the Fabric transpiler discards
the field name and emits DATEPART(DAY, …) regardless of the requested field. A bare literal
('year') is preserved. Because tools that round-trip through the PostgreSQL deparser always
produce the typed form, every date_part(<field>, d) silently returns the day-of-month.

Root cause (pointer)

The date_part -> DATEPART mapping only recognises a bare string literal as the field
argument; a Cast(StringLiteral, TEXT) node is not unwrapped, so the field is lost and the
emitter falls back to its DAY default.

Affected constructs

date_part('year'::text, d), date_part('month'::text, d), date_part('quarter'::text, d),
date_part('week'::text, d), date_part('dow'::text, d) — all collapse to DATEPART(DAY, d).
(EXTRACT(field FROM d) is unaffected — it maps correctly.)

Repro

Cargo.toml:

[package]
name = "datepart_typed_repro"
version = "0.1.0"
edition = "2021"

[dependencies]
polyglot-sql = "0.5.15"

src/main.rs:

use polyglot_sql::{Dialect, DialectType, TranspileOptions};

fn go(sql: &str) {
    let d = Dialect::get(DialectType::PostgreSQL).unwrap();
    match d.transpile_with(sql, DialectType::Fabric, TranspileOptions::strict()) {
        Ok(out) => println!("IN : {sql}\nOUT: {}\n", out.join("; ")),
        Err(e) => println!("IN : {sql}\nERR: {e}\n"),
    }
}

fn main() {
    // typed literal (what pg_get_viewdef emits) -> field dropped, defaults to DAY
    go("SELECT date_part('year'::text, f1) FROM date_tbl");
    go("SELECT date_part('month'::text, f1) FROM date_tbl");
    go("SELECT date_part('dow'::text, f1) FROM date_tbl");
    // bare literal -> field preserved (for contrast)
    go("SELECT date_part('year', f1) FROM date_tbl");
}

Captured output (polyglot-sql 0.5.15):

IN : SELECT date_part('year'::text, f1) FROM date_tbl
OUT: SELECT DATEPART(DAY, f1) FROM date_tbl

IN : SELECT date_part('month'::text, f1) FROM date_tbl
OUT: SELECT DATEPART(DAY, f1) FROM date_tbl

IN : SELECT date_part('dow'::text, f1) FROM date_tbl
OUT: SELECT DATEPART(DAY, f1) FROM date_tbl

IN : SELECT date_part('year', f1) FROM date_tbl
OUT: SELECT DATEPART(year, f1) FROM date_tbl

Expected

REWRITE: unwrap the ::text cast on the field literal and preserve the mapped datepart —
date_part('year'::text, d) -> DATEPART(year, d) (same as the bare form). If the field is
not mappable, REJECT rather than silently defaulting to DAY.

Notes

Sibling of the other PG->Fabric date/time reports (#281 exotic dateparts, #282 date_trunc,
#292 umbrella). This one is specifically the typed-literal field is dropped -> DAY path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions