Bagaimana tepatnya tipe satu-byte "char" bekerja di PostgreSQL?


9

Saya sering melihat orang membicarakannya "char". Saya tidak pernah menggunakannya. Itu didefinisikan dalam dokumen sebagai,

Jenis "char" (perhatikan tanda kutip) berbeda dari char (1) karena hanya menggunakan satu byte penyimpanan. Ini digunakan secara internal dalam katalog sistem sebagai jenis enumerasi sederhana.

Dan selanjutnya,

"char"  1 byte  single-byte internal type

Jadi, jika satu byte, apa domainnya dan bagaimana Anda menggunakannya? Apakah ditandatangani atau tidak ditandatangani? Dalam posting ini oleh @Erwin Brandstetter dia menjabarkannya , tapi saya masih bingung. Dia menggunakan ascii()dan chr(), dan menyediakan ini

SELECT i
     , chr(i)::"char"        AS i_encoded
     , ascii(chr(i)::"char") AS i_decoded
FROM   generate_series(1,256) i;

Itu melakukan sesuatu yang sangat aneh antara 10 dan 11.

  i  | i_encoded | i_decoded 
-----+-----------+-----------
...
   8 | \x08      |         8
   9 |           |         9
  10 |          +|        10
     |           |           -- WTF is going on here.
  11 | \x0B      |        11
  12 | \x0C      |        12
...

Ini juga menjadi sangat aneh di sini:

 126 | ~         |       126
 127 | \x7F      |       127
 128 |           |       128
 129 |           |       128
 130 |           |       128
 131 |           |       128

Mengapa segala sesuatu di utara dari 128 diterjemahkan sebagai 128? Tetapi untuk mengambil bizzare sedikit, setelah 192 ada saklar dan mereka diterjemahkan sebagai 192 ..

 190 |           |       128
 191 |           |       128
 192 |           |       192
 193 |           |       192
 194 |           |       192
 195 |           |       192
 196 |           |       192
 197 |           |       192

Erwin berkata

Ada beberapa karakter yang tidak dimaksudkan untuk tampilan. Jadi, enkode sebelum Anda menyimpan dan decode sebelum Anda menampilkan ...

Saya tidak yakin mengapa kita harus menyandikan sama sekali jika kita melakukan persis apa yang ditanyakan

CREATE TABLE foo AS
SELECT i::"char"
FROM   generate_series(-128,127) i;

Itu bekerja dengan baik. Kita bisa mendapatkan int kembali menggunakan

SELECT i::int FROM foo;

Singkatnya,

  1. Apa yang dilakukan kode Erwin antara 10-11 di mana i null?
  2. Mengapa 128 diulang berkali-kali?
  3. Mengapa 192 diulang berkali-kali?
  4. Bagaimana saya memicu ketidakmampuan untuk menyimpan 0, ketika Erwin mengatakan Anda tidak dapat menyandikan 0 dengan cara ini (karakter nol tidak diizinkan)

    CREATE TABLE foo AS SELECT 0::int::"char" AS x;
    SELECT x::int FROM foo;
     x 
    ---
    0
    

Jawaban:


11

1. chr(10)

... menghasilkan karakter LINEFEED (alias escape sequence \n) dan psql menampilkan karakter dengan baris baru (ditunjukkan oleh +). Semuanya benar di sana.

2. & 3. ascii()menghasilkan 128 atau 192?

Dimulai dengan kesalahan yang saya buat. Saya secara sembarangan berasumsi "char"akan mencakup kisaran integer 1-byte yang tidak ditandatangani (0 hingga 255) dalam jawaban yang dirujuk (sekarang diperbaiki), tetapi sebenarnya kisaran integer 1-byte yang ditandatangani (-128 hingga 127) secara internal.

ascii()mengambil textparameter, pemeran implisit dari "char"untuk textmenghasilkan karakter multibyte-encoded di unicode, dan fungsi kembali ( per dokumentasi aktifascii() ):

Kode ASCII karakter pertama dari argumen. Untuk UTF8 mengembalikan titik kode Unicode karakter. Untuk pengkodean multibyte lainnya, argumen harus berupa karakter ASCII.

Jadi kami mendapatkan banyak nilai terpotong. 128 dan 192 adalah nilai byte untuk byte terkemuka dari karakter multibyte.

4. Bita nol

Ketidakmampuan untuk menyimpan nol byte hanya mempengaruhi jenis karakter biasa ( text, char, varchar), tidak "char". Ini berlaku untuk contoh kereta saya, karena saya berperan textsebagai batu loncatan. Saat casting antara "char"dan integerlangsung, batasan tidak berlaku. Manual tentang chr():

Karakter NULL (0) tidak diperbolehkan karena tipe data teks tidak dapat menyimpan byte tersebut.

Tidak demikian untuk "char", di mana 0dipetakan ke string kosong '':

SELECT ''::"char"::int  -- 0
     , 0::"char" = '';  -- t

Ingat: "char"masih merupakan tipe "internal" yang dimaksudkan untuk penghitungan yang sederhana dan murah. Tidak dirancang secara resmi untuk apa yang kami lakukan di sini, dan tidak portabel untuk RDBMS lainnya. Tidak ada jaminan oleh proyek Postgres untuk ini.


Saya masih berpikir hasil tampilan di psqladalah bug atau sesuatu yang aneh. Itu selesai garis, dan kemudian melewatkan garis?
Evan Carroll

4
@ Evan tidak, itu tidak 'melewatkan satu baris', baris kosong adalah kelanjutan dari baris sebelumnya (yang multiline). Jika Anda bisa mendapatkan psql untuk menggambar garis horizontal antara baris output ini akan lebih jelas, tetapi karena Anda tidak bisa petunjuk visual adalah '+'.
Jack bilang coba topanswers.xyz

0

Untuk melakukan pengalihan ke rentang yang ditandatangani, Anda dapat membuat beberapa fungsi untuk membantu bantuan. Daftar ini akan membuat fungsi - fungsi yang tidak digunakan untuk membantu dalam proses perpindahan dari kisaran int satu-byte[0-255] yang tidak ditandatangani ke kisaran satu byte yang ditandatangani yang diperlukan oleh karakter[-128,127] .

Contoh

Ekstrak dari README

Sekarang Anda dapat melakukan misalnya menyimpan nilai dalam kisaran [0-255]di atas tabel.

CREATE TABLE t(x) AS VALUES
  (to_uchar(255)),
  (to_uchar(0));

Konversikan ke bit(8)

SELECT to_bit8(x) FROM t;
 to_bit8  
----------
 11111111
 00000000
(2 rows)

Mungkin Anda ingin menghapus urutan dua bit yang lebih rendah, Anda dapat melakukannya dengan BITWISE-AND,

UPDATE t
  SET x = to_uchar( to_bit8(x) & (x'fc')::bit(8) );

SELECT to_bit8(x) FROM t;
 to_bit8  
----------
 11111100
 00000000
(2 rows)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.