Ok, thử tưởng tượng bạn vừa code xong một function hay procedure phức tạp. Bạn thấy database của mình chạy ngon lành, nhưng đùng một cái — bùm! — dữ liệu không đúng, query chạy chậm, sếp bắt đầu sốt ruột. Đó là lúc debug xuất hiện trên sân khấu.
Debug trong PL/pgSQL dùng để:
- Tìm lỗi logic, ví dụ khi function trả về không đúng như mong đợi.
- Hiểu rõ về input không hợp lệ. Vì đôi khi user database nhập không chỉ dữ liệu mà còn... cái gì đó rất khó hiểu!
- Giải quyết vấn đề hiệu năng. Vì code viết vội có thể chạy chậm như rùa tìm Wi-Fi giữa sa mạc Sahara.
Nói nghiêm túc, debug không chỉ là tìm và sửa lỗi. Đó là cách để cải thiện code, làm cho nó nhanh hơn, hiệu quả hơn và dễ đọc hơn.
Các cách tiếp cận debug PL/pgSQL
Debug trong PL/pgSQL có thể làm theo nhiều cách khác nhau. Cùng xem từng cách nhé.
- Dùng công cụ tích hợp sẵn của PostgreSQL
PostgreSQL có vài tính năng tích hợp để chẩn đoán, bao gồm function log (RAISE NOTICE và RAISE EXCEPTION), cũng như phân tích execution plan (EXPLAIN ANALYZE). Mấy tool này giúp bạn hiểu chuyện gì đang xảy ra bên trong function của mình.
- Log bằng
RAISE NOTICE
RAISE NOTICE — là bạn thân nếu bạn muốn biết dữ liệu nào đang đi qua function, ở bước nào có vấn đề, hoặc kiểm tra giá trị biến. Khác với RAISE EXCEPTION, nó không dừng function. Ví dụ, bạn có thể in giá trị biến ở từng bước thực thi.
DO $$
DECLARE
counter INT := 0;
BEGIN
FOR counter IN 1..5 LOOP
RAISE NOTICE 'Giá trị hiện tại của counter: %', counter;
END LOOP;
END $$;
Đoạn code này sẽ in giá trị counter từ 1 đến 5. Đơn giản mà cực kỳ hữu ích cho debug!
- Dùng tool bên ngoài
Debug PL/pgSQL cũng có thể làm bằng các tool như pgAdmin (có giao diện GUI). Nó cho phép đặt breakpoint và xem giá trị biến realtime. Nếu bạn thích visual, pgAdmin sẽ là đồng minh tuyệt vời.
Các bước debug
Khi bắt đầu debug function hay procedure, quan trọng là làm theo thứ tự nhất định. Cùng xem chi tiết từng bước nhé:
- Phân tích input
Bước đầu tiên là kiểm tra input. Đảm bảo dữ liệu function nhận được không có lỗi hoặc giá trị bất ngờ. Ví dụ, bạn có thể kiểm tra tất cả tham số đầu vào bằng RAISE NOTICE:
CREATE FUNCTION check_input(x INTEGER) RETURNS VOID AS $$
BEGIN
IF x IS NULL THEN
RAISE EXCEPTION 'Giá trị đầu vào không được NULL!';
END IF;
RAISE NOTICE 'Giá trị đầu vào: %', x;
END;
$$ LANGUAGE plpgsql;
Ví dụ này minh họa cách cảnh báo user về vấn đề với input.
- Kiểm tra từng bước thực thi
Chia nhỏ function thành các block logic và thêm RAISE NOTICE ở các điểm quan trọng. Như vậy bạn sẽ biết chính xác chỗ nào có vấn đề.
CREATE FUNCTION calculate_discount(price NUMERIC, discount NUMERIC) RETURNS NUMERIC AS $$
BEGIN
RAISE NOTICE 'Bắt đầu function: giá %, giảm giá %', price, discount;
IF price <= 0 THEN
RAISE EXCEPTION 'Giá không được âm hoặc bằng 0!';
END IF;
IF discount < 0 OR discount > 100 THEN
RAISE EXCEPTION 'Giảm giá phải từ 0 đến 100!';
END IF;
RETURN price - (price * discount / 100);
END;
$$ LANGUAGE plpgsql;
Ở đây, mỗi bước debug đều in ra thông báo hữu ích về trạng thái process.
- Tối ưu và xử lý vấn đề
- Sau khi xác định được lỗi, hãy sửa nó. Nếu vấn đề liên quan đến hiệu năng, dùng các tool phân tích như
EXPLAIN ANALYZEđể tối ưu query.
Ứng dụng thực tế kỹ năng debug
Cùng xem một bài toán thực tế: bạn có function thêm bản ghi vào bảng và trả về id vừa tạo. Nghe có vẻ đơn giản, nhưng đôi khi function bị lỗi và bạn muốn biết tại sao.
Function gốc:
CREATE FUNCTION add_student(name TEXT, age INTEGER) RETURNS INTEGER AS $$
DECLARE
new_id INTEGER;
BEGIN
INSERT INTO students (name, age) VALUES (name, age) RETURNING id INTO new_id;
RETURN new_id;
END;
$$ LANGUAGE plpgsql;
Khi gọi function này với dữ liệu không hợp lệ, ví dụ age < 0, nó sẽ báo lỗi. Cùng cải thiện nó bằng debug nhé.
Function cải tiến với log:
CREATE FUNCTION add_student(name TEXT, age INTEGER) RETURNS INTEGER AS $$
DECLARE
new_id INTEGER;
BEGIN
-- Log input
RAISE NOTICE 'Thêm sinh viên: tên %, tuổi %', name, age;
-- Kiểm tra tuổi hợp lệ
IF age < 0 THEN
RAISE EXCEPTION 'Tuổi không được âm!';
END IF;
-- Thêm sinh viên và trả về ID
INSERT INTO students (name, age) VALUES (name, age) RETURNING id INTO new_id;
-- Log thành công
RAISE NOTICE 'Sinh viên đã được thêm với ID %', new_id;
RETURN new_id;
END;
$$ LANGUAGE plpgsql;
Bây giờ, nếu có lỗi, bạn sẽ biết chính xác vấn đề nhờ thông báo từ RAISE NOTICE.
Mẹo nhỏ cuối bài
- Đừng quên xóa log không cần thiết trong code production.
RAISE NOTICErất tuyệt để debug, nhưng nếu để nhiều quá sẽ làm log production bị rối. - Làm việc với code nhỏ. Nếu function quá phức tạp, hãy chia nhỏ ra. Debug sẽ dễ hơn nhiều.
- Luyện tập thường xuyên. Càng code và debug nhiều, bạn càng nhanh nhạy trong việc tìm và sửa lỗi.
Debug giống như chơi trò thám tử, chỉ khác là thay kính lúp bằng SQL query và logic PL/pgSQL. Kỹ năng này sẽ lên level theo thời gian, và mỗi bug fix xong bạn lại pro hơn một chút!
GO TO FULL VERSION