refactor: move accessibleTables to SqlFragment

This commit is contained in:
steve-chavez
2025-09-04 09:35:33 -05:00
committed by Steve Chavez
parent 1ace298a1a
commit 9c07070263
4 changed files with 33 additions and 38 deletions
+12 -2
View File
@@ -205,8 +205,8 @@ actionQuery (MayUseDb plan@InspectPlan{ipSchema=tSchema}) AppConfig{..} _ sCache
mainActionQuery = lift $ mainActionQuery = lift $
case configOpenApiMode of case configOpenApiMode of
OAFollowPriv -> do OAFollowPriv -> do
tableAccess <- SQL.statement [tSchema] (SchemaCache.accessibleTables configDbPreparedStatements) tableAccess <- SQL.statement mempty $ SQL.dynamicallyParameterized (SqlFragment.accessibleTables tSchema) decodeAccessibleIdentifiers configDbPreparedStatements
schDesc <- SQL.statement mempty (SQL.dynamicallyParameterized (SqlFragment.schemaDescription tSchema) decodeSchemaDesc configDbPreparedStatements) schDesc <- SQL.statement mempty $ SQL.dynamicallyParameterized (SqlFragment.schemaDescription tSchema) decodeSchemaDesc configDbPreparedStatements
MaybeDbResult plan . Just <$> ((,,) MaybeDbResult plan . Just <$> ((,,)
(HM.filterWithKey (\qi _ -> S.member qi tableAccess) $ SchemaCache.dbTables sCache) (HM.filterWithKey (\qi _ -> S.member qi tableAccess) $ SchemaCache.dbTables sCache)
@@ -214,6 +214,7 @@ actionQuery (MayUseDb plan@InspectPlan{ipSchema=tSchema}) AppConfig{..} _ sCache
<*> pure schDesc) <*> pure schDesc)
OAIgnorePriv -> do OAIgnorePriv -> do
schDesc <- SQL.statement mempty (SQL.dynamicallyParameterized (SqlFragment.schemaDescription tSchema) decodeSchemaDesc configDbPreparedStatements) schDesc <- SQL.statement mempty (SQL.dynamicallyParameterized (SqlFragment.schemaDescription tSchema) decodeSchemaDesc configDbPreparedStatements)
let tbls = HM.filterWithKey (\(QualifiedIdentifier sch _) _ -> sch == tSchema) (SchemaCache.dbTables sCache) let tbls = HM.filterWithKey (\(QualifiedIdentifier sch _) _ -> sch == tSchema) (SchemaCache.dbTables sCache)
routs = HM.filterWithKey (\(QualifiedIdentifier sch _) _ -> sch == tSchema) (SchemaCache.dbRoutines sCache) routs = HM.filterWithKey (\(QualifiedIdentifier sch _) _ -> sch == tSchema) (SchemaCache.dbRoutines sCache)
@@ -224,6 +225,15 @@ actionQuery (MayUseDb plan@InspectPlan{ipSchema=tSchema}) AppConfig{..} _ sCache
decodeSchemaDesc :: HD.Result (Maybe Text) decodeSchemaDesc :: HD.Result (Maybe Text)
decodeSchemaDesc = join <$> HD.rowMaybe (nullableColumn HD.text) decodeSchemaDesc = join <$> HD.rowMaybe (nullableColumn HD.text)
decodeAccessibleIdentifiers :: HD.Result (S.Set QualifiedIdentifier)
decodeAccessibleIdentifiers =
let
row = QualifiedIdentifier
<$> column HD.text
<*> column HD.text
in
S.fromList <$> HD.rowList row
-- Makes sure the querystring pk matches the payload pk -- Makes sure the querystring pk matches the payload pk
-- e.g. PUT /items?id=eq.1 { "id" : 1, .. } is accepted, -- e.g. PUT /items?id=eq.1 { "id" : 1, .. } is accepted,
-- PUT /items?id=eq.14 { "id" : 2, .. } is rejected. -- PUT /items?id=eq.14 { "id" : 2, .. } is rejected.
+20
View File
@@ -41,6 +41,7 @@ module PostgREST.Query.SqlFragment
, escapeIdent , escapeIdent
, escapeIdentList , escapeIdentList
, schemaDescription , schemaDescription
, accessibleTables
) where ) where
import qualified Data.Aeson as JSON import qualified Data.Aeson as JSON
@@ -597,3 +598,22 @@ schemaDescription schema =
"SELECT pg_catalog.obj_description(" <> encoded <> "::regnamespace, 'pg_namespace')" "SELECT pg_catalog.obj_description(" <> encoded <> "::regnamespace, 'pg_namespace')"
where where
encoded = SQL.encoderAndParam (HE.nonNullable HE.unknown) $ encodeUtf8 schema encoded = SQL.encoderAndParam (HE.nonNullable HE.unknown) $ encodeUtf8 schema
accessibleTables :: Text -> SQL.Snippet
accessibleTables schema = SQL.sql (encodeUtf8 [trimming|
SELECT
n.nspname AS table_schema,
c.relname AS table_name
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('v','r','m','f','p')
AND c.relnamespace = |]) <> encodedSchema <> "::regnamespace " <> SQL.sql (encodeUtf8 [trimming|
AND (
pg_has_role(c.relowner, 'USAGE')
or has_table_privilege(c.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER')
or has_any_column_privilege(c.oid, 'SELECT, INSERT, UPDATE, REFERENCES')
)
AND not c.relispartition
ORDER BY table_schema, table_name|])
where
encodedSchema = SQL.encoderAndParam (HE.nonNullable HE.text) schema
+1 -32
View File
@@ -21,7 +21,6 @@ These queries are executed once at startup or when PostgREST is reloaded.
module PostgREST.SchemaCache module PostgREST.SchemaCache
( SchemaCache(..) ( SchemaCache(..)
, querySchemaCache , querySchemaCache
, accessibleTables
, accessibleFuncs , accessibleFuncs
, showSummary , showSummary
) where ) where
@@ -46,7 +45,7 @@ import PostgREST.Config (AppConfig (..))
import PostgREST.Config.Database (TimezoneNames, import PostgREST.Config.Database (TimezoneNames,
toIsolationLevel) toIsolationLevel)
import PostgREST.Query.SqlFragment (escapeIdent) import PostgREST.Query.SqlFragment (escapeIdent)
import PostgREST.SchemaCache.Identifiers (AccessSet, FieldName, import PostgREST.SchemaCache.Identifiers (FieldName,
QualifiedIdentifier (..), QualifiedIdentifier (..),
RelIdentifier (..), RelIdentifier (..),
Schema, isAnyElement) Schema, isAnyElement)
@@ -208,14 +207,6 @@ removeInternal schemas dbStruct =
M2M Junction{junTable} -> qiSchema junTable `notElem` schemas M2M Junction{junTable} -> qiSchema junTable `notElem` schemas
_ -> False _ -> False
decodeAccessibleIdentifiers :: HD.Result AccessSet
decodeAccessibleIdentifiers =
S.fromList <$> HD.rowList row
where
row = QualifiedIdentifier
<$> column HD.text
<*> column HD.text
decodeTables :: HD.Result TablesMap decodeTables :: HD.Result TablesMap
decodeTables = decodeTables =
HM.fromList . map (\tbl@Table{tableSchema, tableName} -> (QualifiedIdentifier tableSchema tableName, tbl)) <$> HD.rowList tblRow HM.fromList . map (\tbl@Table{tableSchema, tableName} -> (QualifiedIdentifier tableSchema tableName, tbl)) <$> HD.rowList tblRow
@@ -473,28 +464,6 @@ funcsSqlQuery = encodeUtf8 [trimming|
WHERE t.oid <> 'trigger'::regtype AND COALESCE(a.callable, true) WHERE t.oid <> 'trigger'::regtype AND COALESCE(a.callable, true)
AND prokind = 'f' AND prokind = 'f'
AND p.pronamespace = ANY($$1::regnamespace[]) |] AND p.pronamespace = ANY($$1::regnamespace[]) |]
accessibleTables :: Bool -> SQL.Statement [Schema] AccessSet
accessibleTables =
SQL.Statement sql params decodeAccessibleIdentifiers
where
params = map escapeIdent >$< arrayParam HE.text
sql = encodeUtf8 [trimming|
SELECT
n.nspname AS table_schema,
c.relname AS table_name
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('v','r','m','f','p')
AND c.relnamespace = ANY($$1::regnamespace[])
AND (
pg_has_role(c.relowner, 'USAGE')
or has_table_privilege(c.oid, 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER')
or has_any_column_privilege(c.oid, 'SELECT, INSERT, UPDATE, REFERENCES')
)
AND not c.relispartition
ORDER BY table_schema, table_name|]
{- {-
Adds M2O and O2O relationships for views to tables, tables to views, and views to views. The example below is taken from the test fixtures, but the views names/colnames were modified. Adds M2O and O2O relationships for views to tables, tables to views, and views to views. The example below is taken from the test fixtures, but the views names/colnames were modified.
-4
View File
@@ -8,13 +8,11 @@ module PostgREST.SchemaCache.Identifiers
, Schema , Schema
, TableName , TableName
, FieldName , FieldName
, AccessSet
, dumpQi , dumpQi
, toQi , toQi
) where ) where
import qualified Data.Aeson as JSON import qualified Data.Aeson as JSON
import qualified Data.Set as S
import qualified Data.Text as T import qualified Data.Text as T
import Protolude import Protolude
@@ -51,5 +49,3 @@ toQi txt = case T.drop 1 <$> T.breakOn "." txt of
type Schema = Text type Schema = Text
type TableName = Text type TableName = Text
type FieldName = Text type FieldName = Text
type AccessSet = S.Set QualifiedIdentifier