Anda dapat membungkusnya dalam subkueri tetapi itu tidak dijamin aman tanpa OFFSET 0
meretas Di 9.3, gunakan LATERAL
. Masalahnya disebabkan oleh parser yang secara efektif memperluas makro *
ke dalam daftar kolom.
Solusi
Dimana:
SELECT (my_func(x)).* FROM some_table;
akan mengevaluasi my_func
n
kali untuk n
kolom hasil dari fungsi, formulasi ini:
SELECT (mf).* FROM (
SELECT my_func(x) AS mf FROM some_table
) sub;
umumnya tidak akan, dan cenderung tidak menambahkan scan tambahan saat runtime. Untuk menjamin bahwa beberapa evaluasi tidak akan dilakukan, Anda dapat menggunakan OFFSET 0
meretas atau menyalahgunakan kegagalan PostgreSQL untuk mengoptimalkan lintas batas CTE:
SELECT (mf).* FROM (
SELECT my_func(x) AS mf FROM some_table OFFSET 0
) sub;
atau:
WITH tmp(mf) AS (
SELECT my_func(x) FROM some_table
)
SELECT (mf).* FROM tmp;
Di PostgreSQL 9.3 Anda dapat menggunakan LATERAL
untuk mendapatkan perilaku yang lebih waras:
SELECT mf.*
FROM some_table
LEFT JOIN LATERAL my_func(some_table.x) AS mf ON true;
LEFT JOIN LATERAL ... ON true
mempertahankan semua baris seperti kueri asli, bahkan jika panggilan fungsi tidak mengembalikan baris.
Demo
Buat fungsi yang tidak sebaris sebagai demonstrasi:
CREATE OR REPLACE FUNCTION my_func(integer)
RETURNS TABLE(a integer, b integer, c integer) AS $$
BEGIN
RAISE NOTICE 'my_func(%)',$1;
RETURN QUERY SELECT $1, $1, $1;
END;
$$ LANGUAGE plpgsql;
dan tabel data dummy:
CREATE TABLE some_table AS SELECT x FROM generate_series(1,10) x;
kemudian coba versi di atas. Anda akan melihat bahwa yang pertama memunculkan tiga pemberitahuan per doa; yang terakhir hanya meningkatkan satu.
Mengapa?
Pertanyaan bagus. Mengerikan.
Sepertinya:
(func(x)).*
diperluas sebagai:
(my_func(x)).i, (func(x)).j, (func(x)).k, (func(x)).l
dalam penguraian, menurut lihat debug_print_parse
, debug_print_rewritten
dan debug_print_plan
. Pohon parse (dipangkas) terlihat seperti ini:
:targetList (
{TARGETENTRY
:expr
{FIELDSELECT
:arg
{FUNCEXPR
:funcid 57168
...
}
:fieldnum 1
:resulttype 23
:resulttypmod -1
:resultcollid 0
}
:resno 1
:resname i
...
}
{TARGETENTRY
:expr
{FIELDSELECT
:arg
{FUNCEXPR
:funcid 57168
...
}
:fieldnum 2
:resulttype 20
:resulttypmod -1
:resultcollid 0
}
:resno 2
:resname j
...
}
{TARGETENTRY
:expr
{FIELDSELECT
:arg
{FUNCEXPR
:funcid 57168
...
}
:fieldnum 3
:...
}
:resno 3
:resname k
...
}
{TARGETENTRY
:expr
{FIELDSELECT
:arg
{FUNCEXPR
:funcid 57168
...
}
:fieldnum 4
...
}
:resno 4
:resname l
...
}
)
Jadi pada dasarnya, kami menggunakan peretasan parser bodoh untuk memperluas wildcard dengan mengkloning node.