Thủ thuật với PREPARE
không hoạt động, vì nó không sử dụng * chuỗi văn bản * (một giá trị) như CREATE FUNCTION
không, nhưng một tuyên bố hợp lệ (mã).
Để chuyển đổi dữ liệu thành mã có thể thực thi bạn cần sử dụng SQL động, tức là EXECUTE
trong một hàm plpgsql hoặc DO
tuyên bố. Điều này hoạt động mà không có vấn đề gì miễn là kiểu trả về không phụ thuộc vào kết quả của hàm đầu tiên myresult()
. Nếu không, bạn sẽ quay lại bắt 22 như đã nêu trong câu trả lời trước của tôi:
- Cách thực thi một kết quả chuỗi của một thủ tục được lưu trữ trong postgres
Phần quan trọng là khai báo kiểu trả về (loại hàng trong trường hợp này) bằng cách nào đó. Bạn có thể tạo TABLE
, TEMP TABLE
hoặc TYPE
cho mục đích. Hoặc bạn có thể sử dụng một câu lệnh đã chuẩn bị sẵn hoặc một con trỏ chuột.
Giải pháp với tuyên bố đã chuẩn bị sẵn
Bạn đã ở rất gần. Phần còn thiếu của câu đố là chuẩn bị truy vấn được tạo bằng SQL động .
Hàm chuẩn bị động câu lệnh
Tạo chức năng này một lần . Đây là phiên bản được tối ưu hóa và an toàn của hàm myresult()
của bạn :
CREATE OR REPLACE FUNCTION f_prep_query (_tbl regclass, _prefix text)
RETURNS void AS
$func$
BEGIN
IF EXISTS (SELECT 1 FROM pg_prepared_statements WHERE name = 'stmt_dyn') THEN
DEALLOCATE stmt_dyn;
END IF; -- you my or may not need this safety check
EXECUTE (
SELECT 'PREPARE stmt_dyn AS SELECT '
|| string_agg(quote_ident(attname), ',' ORDER BY attname)
|| ' FROM ' || _tbl
FROM pg_catalog.pg_attribute
WHERE attrelid = _tbl
AND attname LIKE _prefix || '%'
AND attnum > 0
AND NOT attisdropped
);
END
$func$ LANGUAGE plpgsql;
Tôi sử dụng regclass
cho tham số tên bảng _tbl
để làm cho nó rõ ràng và an toàn trước SQLi. Chi tiết:
- Tên bảng dưới dạng tham số hàm PostgreSQL
Lược đồ thông tin không bao gồm cột oid của danh mục hệ thống, vì vậy tôi đã chuyển sang pg_catalog.pg_attribute
thay vì information_schema.columns
. Nó cũng nhanh hơn. Có những ưu và nhược điểm cho điều này:
- Cách kiểm tra xem một bảng có tồn tại trong một lược đồ nhất định không
Nếu một câu lệnh chuẩn bị có tên stmt_dyn
đã tồn tại, PREPARE
sẽ nêu ra một ngoại lệ. Nếu điều đó có thể chấp nhận được, hãy xóa kiểm tra trên chế độ xem hệ thống pg_prepared_statements
và DEALLOCATE
sau .
Các thuật toán phức tạp hơn có thể quản lý nhiều câu lệnh đã chuẩn bị cho mỗi phiên hoặc lấy tên của câu lệnh đã chuẩn bị làm tham số bổ sung hoặc thậm chí sử dụng hàm băm MD5 của chuỗi truy vấn làm tên, nhưng điều đó vượt quá phạm vi của câu hỏi này.
Lưu ý rằng PREPARE
hoạt động ngoài phạm vi giao dịch , một lần PREPARE
thành công, câu lệnh đã chuẩn bị tồn tại trong suốt thời gian của phiên. Nếu giao dịch gói bị hủy bỏ, hãy PREPARE
không bị ảnh hưởng. ROLLBACK
không thể loại bỏ các báo cáo đã chuẩn bị.
Thực thi truy vấn động
Hai truy vấn, nhưng chỉ một gọi đến máy chủ. Và cũng rất hiệu quả.
SELECT f_prep_query('tbl'::regclass, 'pre'::text);
EXECUTE stmt_dyn;
Đơn giản hơn và hiệu quả hơn nhiều cho hầu hết các trường hợp sử dụng đơn giản hơn là tạo bảng tạm thời hoặc con trỏ và chọn / tìm nạp từ đó (sẽ là các tùy chọn khác).
SQL Fiddle.