PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Chuẩn hóa Unicode trong PostgreSQL 13

Unicode tương đương

Unicode là một con thú phức tạp. Một trong những tính năng đặc biệt của nó là các chuỗi điểm mã khác nhau có thể bằng nhau. Đây không phải là trường hợp trong các bảng mã kế thừa. Ví dụ:trong LATIN1, thứ duy nhất bằng ‘a’ là ‘a’ và thứ duy nhất bằng ‘ä’ là ‘ä’. Tuy nhiên, trong Unicode, các ký tự có dấu phụ thường có thể được mã hóa (tùy thuộc vào ký tự cụ thể) theo nhiều cách khác nhau:hoặc như một ký tự được biên dịch sẵn, như đã được thực hiện trong các mã hóa kế thừa như LATIN1, hoặc được phân tách, bao gồm ký tự cơ sở 'a 'theo sau là dấu phụ ◌̈ ở đây. Đây được gọi là sự tương đương chính tắc . Lợi thế của việc có cả hai tùy chọn này là một mặt, bạn có thể dễ dàng chuyển đổi các ký tự từ các bảng mã kế thừa và mặt khác, không cần thêm mọi tổ hợp dấu vào Unicode như một ký tự riêng biệt. Nhưng sơ đồ này khiến mọi thứ trở nên khó khăn hơn đối với phần mềm sử dụng Unicode.

Miễn là bạn chỉ nhìn vào ký tự kết quả, chẳng hạn như trong trình duyệt, bạn sẽ không nhận thấy sự khác biệt và điều này không quan trọng đối với bạn. Tuy nhiên, trong một hệ thống cơ sở dữ liệu nơi tìm kiếm và sắp xếp các chuỗi là chức năng cơ bản và quan trọng về hiệu suất, mọi thứ có thể trở nên phức tạp.

Đầu tiên, thư viện đối chiếu đang sử dụng cần lưu ý điều này. Tuy nhiên, hầu hết các thư viện hệ thống C bao gồm glibc thì không. Vì vậy, trong glibc, khi bạn tìm kiếm ‘ä’, bạn sẽ không tìm thấy ‘ä’. Xem những gì tôi đã làm ở đó? Thứ hai được mã hóa khác nhau nhưng có thể trông giống nhau đối với bạn đọc. (Ít nhất đó là cách tôi đã nhập nó. Nó có thể đã được thay đổi ở đâu đó trên đường đến trình duyệt của bạn.) Khó hiểu. Nếu bạn sử dụng ICU để đối chiếu, thì điều này sẽ hoạt động và được hỗ trợ đầy đủ.

Thứ hai, khi PostgreSQL so sánh các chuỗi để bình đẳng, nó chỉ so sánh các byte, nó không tính đến khả năng cùng một chuỗi có thể được biểu diễn theo những cách khác nhau. Điều này sai về mặt kỹ thuật khi sử dụng Unicode, nhưng đó là cách tối ưu hóa hiệu suất cần thiết. Để giải quyết vấn đề đó, bạn có thể sử dụng đối chiếu không xác định , một tính năng được giới thiệu trong PostgreSQL 12. Một đối chiếu được khai báo theo cách đó sẽ không chỉ cần so sánh các byte nhưng sẽ thực hiện bất kỳ xử lý trước cần thiết nào để có thể so sánh hoặc băm các chuỗi có thể được mã hóa theo các cách khác nhau. Ví dụ:

CREATE COLLATION ndcoll (provider = icu, locale = 'und', deterministic = false);

Biểu mẫu chuẩn hóa

Vì vậy, mặc dù có nhiều cách hợp lệ khác nhau để mã hóa các ký tự Unicode nhất định, nhưng đôi khi sẽ hữu ích khi chuyển đổi tất cả chúng sang một dạng nhất quán. Đây được gọi là chuẩn hóa . Có hai hình thức chuẩn hóa : sáng tác đầy đủ , nghĩa là chúng tôi chuyển đổi tất cả các chuỗi điểm mã thành các ký tự được soạn sẵn càng nhiều càng tốt và được phân tách hoàn toàn , nghĩa là chúng tôi chuyển đổi tất cả các điểm mã thành các mảnh thành phần của chúng (chữ cái cộng với dấu) càng nhiều càng tốt. Trong thuật ngữ Unicode, các dạng này được gọi là NFC và NFD, tương ứng. Có một số chi tiết khác về vấn đề này, chẳng hạn như đặt tất cả các ký tự kết hợp vào một thứ tự chính tắc, nhưng đó là ý tưởng chung. Vấn đề là, khi bạn chuyển đổi một chuỗi Unicode thành một trong các dạng chuẩn hóa, thì bạn có thể so sánh hoặc băm chúng theo từng byte mà không phải lo lắng về các biến thể mã hóa. Bạn sử dụng cái nào không quan trọng, miễn là toàn bộ hệ thống đồng ý về một cái.

