Hasil yang mengejutkan untuk tipe data dengan pengubah tipe

11

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 varcharkolom 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.

UNIONKueri 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:

  1. Berbagai contoh ekspresi termasuk hasil yang mengejutkan.
  2. Sebuah rCTE sederhana yang bekerja dengan varchar(tanpa pengubah).
  3. 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?

Erwin Brandstetter
sumber
Ini sepertinya agak mencurigakan. Saya menduga kompatibilitas ke belakang untuk kueri serikat yang ada mungkin memainkan peran.
Craig Ringer
@CraigRinger: Saya tidak melihat mengapa rangkaian array menjatuhkan pengubah tanpa perlu dan para pemain tidak, meskipun diminta. Entah mengapa rCTE harus lebih ketat (kurang pintar) daripada UNIONpertanyaan 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?
Erwin Brandstetter

Jawaban:

1

Ini disebabkan oleh atribut relasi (didefinisikan dalam pg_classdan pg_attribute, atau didefinisikan secara dinamis dari suatu selectpernyataan) yang mendukung pengubah (via pg_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 yang return setof <type>akan mempertahankan (sebenarnya, mungkin typecast ke) pengubah apa pun yang didefinisikan untuk typedi pg_attribute.

Ziggy Crueltyfree Zeitgeister
sumber