שרת ה-pgwire של Provisa¶
Provisa חושפת נקודת קצה של פרוטוקול חיווט PostgreSQL (pgwire). כל כלי שדובר את פרוטוקול הלקוח של PostgreSQL — psycopg2, asyncpg, DBeaver, Tableau, JDBC — יכול להתחבר ולשאול נתוני Provisa דרך אותו צינור ממשל השולט ב-API של HTTP. (REQ-266)
שאילתות עוברות דרך מחסנית הממשל המלאה: אכיפת RLS, כללי מיסוך, שומרי קשר, בדיקות גישת-דומיין. (REQ-001, REQ-002, REQ-263) ממשק ה-pgwire אינו עוקף. (REQ-002, REQ-266)
פרטי חיבור¶
השרת מופעל כאשר PROVISA_PGWIRE_PORT מוגדר למספר שלם שאינו אפס. הוא מושבת כברירת מחדל. (REQ-527) [tool-verified: app.py:1739]
TLS. הגדירו את PROVISA_PGWIRE_CERT ו-PROVISA_PGWIRE_KEY לנתיבי תעודת PEM ומפתח. כששניהם קיימים, השרת עוטף חיבורים נכנסים ב-TLS. כשהם נעדרים, TLS כבוי והשרת עונה N לבקשות משא-ומתן SSL. (REQ-530) [tool-verified: server.py:1746-1750]
גרסת שרת מדווחת. לקוחות רואים 14.0.provisa. כלים המגבילים תכונות לפי מספר הגרסה עשויים להתנהג כאילו הם מחוברים ל-PostgreSQL 14. (REQ-579) [tool-verified: server.py:208]
אימות (Authentication)¶
שני מצבים, נשלטים על ידי המפתח provider ב-auth_config:
| מצב | ערך provider |
התנהגות |
|---|---|---|
| Trust | none (או middleware אימות לא-פעיל) |
שם המשתמש שנשלח על ידי הלקוח משמש ישירות כ-role_id. הסיסמה מתעלמת. |
| Simple | simple |
הסיסמה מאומתת מול ספק האימות simple (bcrypt). שם המשתמש הופך ל-role_id בהצלחה. (REQ-124) |
כל ערך provider אחר מחזיר שגיאת FATAL בהתחברות. (REQ-529) הפרוטוקול תמיד משתמש בסוג אימות PG 3 (סיסמת cleartext). (REQ-529) אל תשתמשו במצב trust על חיבור לא-מוצפן. [tool-verified: server.py:282-311]
מה עובד¶
SELECT¶
כל משפטי ה-SELECT עוברים דרך צינור הממשל (_pipeline.py). (REQ-001, REQ-262, REQ-266) הצינור:
- כותב מחדש SQL סמנטי ל-SQL פיזי (
rewrite_semantic_to_physical) - מחיל ממשל (RLS, מיסוך, גישת דומיין) (REQ-263)
- מאמת מול סכמה רשומה (REQ-011)
- מנתב ל-Trino או ל-pool מקור ישיר (REQ-027, REQ-028)
שאילתות פשוטות מרובות-משפטים נתמכות. משפטים מופרדי-נקודה-פסיק מפוצלים ומבוצעים בסדר. (REQ-580) [tool-verified: server.py:318-381]
שאילתות פרמטריות ($1, $2, ...) נתמכות הן במצב שאילתה-פשוטה והן במצב שאילתה-מורחבת (Bind/Execute). פרמטרים מוחלפים כליטרלים לפני הביצוע. (REQ-581) [tool-verified: server.py:78-85]
SELECT * FROM fn(args) ו-SELECT fn(args) — כאשר fn נותן שם לפונקציה עוקבת (tracked) רשומה — מיורטים לפני צינור הממשל ומנותבים דרך ה-executor המנוהל היחיד (invoke_tracked_function). התוצאה היא סט שורות מוקלד זהה למה שכל surface אחר מחזיר עבור אותו command. writable_by וכללי ממשל נאכפים בתוך ה-executor. (REQ-1156) [tool-verified: provisa/pgwire/function_call.py:74-88]
DDL¶
משפטי DDL מזוהים על ידי ה-regex ב-server.py ומנותבים ל-DdlHandler. לתפקיד חייבת להיות היכולת "ddl". (REQ-042) בלעדיה, המשפט נדחה עם SQLSTATE 42501. [tool-verified: ddl_handler.py:82-83]
צורות ה-DDL המזוהות הן:
CREATE TABLE / VIEW / INDEX / UNIQUE INDEX / SEQUENCE / SCHEMA
ALTER TABLE / INDEX / SEQUENCE / VIEW
DROP TABLE / VIEW / INDEX / SEQUENCE / SCHEMA
[tool-verified: server.py:56-61]
שני נתיבי ביצוע קיימים, תלוי ב-ddl_catalog: (REQ-582)
נתיב Trino — משמש כאשר ddl_catalog הוא Iceberg, Hive, או קטלוג Trino לא-רשום אחר (למשל iceberg, hive, otel, results). רק CREATE TABLE ו-CREATE VIEW נתמכים בנתיב זה. ניסיון ALTER, DROP, או CREATE INDEX מעלה שגיאה. שם הטבלה מוסמך במלואו כ-catalog.schema.table. [tool-verified: ddl_handler.py:92-100]
נתיב ישיר — משמש כאשר ddl_catalog תואם מזהה מקור רשום. DDL מלא נתמך: CREATE, ALTER, DROP, אינדקסים, רצפים. CREATE TABLE ו-CREATE VIEW מוסמכים-סכמה כ-schema.table. כל שאר ה-DDL (ALTER, DROP, CREATE INDEX) עובר כפי-שהוא לאחר הגדרת הקשר הסכמה. עבור מקורות PostgreSQL ו-SQLite, ההקשר מוגדר עם SET search_path TO schema. עבור MySQL ו-MariaDB, ההקשר מוגדר עם USE schema. [tool-verified: ddl_handler.py:139-170, ddl_handler.py:207-213]
לאחר DDL בכל אחד מהנתיבים, הטבלה החדשה נרשמת להקשר הקימפול של התפקיד כך שהיא ניתנת לשאילתה מיידית. (REQ-583) [tool-verified: ddl_handler.py:216-250]
פתירת יעד כתיבה. קטלוג ה-DDL והסכמה מגיעים משדות ddl_catalog ו-ddl_schema של הדומיין. אם ddl_catalog אינו מוגדר, המערכת ברירת-מחדל לקטלוג Iceberg. אם ddl_schema אינו מוגדר, ברירת המחדל היא מזהה הדומיין. הדומיין נפתר דרך רשימת domain_access של התפקיד. (REQ-584) [tool-verified: app.py:804-811, ddl_handler.py:104-115]
COPY¶
הן COPY ... TO STDOUT והן COPY ... FROM STDIN נתמכים. (REQ-585) [tool-verified: copy_handler.py:231-257]
COPY TO STDOUT — מייצא תוצאות שאילתה בפורמט חיווט COPY של PG. שתי צורות עובדות:
-- Table reference
COPY my_table TO STDOUT WITH (FORMAT csv)
-- Arbitrary query
COPY (SELECT col1, col2 FROM my_table WHERE ...) TO STDOUT WITH (FORMAT text)
פורמטים נתמכים: text (מופרד-tab, ברירת מחדל) ו-csv. פורמט בינארי אינו נתמך בפלט COPY. [tool-verified: copy_handler.py:36-52]
COPY FROM STDIN — מכניס שורות לטבלת יעד. מוגבל למקורות מסוג postgresql, mysql, sqlite, או mariadb. (REQ-586) ניסיון COPY FROM מול מקור Trino-בלבד (למשל Iceberg) מעלה שגיאת הרשאה. [tool-verified: copy_handler.py:65, copy_handler.py:351-356]
אם לא סופקה רשימת עמודות, העמודות נגזרות מהסכמה הרשומה. [tool-verified: copy_handler.py:357]
עסקאות ופקודות Session¶
SET, BEGIN, COMMIT, ROLLBACK, SAVEPOINT, RELEASE, DISCARD, RESET, ו-DEALLOCATE מיורטים ומחזירים תגובת הצלחה ריקה. (REQ-587) השרת חסר-מצב (stateless) ביחס לעסקאות — אין תמיכה בבידוד עסקאות או rollback. (REQ-587) [tool-verified: catalog.py:27-31, catalog.py:1129-1132]
יירוט קטלוג (Catalog Intercept)¶
שאילתות מול information_schema ו-pg_catalog נענות מקומית ללא round-trip ל-Trino. (REQ-532) שכבת היירוט בונה מסד נתונים DuckDB בזיכרון לכל בקשה, מאוכלס מהקשר הקימפול של התפקיד. (REQ-532) [tool-verified: catalog.py:210-213]
טבלאות מיורטות:
information_schema: schemata, tables, columns, views, table_constraints, key_column_usage, referential_constraints
pg_catalog: pg_namespace, pg_class, pg_attribute, pg_type, pg_attrdef, pg_description, pg_index, pg_constraint, pg_proc, pg_roles, pg_auth_members, pg_database, pg_settings, pg_tables, pg_stat_user_tables, pg_statio_user_tables, pg_am, pg_extension, pg_enum, pg_stat_activity
[tool-verified: catalog.py:39-67]
pg_constraint מאוכלס בנתוני PK ו-FK אמיתיים הנגזרים מ-pk_columns ו-joins של מודל הדומיין. (REQ-392, REQ-399) כלי BI הבודקים קשרי מפתח-זר (Tableau, DBeaver וכו') יראו את גרף ה-join ש-Provisa מכירה. [tool-verified: catalog.py:551-632] joins חד-עמודתיים בין אותו זוג מקור/יעד שעמודות היעד שלהם יחד מהוות את המפתח הראשי המורכב של היעד מכווצים לשורת FK אחת עם מערכי conkey/confkey מרובי-איברים. (REQ-1094) [tool-verified: catalog_constraints.py]
pg_index מאוכלס בשורה אחת לכל אילוץ primary-key ו-UNIQUE (indrelid = oid הטבלה, indkey = attnums מפתח מסודרים, indisprimary/indisunique מוגדרים). לקוחות הפותרים עמודות מפתח דרך pg_index.indkey ולא pg_constraint — DataGrip, לדוגמה — מגלים את העמודות הנכונות דרך ה-join הסטנדרטי pg_index → pg_attribute. (REQ-1095) [tool-verified: catalog_constraints.py:340-384]
הביטויים הסקלריים הבאים גם הם מיורטים: (REQ-588)
current_user,session_user→ ה-role_idהמאומתcurrent_database()→"provisa"current_schema()→"public"version()→"PostgreSQL 14.0 on Provisa"pg_backend_pid()→0current_setting(...)→ מוחזר מטבלת הגדרות קבועהSHOW <setting>→ מוחזר מאותה טבלת הגדרות
[tool-verified: catalog.py:168-207, catalog.py:1076-1120]
קידוד פרמטרים בינארי¶
פרוטוקול השאילתה-המורחבת (Bind/Execute) תומך בפרמטרים מקודדים-בינארית. (REQ-589) OID-ים של הטיפוסים הבאים מפוענחים מבינארי: [tool-verified: postgres.py:69-97]
| OID | טיפוס PG | טיפוס Python |
|---|---|---|
| 16 | bool | bool |
| 17 | bytea | bytes |
| 20 | int8 | int |
| 21 | int2 | int |
| 23 | int4 | int |
| 25 | text | str |
| 700 | float4 | float |
| 701 | float8 | float |
| 1043 | varchar | str |
| 1082 | date | datetime.date |
| 1114 | timestamp | datetime.datetime |
| 1184 | timestamptz | datetime.datetime (UTC) |
| 1700 | numeric | decimal.Decimal |
| 2950 | uuid | str |
כל OID שאינו בטבלה זו מעלה "Unsupported binary parameter type: <oid>". (REQ-589) [tool-verified: postgres.py:579]
עמודות תוצאה נשלחות גם הן בבינארי כשהלקוח מבקש זאת, עבור אותו סט טיפוסים בתוספת ARRAY, JSON, INTERVAL, ו-BIGINT. (REQ-589) [tool-verified: postgres.py:191-244]
המלצות דרייבר¶
דרייברי Python ילידיים (psycopg2, asyncpg). אלה מנהלים משא-ומתן על פרוטוקול השאילתה-המורחבת כברירת מחדל ומשתמשים בקידוד בינארי עבור רוב הטיפוסים. נאמנות הטיפוסים הגבוהה ביותר כאן — עמודות NUMERIC מגיעות כ-Decimal, TIMESTAMP כ-datetime, וכן הלאה. השתמשו באלה עבור ETL מבוסס-Python, סקריפטים, או אינטגרציה ישירה.
JDBC (דרייבר JDBC של PostgreSQL). השתמשו בו עבור כלי אקוסיסטם-Java: DBeaver, Tableau, Power BI, Metabase, מפעילי JDBC של Airflow. JDBC ברירת מחדל לפרוטוקול השאילתה-הפשוטה, המונע סיבוכי קידוד בינארי. מחרוזת חיבור:
חלק מכלי ה-BI מבוססי-JDBC שולחים בהתחברות פרץ שאילתות information_schema ו-pg_catalog כדי לאכלס את דפדפן הסכמה שלהם. אלה כולן נענות על ידי שכבת יירוט הקטלוג — אין תעבורת Trino נוצרת במהלך בדיקת הסכמה. (REQ-532)
מתי להעדיף אחד על פני השני. אם הלקוח הוא Python, השתמשו ב-psycopg2 או asyncpg לטיפול טוב יותר בטיפוסים. אם הלקוח הוא כלי BI או כל אפליקציית JVM, השתמשו ב-JDBC. הימנעו מערבוב ציפיות פרוטוקול בינארי וטקסט באותו חיבור אם אתם רואים הפתעות המרת-טיפוסים — התנהגות מצב-הטקסט של JDBC פשוטה יותר להבנה.
אזהרות ואילוצים¶
SQL בלבד; ללא מוטציות DML. מאזין ה-pgwire מפענח ומבצע SQL בלבד — מחרוזות GraphQL ו-Cypher אינן מתקבלות. (REQ-614) INSERT, UPDATE, ו-DELETE פשוטים אינם מנותבים לנתיב כתיבה. (REQ-615) כתבו נתונים דרך COPY FROM STDIN (מקורות בני-כתיבה) או CREATE TABLE AS; מוטציות ברמת-שורה עוברות במקום זאת דרך נתיבי הכתיבה של GraphQL, Cypher, או Trino.
COPY ו-DDL דורשים את היכולת ddl. הן COPY (בכל כיוון) והן DDL מסויגים ביכולת ddl של התפקיד; תפקידים בלעדיה מקבלים SQLSTATE 42501. (REQ-616)
אין תמיכת עסקאות אמיתית. BEGIN/COMMIT/ROLLBACK מתקבלים ומתעלמים בשקט. כל משפט פועל באופן עצמאי. (REQ-587) [tool-verified: server.py:146-158 — in_transaction() מחזיר תמיד False]
timeout של 60 שניות ל-DDL, timeout של 120 שניות לשאילתה. אלה מקודדים-קשיח ב-threads המטפלים. (REQ-590) DDL ארוך-ריצה מול מקורות מרוחקים (שינויי סכמה על טבלאות גדולות) עשוי לפוג. [tool-verified: ddl_handler.py:136, server.py:186]
COPY FROM הוא מקור-בר-כתיבה-בלבד. Iceberg, Hive, מקורות Trino-בלבד, וסוגי מקור לקריאה-בלבד אינם מקבלים COPY FROM. השגיאה היא SQLSTATE 42501. (REQ-586) [tool-verified: copy_handler.py:65]
פורמט פלט COPY הוא text או csv. פורמט COPY בינארי של PG (FORMAT binary) אינו ממומש. [inferred: only text and csv branches exist in _rows_to_copy_text / _rows_to_copy_csv]
DDL בנתיב Trino הוא CREATE בלבד. ALTER, DROP, ו-CREATE INDEX מול קטלוגי Iceberg או Hive אינם נתמכים. השתמשו במקור SQL רשום כ-ddl_catalog אם אתם זקוקים ל-DDL מלא. (REQ-582) [tool-verified: ddl_handler.py:92-100]
החלפת פרמטרים היא ליטרלית. פרמטרים $1, $2, ... מוחלפים כליטרלי SQL לפני הביצוע, לא נשלחים כפרמטרי bind למנוע ה-upstream. משמעות הדבר שמנוע ה-upstream לעולם לא רואה משפט מוכן (prepared statement). עבור Trino אין לכך השפעה מעשית; עבור מקורות pool-ישיר זה עוקף מטמון prepared-statement. (REQ-581) [tool-verified: server.py:78-85]
pg_stat_activity, pg_stat_user_tables, pg_extension, pg_enum, pg_attrdef, pg_proc. טבלאות אלה קיימות בשכבת הקטלוג אך הן stubs ריקים. כלי ניטור השואלים אותן יקבלו אפס שורות במקום שגיאות. (REQ-532) [tool-verified: catalog.py:519-535, catalog.py:639-934] (pg_index מאוכלס — ראו יירוט קטלוג.)