Trên thực tế, hầu hết thế giới đều sử dụng NFC. Và hơn nữa, nhiều hệ thống bị lỗi ở chỗ chúng không xử lý chính xác Unicode không phải NFC, bao gồm hầu hết các cơ sở đối chiếu của thư viện C và thậm chí cả PostgreSQL theo mặc định, như đã đề cập ở trên. Vì vậy, đảm bảo rằng tất cả Unicode được chuyển đổi sang NFC là một cách tốt để đảm bảo khả năng tương tác tốt hơn.

Chuẩn hóa trong PostgreSQL

PostgreSQL 13 hiện có hai phương tiện mới để đối phó với chuẩn hóa Unicode:một hàm để kiểm tra chuẩn hóa và một để chuyển đổi sang dạng chuẩn hóa. Ví dụ:

SELECT 'foo' IS NFC NORMALIZED;
SELECT 'foo' IS NFD NORMALIZED;
SELECT 'foo' IS NORMALIZED;  -- NFC is the default

SELECT NORMALIZE('foo', NFC);
SELECT NORMALIZE('foo', NFD);
SELECT NORMALIZE('foo');  -- NFC is the default

(Cú pháp được chỉ định trong tiêu chuẩn SQL.)

Một tùy chọn là sử dụng điều này trong một miền, ví dụ:

CREATE DOMAIN norm_text AS text CHECK (VALUE IS NORMALIZED);

Lưu ý rằng việc chuẩn hóa văn bản tùy ý không hoàn toàn rẻ. Vì vậy, hãy áp dụng điều này một cách hợp lý và chỉ khi nó thực sự quan trọng.

Cũng lưu ý rằng quá trình chuẩn hóa không được đóng dưới sự nối. Điều đó có nghĩa là việc nối hai chuỗi chuẩn hóa không phải lúc nào cũng dẫn đến một chuỗi chuẩn hóa. Vì vậy, ngay cả khi bạn cẩn thận áp dụng các chức năng này và kiểm tra xem hệ thống của bạn chỉ sử dụng các chuỗi chuẩn hóa hay không, chúng vẫn có thể "chui vào" trong các hoạt động hợp pháp. Vì vậy, chỉ cần giả sử rằng các chuỗi không chuẩn hóa không thể xảy ra sẽ bị lỗi; vấn đề này phải được xử lý đúng cách.

Các ký tự tương thích

Có một trường hợp sử dụng khác để chuẩn hóa. Unicode chứa một số dạng thay thế của các chữ cái và các ký tự khác, cho các mục đích kế thừa và tương thích khác nhau. Ví dụ:bạn có thể viết Fraktur:

SELECT '𝔰𝔬𝔪𝔢𝔫𝔞𝔪𝔢';

Bây giờ, hãy tưởng tượng ứng dụng của bạn chỉ định tên người dùng hoặc các số nhận dạng khác và có một người dùng tên là 'somename' và một cái khác có tên '𝔰𝔬𝔪𝔢𝔫𝔞𝔪𝔢' . Điều này ít nhất sẽ gây nhầm lẫn, nhưng có thể là một rủi ro bảo mật. Khai thác những điểm tương đồng như vậy thường được sử dụng trong các cuộc tấn công lừa đảo, URL giả mạo và các mối quan tâm tương tự. Vì vậy, Unicode chứa hai biểu mẫu chuẩn hóa bổ sung giải quyết những điểm tương đồng này và chuyển đổi các biểu mẫu thay thế đó thành một ký tự cơ sở chính tắc. Các hình thức này được gọi là NFKC và NFKD. Về mặt khác, chúng giống như NFC và NFD, tương ứng. Ví dụ:

=> select normalize('𝔰𝔬𝔪𝔢𝔫𝔞𝔪𝔢', nfkc);
 normalize
-----------
 somename

Một lần nữa, việc sử dụng các ràng buộc kiểm tra có thể là một phần của miền có thể hữu ích:

CREATE DOMAIN username AS text CHECK (VALUE IS NFKC NORMALIZED OR VALUE IS NFKD NORMALIZED);

(Quá trình chuẩn hóa thực tế có lẽ nên được thực hiện trong giao diện người dùng giao diện người dùng.)

Xem thêm RFC 3454 để biết cách xử lý các chuỗi nhằm giải quyết các mối quan tâm như vậy.

Tóm tắt

Các vấn đề tương đương Unicode thường bị bỏ qua mà không có hậu quả. Trong nhiều ngữ cảnh, hầu hết dữ liệu ở dạng NFC, vì vậy không có vấn đề gì phát sinh. Tuy nhiên, việc bỏ qua những vấn đề này có thể dẫn đến hành vi kỳ lạ, dữ liệu dường như bị thiếu và trong một số tình huống có nguy cơ bảo mật. Vì vậy, nhận thức về những vấn đề này là quan trọng đối với các nhà thiết kế cơ sở dữ liệu và các công cụ được mô tả trong bài viết này có thể được sử dụng để giải quyết chúng.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Làm cách nào để kết nối với PostgreSQL mà không chỉ định tên cơ sở dữ liệu?

  2. Hội nghị mùa xuân FLOSS Vương quốc Anh

  3. Cách tốt nhất để xóa hàng triệu hàng theo ID

  4. 4 cách để tìm hàng có chứa ký tự chữ hoa trong PostgreSQL

  5. postgreSQL - trong so với bất kỳ