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

Định dạng () an toàn như thế nào đối với các truy vấn động bên trong một hàm?

Một lời cảnh báo :kiểu này với SQL động trong SECURITY DEFINER chức năng có thể được trang nhã và thuận tiện. Nhưng đừng lạm dụng nó. Không lồng nhiều cấp chức năng theo cách này:

  • Kiểu này dễ bị lỗi hơn nhiều so với SQL thuần túy.
  • Chuyển đổi ngữ cảnh với SECURITY DEFINER có một thẻ giá.
  • SQL động với EXECUTE không thể lưu và sử dụng lại các kế hoạch truy vấn.
  • Không có "hàm nội tuyến".
  • Và tôi không muốn sử dụng nó cho các truy vấn lớn trên các bảng lớn. Sự tinh vi được thêm vào có thể là một rào cản về hiệu suất. Như:tính năng song song bị tắt đối với các kế hoạch truy vấn theo cách này.

Điều đó nói rằng, chức năng của bạn có vẻ tốt, tôi không thấy cách nào để chèn SQL. định dạng () được chứng minh là tốt để nối và trích dẫn các giá trị và số nhận dạng cho SQL động. Ngược lại, bạn có thể loại bỏ một số phần thừa để làm cho nó rẻ hơn.

Tham số hàm offset__ilimit__iinteger . Việc chèn SQL là không thể thông qua các số nguyên, thực sự không cần phải trích dẫn chúng (mặc dù SQL cho phép các hằng số chuỗi được trích dẫn cho LIMITOFFSET ). Vì vậy, chỉ:

format(' OFFSET %s LIMIT %s', offset__i, limit__i)

Ngoài ra, sau khi xác minh rằng mỗi key__v nằm trong số các tên cột hợp pháp của bạn - và mặc dù đó là tất cả các tên cột hợp pháp, không được trích dẫn - không cần phải chạy nó qua %I . Chỉ có thể là %s

Tôi muốn sử dụng text thay vì varchar . Không phải là vấn đề lớn, nhưng text là loại chuỗi "ưa thích".

Có liên quan:

COST 1 có vẻ quá thấp. Hướng dẫn sử dụng:

Trừ khi bạn biết rõ hơn, hãy để lại COST100 mặc định .

Hoạt động dựa trên một tập hợp duy nhất thay vì tất cả các vòng lặp

Toàn bộ vòng lặp có thể được thay thế bằng một SELECT duy nhất bản tường trình. Sẽ nhanh hơn đáng kể. Các nhiệm vụ tương đối đắt trong PL / pgSQL. Như thế này:

CREATE OR REPLACE FUNCTION goods__list_json (_options json, _limit int = NULL, _offset int = NULL, OUT _result jsonb)
    RETURNS jsonb
    LANGUAGE plpgsql SECURITY DEFINER AS
$func$
DECLARE
   _tbl  CONSTANT text   := 'public.goods_full';
   _cols CONSTANT text[] := '{id, id__category, category, name, barcode, price, stock, sale, purchase}';   
   _oper CONSTANT text[] := '{<, >, <=, >=, =, <>, LIKE, "NOT LIKE", ILIKE, "NOT ILIKE", BETWEEN, "NOT BETWEEN"}';
   _sql           text;
BEGIN
   SELECT concat('SELECT jsonb_agg(t) FROM ('
           , 'SELECT ' || string_agg(t.col, ', '  ORDER BY ord) FILTER (WHERE t.arr->>0 = 'true')
                                               -- ORDER BY to preserve order of objects in input
           , ' FROM '  || _tbl
           , ' WHERE ' || string_agg (
                             CASE WHEN (t.arr->>1)::int BETWEEN  1 AND 10 THEN
                                format('%s %s %L'       , t.col, _oper[(arr->>1)::int], t.arr->>2)
                                  WHEN (t.arr->>1)::int BETWEEN 11 AND 12 THEN
                                format('%s %s %L AND %L', t.col, _oper[(arr->>1)::int], t.arr->>2, t.arr->>3)
                               -- ELSE NULL  -- = default - or raise exception for illegal operator index?
                             END
                           , ' AND '  ORDER BY ord) -- ORDER BY only cosmetic
           , ' OFFSET ' || _offset  -- SQLi-safe, no quotes required
           , ' LIMIT '  || _limit   -- SQLi-safe, no quotes required
           , ') t'
          )
   FROM   json_each(_options) WITH ORDINALITY t(col, arr, ord)
   WHERE  t.col = ANY(_cols)        -- only allowed column names - or raise exception for illegal column?
   INTO   _sql;

   IF _sql IS NULL THEN
      RAISE EXCEPTION 'Invalid input resulted in empty SQL string! Input: %', _options;
   END IF;
   
   RAISE NOTICE 'SQL: %', _sql;
   EXECUTE _sql INTO _result;
END
$func$;

db <> fiddle tại đây

Ngắn hơn, nhanh hơn và vẫn an toàn trước SQLi.

Dấu ngoặc kép chỉ được thêm vào khi cần thiết cho cú pháp hoặc để bảo vệ khỏi việc đưa vào SQL. Ghi xuống chỉ để lọc các giá trị. Tên cột và toán tử được xác minh dựa trên danh sách cố định các tùy chọn được phép.

Đầu vào là json thay vì jsonb . Thứ tự của các đối tượng được giữ nguyên trong json , vì vậy bạn có thể xác định trình tự của các cột trong SELECT danh sách (có ý nghĩa) và WHERE điều kiện (hoàn toàn là mỹ phẩm). Chức năng quan sát cả hai ngay bây giờ.

Đầu ra _result vẫn là jsonb . Sử dụng OUT tham số thay vì biến. Đó là hoàn toàn tùy chọn, chỉ để thuận tiện. (Không có RETURN rõ ràng yêu cầu tuyên bố.)

Lưu ý việc sử dụng chiến lược concat() để im lặng bỏ qua NULL và toán tử nối || để NULL làm cho chuỗi nối là NULL. Bằng cách này, FROM , WHERE , LIMITOFFSET chỉ được chèn khi cần thiết. A SELECT câu lệnh hoạt động mà không có một trong hai điều đó. SELECT trống danh sách (cũng hợp pháp, nhưng tôi cho là không mong muốn) dẫn đến lỗi cú pháp. Tất cả đều có mục đích.
Sử dụng format() chỉ dành cho WHERE bộ lọc, để thuận tiện và báo giá trị. Xem:

Hàm không phải là STRICT nữa không. _limit_offset có giá trị mặc định NULL , vì vậy chỉ tham số đầu tiên _options bắt buộc. _limit_offset có thể là NULL hoặc bị bỏ qua, sau đó mỗi thứ sẽ bị loại bỏ khỏi câu lệnh.

Sử dụng text thay vì varchar .

Thực sự tạo các biến không đổi CONSTANT (chủ yếu để làm tài liệu).

Ngoài ra, chức năng thực hiện những gì bản gốc của bạn làm.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Nhiều tham gia vào cùng một bảng

  2. Postgres 9.5 BẬT MÍ DO CHỌN

  3. làm mới chế độ xem cụ thể hóa theo định kỳ postgres

  4. Cách tự động hóa PostgreSQL 12 sao chép và chuyển đổi dự phòng với repmgr - Phần 2

  5. pg_dump về lỗi ném cơ sở dữ liệu 'ra khỏi bộ nhớ dùng chung'