การเข้าถึงฐานข้อมูล PostgreSQL สำหรับผู้สร้างแอป AI
ตั้งค่าการเข้าถึงฐานข้อมูล PostgreSQL สำหรับตัวสร้างแอป AI ด้วยการสำรวจแบบอ่านอย่างเดียว ข้อมูลรับรองแบบจำกัดขอบเขต migration ที่อนุมัติ และการทำ pooling อย่างปลอดภัย

ตัวสร้างแอป AI เชื่อมต่อกับฐานข้อมูล PostgreSQL ที่มีอยู่โดยไม่เป็นเจ้าของสคีมาได้ แต่ต้องทำให้ขอบเขตนั้นเกิดขึ้นจริงใน PostgreSQL คำสั่งว่า «ห้ามเปลี่ยน production» ไม่ใช่มาตรการควบคุม บทบาทที่แยกออกมา ค่าเริ่มต้นของทรานแซกชัน การตรวจ migration อย่างชัดเจน และการตรวจสคีมา ต่างหากที่เป็นมาตรการควบคุม
โมเดลที่ปลอดภัยแบ่งงานฐานข้อมูลเป็นสามส่วน การสำรวจอ่านเมทาดาทาและตัวอย่างข้อมูลที่อนุญาต แอปอ่านและเขียนเฉพาะตารางกับการดำเนินการที่จำเป็น การเปลี่ยนสคีมาทำผ่านตัวตนสำหรับ migration ที่แยกออกมา หลังคนอนุมัติ SQL ที่แน่นอนแล้ว ผมเคยเห็นทีมรวมสามส่วนนี้ไว้ในข้อมูลรับรองเจ้าของที่สะดวกใช้ชุดเดียว แล้วพบว่าเอเจนต์ตีความชื่อคอลัมน์ที่ดูเป็นไปได้ว่าเป็นสิทธิ์ในการออกแบบตารางที่ใช้งานจริงใหม่ ความสะดวกอยู่ได้เพียงบ่ายเดียว แต่การเก็บกวาดใช้เวลานานกว่านั้นมาก
การสำรวจควรอ่านอย่างเดียวตั้งแต่การออกแบบ
การเชื่อมต่อเพื่อสำรวจต้องมีสิทธิ์พอจะเข้าใจสคีมาที่อนุญาต แต่ไม่มากพอจะปรับปรุงมัน สร้างบทบาทสำหรับล็อกอินที่สร้างฐานข้อมูลหรือบทบาทไม่ได้ ข้าม row security ไม่ได้ และไม่รับสิทธิ์ที่คาดไม่ถึงจากกลุ่มสิทธิ์กว้าง ๆ PostgreSQL สร้างบทบาทใหม่โดยไม่มีอำนาจเหล่านี้อยู่แล้ว แต่การระบุชัดทำให้ตรวจทานเจตนาได้
CREATE ROLE app_discovery
LOGIN
NOSUPERUSER
NOCREATEDB
NOCREATEROLE
NOINHERIT
NOBYPASSRLS
CONNECTION LIMIT 3
PASSWORD 'replace-through-secret-manager';
ALTER ROLE app_discovery SET default_transaction_read_only = on;
GRANT CONNECT ON DATABASE customer_portal TO app_discovery;
GRANT USAGE ON SCHEMA app TO app_discovery;
GRANT SELECT ON ALL TABLES IN SCHEMA app TO app_discovery;
default_transaction_read_only ปิดกั้นการเขียนตามปกติในเซสชันที่ใช้ค่าเริ่มต้น มันเป็นเข็มขัดเสริม ไม่ใช่สิ่งเดียวที่พึ่งพา การไม่มี INSERT, UPDATE, DELETE, TRUNCATE, CREATE และสิทธิ์ความเป็นเจ้าของต่างหากที่จำกัดบทบาทนี้ หากไคลเอนต์เปลี่ยนค่าทรานแซกชัน อย่าให้บทบาทนี้เป็นสมาชิกของกลุ่มเจ้าของแอป และอย่าทำให้เป็นเจ้าของสคีมา
ควรตรวจสิทธิ์ที่มีอยู่ก่อนให้ตัวสร้างแอปเชื่อมต่อ คิวรีต่อไปนี้สร้างหนึ่งแถวต่อสิทธิ์ของตาราง เพื่อให้ผู้ตรวจเห็นสิ่งใดก็ตามที่เกิน SELECT:
SELECT table_schema, table_name, privilege_type
FROM information_schema.role_table_grants
WHERE grantee = 'app_discovery'
ORDER BY table_schema, table_name, privilege_type;
ผลลัพธ์ที่ดีมีรูปแบบเช่น app | invoices | SELECT ผลลัพธ์ว่างอาจหมายความว่าการสำรวจมองไม่เห็นตารางที่จำเป็น แถวที่ลงท้ายด้วย UPDATE หมายถึงบทบาทมีอำนาจมากเกินไป ตรวจสิทธิ์สคีมาด้วย has_schema_privilege และสิทธิ์ฐานข้อมูลด้วย has_database_privilege ด้วย เพราะสิทธิ์ตารางไม่ได้บอกว่าบทบาทสร้างอ็อบเจ็กต์ที่อื่นได้หรือไม่
อย่าใช้สแนปช็อต production เป็นข้ออ้างในการแชร์ข้อมูลรับรองเจ้าของ สำเนายังอาจมีข้อมูลลูกค้า และเอเจนต์ที่เป็นเจ้าของอาจแก้ไขมันจนการเปรียบเทียบภายหลังไร้ประโยชน์ กำหนดตัวตนเฉพาะสำหรับการสำรวจในทุกสภาพแวดล้อม
การตรวจแค็ตตาล็อกต้องอยู่ภายใน allowlist
ตัวสร้างแอปควรสำรวจเฉพาะสคีมาที่อนุมัติ และบันทึกสิ่งที่ PostgreSQL รายงานจริง information_schema มีวิวแบบพกพาสำหรับตาราง คอลัมน์ ข้อจำกัด และสิทธิ์ pg_catalog เปิดเผยรายละเอียดของ PostgreSQL เช่น ดัชนี ชนิดข้อมูล expression ที่สร้างขึ้น และ row security ทั้งสองแหล่งดีกว่าความทรงจำของ LLM เกี่ยวกับตารางลูกค้าทั่วไป
เริ่มจาก allowlist เช่น app และ reporting ปฏิเสธ pg_catalog, information_schema, สคีมาชั่วคราว สคีมาของ extension และทุกสคีมาของ tenant ที่ไม่อยู่ในรายการ ในฐานะเป้าหมายของแอป คิวรีควรกรองทั้งระดับฐานข้อมูล บทบาท และ SQL เพราะ allowlist ที่อยู่แค่ในพรอมป์ต์อาจหายไปในการแชตครั้งถัดไป
SELECT
c.table_schema,
c.table_name,
c.ordinal_position,
c.column_name,
c.data_type,
c.is_nullable,
c.column_default
FROM information_schema.columns AS c
WHERE c.table_schema IN ('app', 'reporting')
ORDER BY c.table_schema, c.table_name, c.ordinal_position;
เก็บผลลัพธ์เป็นสแนปช็อตสคีมาพร้อมเวลาที่ดึงข้อมูลและตัวระบุฐานข้อมูล สแนปช็อตคือหลักฐานของสิ่งที่ตัวสร้างเห็น ไม่ใช่ความจริงถาวร PostgreSQL อาจเปลี่ยนระหว่างการสำรวจกับการสร้างโค้ด จึงควรเปรียบเทียบ fingerprint ใหม่ก่อนดีพลอย fingerprint ที่ใช้ได้จริงอาจแฮชคำอธิบายของตาราง คอลัมน์ ชนิดข้อมูล การอนุญาตค่าว่าง ค่าเริ่มต้น ข้อจำกัด และดัชนีตามลำดับ หาก fingerprint ต่างกัน ให้หยุดและสำรวจใหม่ แทนการเดาว่าการเปลี่ยนแปลงใดไม่เป็นอันตราย
การสุ่มตัวอย่างแถวเป็นการตัดสินใจเรื่องสิทธิ์อีกเรื่องหนึ่ง เมทาดาทาของคอลัมน์มักไม่มีข้อมูลส่วนบุคคล แต่ตัวอย่างแถวมักมี ควรไม่สุ่มแถวเลยเมื่อสร้างโค้ด หากจำเป็นต้องมีตัวอย่าง ให้เปิดเผยวิวที่ลบหรือปกปิดข้อมูลลับและตัวระบุตัวตนโดยตรง แล้วให้ SELECT เฉพาะวิวนี้ LIMIT 10 ไม่ได้ทำให้คิวรีที่ละเอียดอ่อนปลอดภัย มันเพียงทำให้การรั่วไหลเล็กลง
ควรดูแล search path เช่นเดียวกัน ตั้งให้มีสคีมาที่อนุมัติร่วมกับ pg_catalog ระบุชื่อของตารางที่สร้างให้ครบ และอย่าพึ่งพาอ็อบเจ็กต์ที่ PostgreSQL แก้ชื่อได้ก่อน ผู้โจมตีหรือ migration ที่ประมาทอาจสร้างอ็อบเจ็กต์ชื่อเดียวกันในสคีมาที่เขียนได้ ชื่อที่ระบุครบ เช่น app.orders ตัดความกำกวมนี้ออก
บทบาทรันไทม์ควรตรงกับการกระทำของผู้ใช้จริง
การสำรวจและรันไทม์เป็นงานคนละอย่าง แอปรันไทม์อาจต้องเพิ่มคำสั่งซื้อ อัปเดตร่าง หรือเรียกฟังก์ชันที่ออกแบบอย่างรอบคอบ แต่นั่นไม่ใช่เหตุผลให้สิทธิ์เขียนกว้างทั่วสคีมาที่สำรวจ สร้างเมทริกซ์สิทธิ์จากการกระทำของผู้ใช้ แล้วแปลงแต่ละการกระทำเป็นสิทธิ์ PostgreSQL ที่น้อยที่สุด
ตัวอย่างเช่น หน้าดูใบแจ้งหนี้อาจต้อง SELECT บน app.invoices และ app.invoice_lines ขณะที่ฟีเจอร์โน้ตต้อง SELECT และ INSERT บน app.invoice_notes มันไม่น่าต้อง DELETE ใบแจ้งหนี้ เข้าถึงข้อมูลรีเซ็ตรหัสผ่าน หรือสร้างสคีมา ให้สิทธิ์ใช้ sequence เมื่อการ insert นั้นอาศัย sequence จริงเท่านั้น PostgreSQL มอง sequence เป็นอ็อบเจ็กต์แยกต่างหาก ซึ่งมักทำให้ตัวสร้างที่ทดสอบด้วยบัญชีเจ้าของแปลกใจ
วิวและฟังก์ชันช่วยจำกัดพื้นผิวการเข้าถึงได้อีก วิวเปิดเผยคอลัมน์ที่อนุมัติขณะซ่อนฟิลด์ภายใน ฟังก์ชัน SECURITY DEFINER ทำการดำเนินการที่ควบคุมไว้หนึ่งอย่างซึ่งสิทธิ์ปกติอธิบายไม่ได้ แต่ต้องมี search_path ที่ตายตัว ตรวจอินพุตอย่างเข้มงวด และใช้เจ้าของที่ไม่มีอำนาจเกินจำเป็น มองฟังก์ชันนี้เป็นโค้ดสิทธิ์สูง ไม่ใช่ทางลัดข้ามโมเดลสิทธิ์
Row level security เพิ่มขอบเขตข้อมูลภายในตารางร่วมกัน มันไม่แทนที่สิทธิ์ของตาราง PostgreSQL จะตรวจว่าบทบาททำการดำเนินการนั้นได้หรือไม่ก่อน แล้วจึงใช้ row security policy เมื่อเปิดใช้และเกี่ยวข้อง ทดสอบด้วยบทบาทรันไทม์ที่แน่นอน เพราะเจ้าของตารางและบทบาทที่มี BYPASSRLS หลบเลี่ยง policy ได้ การทดสอบด้วยเจ้าของ migration แทบไม่พิสูจน์อะไรเกี่ยวกับสิ่งที่ผู้ใช้ปลายทางมองเห็น
อย่าใส่ข้อมูลลับในพรอมป์ต์ ซอร์สที่สร้างขึ้น browser bundle บิลด์ล็อก หรือภาพหน้าจอ เก็บข้อมูลรับรองรันไทม์ไว้ใน secret store ของสภาพแวดล้อมโฮสต์ และส่งเข้าเฉพาะกระบวนการเซิร์ฟเวอร์ แอปมือถือและเบราว์เซอร์เก็บรหัสผ่าน PostgreSQL เป็นความลับไม่ได้ จึงควรเรียก server API แทนการเชื่อมต่อโดยตรง หมุนเวียนข้อมูลรับรองสำหรับการสำรวจ รันไทม์ และ migration แยกกัน การรั่วไหลในส่วนหนึ่งไม่ควรเปิดอีกสองส่วน
อำนาจ migration ต้องอยู่ในเส้นทางอนุมัติแยกต่างหาก
ตัวสร้างแอปเสนอ migration ได้ แต่ไม่ควรรันด้วยเซสชันสำรวจหรือรันไทม์ ให้บทบาทแยกสำหรับงาน migration หรือให้ระบบดีพลอยที่ใช้อยู่สวมบทบาทนี้สำหรับงานที่อนุมัติเพียงครั้งเดียว เก็บข้อมูลรับรองนี้ให้ใช้งานไม่ได้ระหว่างการแชตและพรีวิวทั่วไป
การอนุมัติต้องครอบคลุม SQL ที่แน่นอน ตัวตนฐานข้อมูลเป้าหมาย fingerprint สคีมาที่ใช้เตรียม และพฤติกรรมการล็อกหรือการเขียนใหม่ที่คาดไว้ การอนุมัติประโยคภาษาคน เช่น «เพิ่มสถานะลูกค้า» เปิดช่องว่างมากเกินไป การเปลี่ยนแปลงที่รันได้อาจเพิ่มคอลัมน์ข้อความที่อนุญาตค่าว่าง สร้างตารางใหญ่ใหม่ สร้าง enum หรืออัปเดตทุกแถวที่มีอยู่ สิ่งเหล่านี้เป็นการดำเนินการต่างกันและมีรูปแบบความล้มเหลวต่างกัน
ผมใช้ migration packet ขนาดกะทัดรัด:
- เหตุผลของการเปลี่ยนแปลงและเวอร์ชันแอปที่ต้องใช้
- SQL ไปข้างหน้าที่แน่นอน และ SQL ย้อนกลับที่แน่นอนเมื่อระบุได้อย่างซื่อตรง
- อ็อบเจ็กต์ สิทธิ์ และแถวที่คำสั่งอาจกระทบ
- คิวรี preflight ผลลัพธ์ที่คาดหวัง และ fingerprint สคีมาใหม่
- lock timeout, statement timeout, ข้อมูลอ้างอิงของ backup หรือ snapshot และเจ้าของการปล่อยเวอร์ชัน
สคริปต์ย้อนกลับไม่ได้เป็น rollback เสมอไป การลบคอลัมน์ที่เพิ่งเพิ่มอาจย้อนการเปลี่ยนแปลงในแค็ตตาล็อกได้ แต่มันทำลายข้อมูลที่เขียนหลังการปล่อยเวอร์ชันด้วย แม้ transactional DDL ของ PostgreSQL ช่วยเรื่องการดำเนินการแค็ตตาล็อกได้มาก แต่ทรานแซกชันกู้ผลข้างเคียงภายนอกหรือข้อมูลที่คำสั่งภายหลังลบไปไม่ได้ ระบุการย้อนกลับที่ทำลายข้อมูลให้ชัด แทนการมอง DOWN เป็นคำวิเศษ
ตั้ง lock_timeout เพื่อให้ migration ล้มเหลวแทนการรอทรานแซกชันที่กำลังยุ่ง แล้วขวางงานใหม่ ตั้ง statement_timeout ตามการดำเนินการที่ตรวจทาน รันคิวรี preflight อีกครั้งในช่วงเปลี่ยนแปลง หากขนาดตาราง อ็อบเจ็กต์ที่ขัดแย้ง จำนวนค่า null หรือ fingerprint สคีมาต่างจากสมมติฐานที่อนุมัติ ให้ยกเลิก เอเจนต์ควรส่งรายงานความไม่ตรงกัน ไม่ใช่ด้น migration ใหม่กับ production
อย่าอนุมัติ migration อัตโนมัติเพียงเพราะการทดสอบที่สร้างขึ้นผ่าน การทดสอบมักรันบนสคีมาขนาดเล็กและสะอาด จึงพลาดคิวล็อก ค่า null เก่า ข้อจำกัดผิดปกติ extension และเวอร์ชันแอปที่ยังให้บริการอยู่ การอนุมัติคือจุดที่คนเชื่อมเจตนาที่สร้างขึ้นกับระบบจริง
Connection pooling เปลี่ยนวิธีคำนวณความปลอดภัย
พูลนำเซสชันฐานข้อมูลกลับมาใช้ใหม่ สถานะเซสชันจึงอาจอยู่ต่อหลังคำขอที่สร้างมัน หากคำขอหนึ่งรัน SET search_path เปลี่ยนบทบาท สร้างอ็อบเจ็กต์ชั่วคราว หรือปิด timeout ผู้ยืมรายถัดไปอาจได้รับผลนั้น แอปต้องหลีกเลี่ยงสถานะเซสชันที่เปลี่ยนได้ หรือรีเซ็ตอย่างเชื่อถือได้เมื่อคืนการเชื่อมต่อให้พูล
Transaction pooling ทำให้ขอบเขตเข้มขึ้น ไคลเอนต์อาจได้เซสชันเซิร์ฟเวอร์ต่างกันหลังแต่ละทรานแซกชัน จึงทำให้สมมติฐานเกี่ยวกับ prepared statement ระดับเซสชัน ตารางชั่วคราว advisory lock และการตั้งค่าระดับเซสชันใช้ไม่ได้ ตัวสร้างมักสร้างโค้ดที่ใช้ได้ผ่านการเชื่อมต่อโดยตรง แต่ล้มเหลวหลังพูลเพราะไม่เคยจำลองความต่างนี้ ตัดสินใจว่าพูลใช้โหมด session หรือ transaction แล้วใส่โหมดนั้นในการสร้างและการทดสอบ
วางงบจำนวนการเชื่อมต่อก่อนดีพลอย เริ่มจากจำนวนการเชื่อมต่อที่ฐานข้อมูลอนุญาต สำรองความจุให้การดูแลระบบ migration การมอนิเตอร์ และบริการอื่น แล้วแบ่งส่วนที่เหลือให้แต่ละอินสแตนซ์แอป หากสิบอินสแตนซ์เปิดการเชื่อมต่ออินสแตนซ์ละยี่สิบ PostgreSQL จะเห็นเซสชันที่เป็นไปได้สองร้อยเซสชัน แม้ทราฟฟิกจะเงียบ พูลขนาดเล็กแบบระมัดระวังที่มีคิวมักปลอดภัยกว่าการเพิ่มจำนวนการเชื่อมต่อจนฐานข้อมูลปฏิเสธ
ใช้ timeout ฝั่งเซิร์ฟเวอร์เป็นแนวป้องกัน statement_timeout จำกัดคำสั่งที่นาน lock_timeout จำกัดเวลารอล็อก และ idle_in_transaction_session_timeout ตัดเซสชันที่เปิดทรานแซกชันค้างโดยไม่ทำอะไร ตั้งค่าสำหรับแต่ละบทบาท แทนการเชื่อว่าไคลเอนต์ที่สร้างขึ้นทุกตัวจะจำได้ ตรวจด้วย SHOW ภายใต้บทบาทจริงและผ่านพูลจริง
Health check ควรเบา SELECT 1 ยืนยันการไปกลับได้ แต่ไม่ยืนยันว่าแอปเข้าถึงตารางที่อนุมัติได้หรือมี search path ถูกต้อง Readiness check อาจคิวรีวิวขนาดเล็กที่คงที่ด้วยบทบาทรันไทม์ อย่าใส่ migration ไว้ตอนเริ่มแอป เพราะหลายอินสแตนซ์ที่แข่งกันแก้สคีมาสร้าง coupling ที่การออกแบบนี้ต้องการตัดออกพอดี
คอลัมน์ที่สร้างขึ้นเองต้องล้มเหลวก่อนรันคิวรี
LLM สร้าง identifier ที่ดูสมเหตุผลขึ้นเองได้ หากพรอมป์ต์พูดถึงชื่อที่แสดงของลูกค้า โค้ดที่สร้างอาจเรียก customers.display_name แม้ฐานข้อมูลเก็บ given_name และ family_name ฐานข้อมูลจะปฏิเสธคิวรีนั้น ซึ่งดีกว่าการอ่านฟิลด์ผิดเงียบ ๆ แต่ error ใน production ก็ยังเป็นวิธีตรวจสคีมาที่แย่
สร้างอาร์ทิแฟกต์สคีมาแบบมีชนิดข้อมูลจากสแนปช็อตแค็ตตาล็อกที่อนุมัติ และให้เป็นแหล่งเดียวสำหรับประกอบคิวรี ตารางหรือคอลัมน์ที่ไม่มีในอาร์ทิแฟกต์นี้ควรทำให้การสร้างล้มเหลว อย่าให้โมเดลซ่อม error ด้วยการเพิ่ม migration เว้นแต่งานจะเข้าสู่ส่วน migration อย่างชัดเจน identifier ที่หายไปอาจหมายถึงการสำรวจเก่า การสะกดผิด สภาพแวดล้อมผิด หรือความต้องการผลิตภัณฑ์จริง แต่ละกรณีต้องตอบสนองต่างกัน
การตรวจแบบ static ควร parse SQL และ resolve ทุก relation กับคอลัมน์เทียบกับสแนปช็อต จากนั้น prepare statement กับฐานข้อมูลแบบใช้แล้วทิ้ง หรือภายในทรานแซกชันที่เขียนไม่ได้ parser ของ PostgreSQL จับคอลัมน์ที่ไม่รู้จัก การอ้างอิงกำกวม ความผิดพลาดของชนิด operator และการ cast ที่ผิดได้มาก โดยไม่ต้องมีข้อมูลธุรกิจที่สำเร็จ รัน integration test ด้วยบทบาทรันไทม์ เพื่อให้สิทธิ์และ row policy มีผลด้วย
รายงานความล้มเหลวต้องมีรายละเอียดพอให้คนตัดสินใจ ใส่ตำแหน่ง SQL identifier ที่ resolve ไม่ได้ identifier ที่ถูกต้องใกล้เคียง fingerprint ของสแนปช็อต และตัวตนฐานข้อมูลเป้าหมาย คำแนะนำมีประโยชน์ แต่การแทนที่แบบ fuzzy อัตโนมัติอันตราย การเปลี่ยน billing_address_id เป็น shipping_address_id เพียงเพราะชื่อคล้ายกัน อาจให้ SQL ที่ถูกต้องแต่มีความหมายทางธุรกิจผิด
สำหรับ filter และ sort แบบ dynamic ให้แมปชื่อ public API กับชุด expression SQL แบบปิดที่ระบุสคีมาครบ อย่าวาง identifier ที่โมเดลส่งมาใน SQL แม้ผ่าน value parameter parameter ปกป้องค่า ไม่ได้ปกป้องชื่อตารางหรือคอลัมน์ หากผู้ใช้เลือกฟิลด์เรียงลำดับได้ ให้แปล created เป็น expression ที่รู้จัก เช่น app.orders.created_at และปฏิเสธ token อื่นทั้งหมด
Schema drift ควรหยุดการปล่อยเวอร์ชัน ไม่ใช่กระตุ้นให้กระทบยอดแบบสร้างสรรค์ สร้างสแนปช็อตใหม่ แสดง diff และทดสอบซ้ำ ความล่าช้านั้นอาจดูจุกจิก แต่ถูกกว่าการดีพลอยโค้ดที่เข้าใจฐานข้อมูลอยู่เพียงในบันทึกการสนทนา
SQL ที่ทำลายข้อมูลต้องมีนโยบายปฏิเสธและหลักฐาน
ตัวสร้างควรจัดประเภท SQL ก่อนที่ใครจะรันได้ บล็อก DROP, TRUNCATE, DELETE หรือ UPDATE วงกว้างที่ไม่มี predicate ตรวจทาน การเปลี่ยนเจ้าของ การยกระดับสิทธิ์ การเปลี่ยน extension และคำสั่งที่มุ่งนอกสคีมาที่อนุมัติ ให้ ALTER TABLE ต้องผ่านการตรวจทาน ไม่ใช่ปลอดภัยโดยอัตโนมัติ การเปลี่ยนชนิดคอลัมน์หรือเพิ่มข้อจำกัดห้ามค่าว่างอาจสแกนหรือเขียนข้อมูลใหม่และถือ lock ที่มีผลกระทบมาก
การจับคู่ข้อความอย่างเดียวอ่อนแอ เพราะ SQL มี comment identifier ที่อยู่ในเครื่องหมายคำพูด ฟังก์ชัน และวิธีแสดงผลข้างเคียงมากมาย Parse statement ด้วย parser ที่เข้าใจ PostgreSQL ตรวจ syntax tree ของมัน และพึ่งบทบาทฐานข้อมูลให้ปฏิเสธการกระทำต้องห้ามด้วย ตัวจัดประเภทช่วยการตรวจทาน สิทธิ์บังคับขอบเขต ทั้งคู่ไม่ควรแบกภาระทั้งหมดเพียงลำพัง
ใช้ฐานข้อมูล staging ที่กู้จากสแนปช็อตล่าสุดซึ่งปกป้องอย่างเหมาะสม เมื่อ migration ต้องอาศัยรูปร่างตารางจริงหรือการกระจายข้อมูล ใช้ migration packet เดียวกันที่นั่น เก็บระยะเวลาและการสังเกตล็อก รันทดสอบแอปด้วยข้อมูลรับรองรันไทม์ แล้วทิ้งสภาพแวดล้อม อย่าแก้ SQL เงียบ ๆ ระหว่าง staging กับ production การแก้ทุกครั้งสร้างอาร์ทิแฟกต์ใหม่ที่ต้องมี fingerprint และการอนุมัติใหม่
ล็อกควรเชื่อมข้อเสนอเข้ากับการรันโดยไม่บันทึกข้อมูลลับหรือแถวละเอียดอ่อน บันทึกผู้อนุมัติ migration artifact ที่เปลี่ยนไม่ได้ digest ตัวตนเป้าหมาย สถานะเริ่มและเสร็จ และรายละเอียด error ของ PostgreSQL เก็บ diff ที่สร้างและผล preflight ไว้ การแชตกับเอเจนต์เป็นบริบทที่มีประโยชน์ แต่ไม่ใช่บันทึกตรวจสอบ เพราะผู้ใช้แตกแขนง ลองใหม่ และถอดความคำสั่งได้
สแนปช็อตและการควบคุม rollback ลดเวลาการกู้คืน แต่ไม่ได้ทำให้ SQL ที่ทำลายข้อมูลยอมรับได้ สแนปช็อตอาจกู้ทั้งฐานข้อมูลกลับจุดก่อนหน้า ทั้งที่ต้องการเพียงคอลัมน์หนึ่งที่ถูกลบ และการกู้อาจทิ้งการเขียนที่ถูกต้องหลังสแนปช็อต ทดสอบการกู้คืนแยกต่างหาก และระบุว่าใครมีสิทธิ์เรียกใช้
เมื่อผมใช้ Koder.ai กับแอปที่แตะฐานข้อมูลเดิม ผมจะทำงานในโหมดวางแผนจนกว่าจะตรวจซอร์สที่ส่งออกและขอบเขตฐานข้อมูลที่เสนอแล้ว สแนปช็อตและ rollback คือมาตรการกู้คืน ไม่ใช่สิทธิ์ให้ข้ามการตรวจทาน กฎเดียวกันนี้ใช้กับตัวสร้างทุกตัว ความสะดวกของผลิตภัณฑ์ต้องอยู่หลังการบังคับใช้ของฐานข้อมูล
การเปลี่ยนสคีมาต้องทนต่อแอปหลายเวอร์ชัน
migration จะปลอดภัยก็ต่อเมื่อทั้งแอปเก่าและแอปใหม่รันได้ในช่วงปล่อยเวอร์ชัน production แทบไม่เคยเปลี่ยนจากเวอร์ชันหนึ่งไปอีกเวอร์ชันในทันที คำขออาจไปถึงอินสแตนซ์เก่าระหว่างที่อินสแตนซ์ใหม่เริ่ม งานคิวอาจมี payload เก่า และ rollback อาจนำโค้ดเมื่อวานกลับมาทำงานกับสคีมาวันนี้ ตัวสร้างแอปที่ตรวจเพียงโค้ดสุดท้ายกับสคีมาสุดท้ายจะมองไม่เห็นช่วงทับซ้อนนี้
ให้เลือกการเปลี่ยนแปลงแบบเพิ่มก่อน เพิ่มคอลัมน์ที่อนุญาตค่าว่าง เพิ่มตารางใหม่ หรือเพิ่มดัชนีโดยไม่ลบเส้นทางเดิม ดีพลอยโค้ดที่อ่านข้อมูลได้ทั้งสองรูปแบบ และเขียนรูปแบบใหม่เมื่อเหมาะสม เติมข้อมูลแถวเดิมด้วยงานที่ตรวจทานแยกต่างหาก เฝ้าดู error และ lag แล้วจึงทำให้ฟิลด์ใหม่เป็นข้อมูลหลัก ลบคอลัมน์หรือข้อจำกัดเก่าใน release ถัดไป หลังมีหลักฐานว่าไม่มีโค้ดที่กำลังรันใช้อยู่
ลำดับนี้ใช้เวลานานกว่าการสร้างคำสั่ง ALTER TABLE เพียงคำสั่งเดียว แต่มันแยกความล้มเหลวออกจากกัน หากโค้ดใหม่ทำงานผิดก่อนลบ เส้นทางเก่ายังมีอยู่ หาก backfill ตามไม่ทัน ก็หยุดพักได้โดยไม่จับ release ของแอปเป็นตัวประกัน หากดีพลอย rollback แอปเก่ายังเข้าใจฐานข้อมูล การเพิ่ม release หนึ่งครั้งถูกกว่าการพบระหว่าง rollback ว่า binary ก่อนหน้าคิวรีคอลัมน์ที่ migration ลบไปแล้ว
การเปลี่ยนชื่อต้องระวังเป็นพิเศษ เพราะ PostgreSQL เปลี่ยนชื่อทันที ตัวสร้างอาจเสนอเปลี่ยน customer_ref เป็น customer_id เพราะชื่อใหม่อ่านง่ายกว่า อินสแตนซ์เก่าจะล้มเหลวทันทีที่ migration commit เพิ่ม customer_id เก็บทั้งสองฟิลด์ให้สอดคล้องกันในโค้ดแอปหรือ trigger ที่ตรวจทานอย่างแคบ ย้ายผู้อ่าน แล้วลบ customer_ref เมื่อผู้เขียนเก่าหายไป การซ้ำชั่วคราวคือหนี้ที่มองเห็นและมีเงื่อนไขการลบ ส่วนการเปลี่ยนชื่อทันทีคือ coupling ของ release ที่มองไม่เห็น
ค่าเริ่มต้นและข้อจำกัดห้ามค่าว่างอาจซ่อนงานไว้เช่นกัน ก่อนอนุมัติ SET NOT NULL ให้นับค่า null ที่มีอยู่และพิสูจน์ว่าผู้เขียนที่ยังทำงานอยู่ทุกตัวส่งค่า สำหรับตารางใหญ่หรือมีงานหนาแน่น ให้ตรวจว่า PostgreSQL เวอร์ชันนั้นตรวจข้อจำกัดอย่างไรและใช้ lock ใด ตัวสร้างควรรายงานเงื่อนไขล่วงหน้าเหล่านี้ แทนการอนุมานจากสคีมาที่ไม่มีทราฟฟิกตัวแทน
อย่าทำ data backfill ภายใน schema transaction แบบไม่มีขอบเขต อัปเดตแถวเป็น batch ที่วัดได้ผ่าน worker ที่อนุมัติ บันทึกความคืบหน้าด้วย cursor ที่คงที่ และทำให้การลองใหม่เป็น idempotent การลองใหม่เป็น idempotent เมื่อใช้ซ้ำสองครั้งแล้วได้สถานะที่ตั้งใจ ไม่ใช่แค่ PostgreSQL ยอมรับคิวรีครั้งที่สอง สำหรับค่าที่คำนวณได้ ให้บันทึกเวอร์ชันของวิธีคำนวณ หากโค้ดภายหลังอาจคำนวณต่างกัน
release packet ควรระบุจุดความเข้ากันได้สี่ข้อ:
- เวอร์ชันแอปที่เก่าที่สุดซึ่งอนุญาตให้รันก่อน migration
- สถานะสคีมาที่ทั้งเวอร์ชันเก่าและใหม่ยอมรับ
- สัญญาณที่อนุญาตให้ทำ release สำหรับล้างข้อมูลแบบทำลาย
- เส้นทางกู้คืน หากโค้ดใหม่ rollback หลังข้อมูลเปลี่ยนไปแล้ว
คิวรีที่สร้างควรหลีกเลี่ยง SELECT * ระหว่างช่วงเปลี่ยนเหล่านี้ การเพิ่มคอลัมน์อาจเปลี่ยนต้นทุนการสแกน การถอดรหัสผลลัพธ์ การแมปตามตำแหน่ง และการเปิดเผยข้อมูล แม้ SQL เก่ายัง parse ได้ ให้ระบุคอลัมน์พร้อมสคีมาอย่างชัดเจน และสร้าง decoder จากสแนปช็อตสคีมาเดียวกัน สิ่งนี้ทำให้การตรวจซอร์สบอกได้ชัดว่าข้อมูลใดข้ามขอบเขตฐานข้อมูล
เครื่องมือ migration ที่เตรียมไว้มักติดตามเวอร์ชันที่ใช้แล้วในตาราง แต่หมายเลขเวอร์ชันอย่างเดียวไม่ได้พิสูจน์ความเข้ากันได้ บันทึก digest ของ SQL artifact ที่แน่นอน เพราะไฟล์สองไฟล์ที่ชื่ออ่านง่ายเหมือนกันอาจมีคำสั่งต่างกัน ตัวรันควรปฏิเสธเวอร์ชันที่บันทึกไปแล้วหาก digest ต่างกัน และปฏิเสธ migration ถัดไปหากไม่มีตัวก่อนหน้าที่จำเป็น
อย่าให้ทุกอินสแตนซ์ของแอปรัน migration ตอนเริ่มต้น แม้เครื่องมือ migration จะใช้ advisory lock การเริ่มต้นก็ยังขึ้นกับข้อมูลรับรองสิทธิ์สูง และงานสคีมาต้องเสร็จก่อน health check หมดเวลา ให้รัน migration ใน release job เดียว รอผลที่บันทึกไว้ แล้วเริ่มอินสแตนซ์รันไทม์ด้วยตัวตนที่แก้สคีมาไม่ได้ หากระบบ release แยกสองช่วงนี้ไม่ได้ ให้แก้ระบบ release ก่อนมอบอำนาจเจ้าของแก่แอป
ทดสอบไทม์ไลน์นี้ ไม่ใช่แค่ปลายทาง: โค้ดเก่ากับสคีมาเก่า โค้ดเก่ากับสคีมาที่ขยาย โค้ดใหม่กับสคีมาที่ขยาย และโค้ดที่ rollback หลังมีการเขียนจากโค้ดใหม่ การล้างข้อมูลควรมีการทดสอบของตัวเองภายหลัง เมทริกซ์นี้จับการเปลี่ยนแปลงที่ถูกต้องตามไวยากรณ์แต่ย้อนกลับในการปฏิบัติไม่ได้
พิสูจน์ขอบเขตด้วยการทดสอบเชิงลบ
การออกแบบด้านความปลอดภัยยังไม่สมบูรณ์จนกว่าการกระทำที่ห้ามไว้จะล้มเหลวในการทดสอบ เชื่อมต่อด้วยบัญชีสำหรับการสำรวจแล้วพยายาม insert สร้างตาราง และ SET TRANSACTION READ WRITE เชื่อมต่อด้วยบัญชีรันไทม์แล้วพยายามเข้าถึงตารางที่ไม่ได้รับสิทธิ์ อ่านข้าม tenant ที่ row security ปกป้อง และเปลี่ยนสคีมา ผลที่คาดหวังคือ PostgreSQL permission error ไม่ใช่คำสัญญาในล็อกเอเจนต์
รันการทดสอบเชิงบวกด้วย การสำรวจต้องยังอ่านรายการแค็ตตาล็อกที่อนุญาตได้ทั้งหมด รันไทม์ต้องทำทุกการกระทำของผู้ใช้ที่อนุมัติผ่านพูล การรัน migration ต้องใช้ได้ผ่านเส้นทางอนุมัติเท่านั้น ขอบเขตที่ขัดขวางงานปกติของผลิตภัณฑ์ จะล่อให้ใครสักคนแทนที่มันด้วยข้อมูลรับรองเจ้าของในระหว่างเหตุขัดข้อง
เก็บ access contract ขนาดเล็กไว้ข้างซอร์สแอป ควรระบุฐานข้อมูล สคีมาที่อนุญาต ขอบเขตการสำรวจ การดำเนินการรันไทม์ โหมดพูล นโยบาย timeout ผู้อนุมัติ migration วิธีทำ fingerprint สคีมา และ statement ที่ห้าม เปรียบเทียบสิทธิ์จริงกับ contract นี้ในการตรวจต่อเนื่อง สิทธิ์ PostgreSQL ที่เปลี่ยนไปคือ configuration drift แม้ไม่มีใครแก้โค้ดแอป
ตรวจซ้ำหลังเปลี่ยนบทบาท เพิ่มตาราง กู้ฐานข้อมูล อัปเกรดพูล และเปลี่ยนโฮสติ้ง default privileges สำคัญกับอ็อบเจ็กต์ในอนาคต การให้ SELECT ON ALL TABLES ครอบคลุมตารางปัจจุบัน ไม่ครอบคลุมตารางที่สร้างภายหลัง ตัดสินใจว่าตารางใหม่ควรมองไม่เห็นจนกว่าจะตรวจทาน หรือควรรวมผ่าน default privileges ที่ตั้งอย่างแคบ ผมเลือกให้มองไม่เห็นเป็นค่าเริ่มต้น เพราะการให้สิทธิ์อย่างชัดเจนบังคับให้ต้องพูดคุยเรื่องการเข้าถึงตารางใหม่
รวมการเพิกถอนสิทธิ์ไว้ในแผนทดสอบ ปิดข้อมูลรับรองสำหรับการสำรวจ แล้วตรวจว่าทราฟฟิกรันไทม์ยังทำงาน ปิดรันไทม์ แล้วตรวจว่าเครื่องมือ migration ไม่แอบใช้ตัวตนที่มีอำนาจกว่าแทน จากนั้นหมุนข้อมูลลับแต่ละตัวขณะที่การเชื่อมต่อยังทำงาน และดูว่าพูลเลิกใช้เซสชันเก่าภายในช่วงเวลาที่ตั้งไว้หรือไม่ การเปลี่ยนรหัสผ่านไม่ยุติเซสชันที่ยืนยันตัวตนแล้ว จึงต้องมีขั้นตอน recycle พูลหรือ policy ยุติเซสชัน PostgreSQL ที่ชัดเจน
ตรวจข้อความความล้มเหลวระหว่างการทดสอบเหล่านี้เพื่อหาการเปิดเผยโดยไม่ตั้งใจ error ของ PostgreSQL อาจมีชื่อ relation ส่วนของ SQL ชื่อข้อจำกัด และค่าที่ส่งเข้าไป ส่ง error ละเอียดไปยัง server log ที่จำกัดสิทธิ์ ส่ง public error ที่คงที่กลับให้ไคลเอนต์ และอย่าป้อนสตรีม error จาก production ทั้งหมดกลับเข้าสู่การสนทนากับเอเจนต์ ตัวสร้างต้องใช้ตำแหน่ง statement และคำตอบฐานข้อมูลที่ผ่านการปกปิดเพื่อซ่อมโค้ด ไม่ต้องใช้ค่าของลูกค้า
มีการทดสอบสุดท้ายที่จับการเชื่อมต่อที่ไม่ปลอดภัยได้มากอย่างน่าประหลาด: นำข้อมูลรับรอง migration ออกทั้งหมด แล้วรันชุดทดสอบแอป หากการเริ่มปกติ health check พรีวิว หรือการจัดการคำขอล้มเหลว แปลว่าความเป็นเจ้าของสคีมารั่วเข้าสู่เส้นทางรันไทม์ แก้ coupling นี้ก่อนเชื่อมตัวสร้างแอปกับ production ตัวสร้างแอป AI ทำงานกับฐานข้อมูลที่ไม่ได้เป็นเจ้าของได้ แต่ PostgreSQL ต้องปฏิเสธได้เมื่อโค้ดที่สร้างขึ้นลืมข้อตกลงนี้
คำถามที่พบบ่อย
ตัวสร้างแอป AI ใช้ฐานข้อมูล PostgreSQL ที่มีอยู่แล้วของฉันได้หรือไม่
ได้ หากเครื่องมือเชื่อมต่อผ่านบทบาทเฉพาะและสำรวจเฉพาะสคีมาที่อนุมัติ แยกการสำรวจ คำสั่งรันไทม์ และการย้ายสคีมาไว้คนละเส้นทางสิทธิ์ เพื่อไม่ให้การเชื่อมต่อเครื่องมือกลายเป็นการมอบสิทธิ์เจ้าของสคีมา
ผู้ใช้ PostgreSQL แบบอ่านอย่างเดียวรับประกันได้หรือไม่ว่าจะไม่มีข้อมูลเปลี่ยนแปลง
บทบาทที่มีเพียง SELECT และไม่ได้เป็นเจ้าของอ็อบเจ็กต์คือการควบคุมหลัก default_transaction_read_only ช่วยเพิ่มการป้องกัน แต่ไม่ควรใช้ทดแทนสิทธิ์ที่กว้างเกินไปหรือการเป็นสมาชิกที่สืบทอดมา
ควรให้รหัสผ่านเจ้าของฐานข้อมูลแก่ตัวสร้างแอปหรือไม่
ไม่ ข้อมูลรับรองเจ้าของทำลายขอบเขตการป้องกัน และเปิดให้ SQL ที่สร้างขึ้นแก้สิทธิ์ ตาราง และข้อมูลได้ สร้างข้อมูลรับรองแยกสำหรับการสำรวจ รันไทม์ และงานย้ายสคีมาที่ควบคุมไว้
ตัวสร้างแอปจะเรียนรู้สคีมาของฉันอย่างปลอดภัยได้อย่างไร
ให้เครื่องมือคิวรีวิว information_schema และ pg_catalog ที่อนุมัติผ่านบทบาทสิทธิ์จำกัด แล้วบันทึกสแนปช็อตที่มี fingerprint หลีกเลี่ยงการสุ่มตัวอย่างแถว เว้นแต่จะเตรียมวิวที่ปกปิดข้อมูลไว้เพื่อจุดประสงค์นี้
จะเกิดอะไรขึ้นเมื่อ AI สร้างชื่อคอลัมน์ PostgreSQL ขึ้นเอง
การสร้างควรล้มเหลวเมื่อเทียบกับสแนปช็อตสคีมาแบบมีชนิดข้อมูลก่อนดีพลอย รายงานชื่อที่ไม่รู้จักและชื่อที่ใกล้เคียงที่ใช้ได้ แต่ให้คนตัดสินว่าควรแก้โค้ด สำรวจใหม่ หรือทำ migration ที่อนุมัติ
แอปเชื่อมต่อโดยตรงจากเบราว์เซอร์หรือมือถือได้หรือไม่
ไม่ควรเชื่อมต่อ PostgreSQL โดยตรง เพราะไคลเอนต์เหล่านั้นเก็บรหัสผ่านฐานข้อมูลเป็นความลับไม่ได้ ให้เข้าถึงฐานข้อมูลในกระบวนการเซิร์ฟเวอร์ แล้วให้เบราว์เซอร์หรือแอปมือถือเรียก API ของเซิร์ฟเวอร์
แอปที่สร้างขึ้นต้องมี connection pool หรือไม่
โดยทั่วไปควรใช้ แต่ต้องตั้งค่าอย่างตั้งใจ จำกัดจำนวนเซสชันทั้งหมด เลือกโหมด session หรือ transaction รีเซ็ตสถานะที่เปลี่ยนแปลงได้ และทดสอบโค้ดที่สร้างผ่านพูลเดียวกับที่ใช้ในระบบจริง
สามารถย้อนกลับ PostgreSQL migration ได้อย่างปลอดภัยหรือไม่
การเปลี่ยนแปลงในแค็ตตาล็อกบางอย่างย้อนกลับได้สะอาดภายในทรานแซกชัน แต่การสูญเสียข้อมูลและผลข้างเคียงภายนอกย้อนกลับไม่ได้ ตรวจ SQL ไปข้างหน้าและ SQL สำหรับย้อนกลับแยกกัน และมองสแนปช็อตเป็นเครื่องมือกู้คืน ไม่ใช่หลักฐานว่าการเปลี่ยนแปลงปลอดภัย
จะหยุดไม่ให้ตัวสร้างแอปเปลี่ยนตารางที่ไม่ได้อนุมัติได้อย่างไร
ใช้ allowlist ของสคีมา ชื่อแบบระบุสคีมา สิทธิ์ที่แคบ นโยบาย SQL ที่แยกวิเคราะห์แล้ว และการทดสอบสิทธิ์เชิงลบ บทบาท PostgreSQL ต้องปฏิเสธการกระทำ แม้โมเดลหรือตัวตรวจนโยบายจะพลาด
ตัวสร้างแอปควรสำรวจสคีมาใหม่บ่อยแค่ไหน
สำรวจใหม่ทุกครั้งที่ fingerprint ที่เก็บไว้ต่างออกไป และหลัง migration การกู้คืน หรือการเปลี่ยนสภาพแวดล้อม อย่ารีเฟรชเงียบ ๆ ระหว่างการปล่อยเวอร์ชัน ให้แสดง diff และรันการตรวจสอบกับสแนปช็อตใหม่อีกครั้ง