Saat mendiskusikan solusi CTE rekursif untuk pertanyaan ini:
@ypercube menemukan pengecualian yang mengejutkan, yang mengarahkan kami untuk menyelidiki penanganan pengubah tipe. Kami menemukan perilaku yang mengejutkan.
1. Tipe cast mempertahankan pengubah tipe dalam beberapa konteks
Bahkan ketika diperintahkan untuk tidak melakukannya. Contoh paling dasar:
SELECT 'vc8'::varchar(8)::varchar
Orang mungkin berharap varchar
(tidak ada pengubah), setidaknya saya akan melakukannya. Tetapi hasilnya adalah varchar(8)
(dengan pengubah). Banyak kasus terkait di biola di bawah ini.
2. Rangkaian array kehilangan pengubah tipe dalam beberapa konteks
Tanpa perlu, jadi ini salah di sisi yang berlawanan:
SELECT ARRAY['vc8']::varchar(8)[]
, ARRAY['vc8']::varchar(8)[] || 'vc8'::varchar(8)
Ekspresi 1 menghasilkan varchar(8)[]
seperti yang diharapkan.
Tapi yang ke-2, setelah penggabungan yang lain varchar(8)
dipermudah hanya varchar[]
(tidak ada pengubah). Perilaku serupa dari array_append()
, contoh di biola di bawah ini.
Semua ini tidak penting di sebagian besar konteks. Postgres tidak kehilangan data, dan ketika ditugaskan ke sebuah kolom, nilainya dipaksakan ke tipe yang tepat. Namun , melakukan kesalahan dengan arah yang berlawanan memuncak dengan pengecualian yang mengejutkan:
3. CTE rekursif menuntut tipe data agar sama persis
Dengan tabel sederhana ini:
CREATE TABLE a (
vc8 varchar(8) -- with modifier
, vc varchar -- without
);
INSERT INTO a VALUES ('a', 'a'), ('bb', 'bb');
Meskipun rCTE ini berfungsi untuk varchar
kolom vc
, namun gagal untuk varchar(8)
kolom vc8
:
WITH RECURSIVE cte AS (
(
SELECT ARRAY[vc8] AS arr -- produces varchar(8)[]
FROM a
ORDER BY vc8
LIMIT 1
)
UNION ALL
(
SELECT a.vc8 || c.arr -- produces varchar[] !!
FROM cte c
JOIN a ON a.vc8 > c.arr[1]
ORDER BY vc8
LIMIT 1
)
)
TABLE cte;
GALAT: kueri rekursif "cte" kolom 1 memiliki karakter tipe bervariasi (8) [] dalam istilah non-rekursif tetapi tipe karakter beragam [] secara keseluruhan Petunjuk: Keluarkan output dari istilah non-rekursif ke jenis yang benar. Posisi: 103
Salah satu solusi cepat akan dilemparkan ke text
.
UNION
Kueri biasa tidak menunjukkan masalah yang sama: kueri untuk jenis tanpa pengubah, yang dijamin untuk menyimpan semua informasi. Tapi rCTE lebih pilih-pilih.
Selain itu, Anda tidak akan mengalami masalah dengan yang lebih umum digunakan max(vc8)
daripada ORDER BY
/ LIMIT 1
, karena max()
dan teman langsung puas text
(atau masing-masing jenis basis tanpa pengubah).
SQL Fiddle menunjukkan 3 hal:
- Berbagai contoh ekspresi termasuk hasil yang mengejutkan.
- Sebuah rCTE sederhana yang bekerja dengan
varchar
(tanpa pengubah). - RCTE yang sama meningkatkan pengecualian untuk
varchar(n)
(dengan pengubah).
Biola untuk hal 9.3. Saya mendapatkan hasil yang sama secara lokal untuk hal 9.4.4.
Saya membuat tabel dari ekspresi demo untuk dapat menampilkan tipe data yang tepat termasuk pengubah. Sementara pgAdmin menunjukkan informasi ini di luar kotak, itu tidak tersedia dari sqlfiddle. Hebatnya, ini juga tidak tersedia di psql
(!). Ini dikenal kekurangan dalam psql dan solusi yang mungkin telah dibahas pada pgsql-hacker sebelumnya - tetapi belum diimplementasikan. Ini mungkin salah satu alasan masalah ini belum terdeteksi dan diperbaiki.
Pada tingkat SQL, Anda dapat menggunakan pg_typeof()
untuk mendapatkan tipe (tetapi bukan pengubah).
Pertanyaan
Bersama-sama, 3 masalah ini membuat berantakan.
Tepatnya, masalah 1. tidak terlibat secara langsung, tetapi merusak perbaikan yang tampaknya jelas dengan para pemain dalam istilah non-rekursif: ARRAY[vc8]::varchar[]
atau serupa, yang menambah kebingungan.
Manakah dari item-item ini yang merupakan bug, kesalahan atau hanya bagaimana seharusnya?
Apakah saya melewatkan sesuatu atau haruskah kami melaporkan bug?
UNION
pertanyaan sederhana . Mungkinkah kami menemukan tiga bug kecil yang independen sekaligus? (Setelah berbulan-bulan dan berbulan-bulan tidak ada yang menemukan.) Yang mana dari Anda akan merasa harus diajukan sebagai bug?Jawaban:
Ini disebabkan oleh atribut relasi (didefinisikan dalam
pg_class
danpg_attribute
, atau didefinisikan secara dinamis dari suatuselect
pernyataan) yang mendukung pengubah (viapg_attribute.atttypmod
), sedangkan parameter fungsi tidak. Pengubah hilang ketika diproses melalui fungsi, dan karena semua operator ditangani melalui fungsi, pengubah hilang saat diproses oleh operator juga.Fungsi dengan nilai output, atau yang mengembalikan set catatan, atau yang setara
returns table(...)
juga tidak dapat mempertahankan pengubah yang termasuk dalam definisi. Namun, tabel yangreturn setof <type>
akan mempertahankan (sebenarnya, mungkin typecast ke) pengubah apa pun yang didefinisikan untuktype
dipg_attribute
.sumber