diff --git a/server/src-exec/Migrate.hs b/server/src-exec/Migrate.hs index 05da6a6558004..765f699e42bfb 100644 --- a/server/src-exec/Migrate.hs +++ b/server/src-exec/Migrate.hs @@ -19,7 +19,7 @@ import qualified Data.Yaml.TH as Y import qualified Database.PG.Query as Q curCatalogVer :: T.Text -curCatalogVer = "22" +curCatalogVer = "23" migrateMetadata :: ( MonadTx m @@ -360,6 +360,12 @@ from21To22 = do $(Q.sqlFromFile "src-rsr/migrate_from_21_to_22.sql") pure () +from22To23 :: (MonadTx m) => m () +from22To23 = do + Q.Discard () <- liftTx $ Q.multiQE defaultTxErrorHandler + $(Q.sqlFromFile "src-rsr/migrate_from_22_to_23.sql") + pure () + migrateCatalog :: ( MonadTx m , CacheRWM m @@ -403,6 +409,7 @@ migrateCatalog migrationTime = migrateFrom =<< getCatalogVersion , ("19", from19To20) , ("20", from20To21) , ("21", from21To22) + , ("22", from22To23) ] postMigrate = do diff --git a/server/src-lib/Hasura/GraphQL/Schema.hs b/server/src-lib/Hasura/GraphQL/Schema.hs index ca23bb21118b0..33c36716a1d93 100644 --- a/server/src-lib/Hasura/GraphQL/Schema.hs +++ b/server/src-lib/Hasura/GraphQL/Schema.hs @@ -70,7 +70,7 @@ isValidRel rn rt = isValidName (mkRelName rn) && isValidObjectName rt isValidField :: FieldInfo PGColumnInfo -> Bool isValidField = \case - FIColumn (PGColumnInfo col _ _) -> isValidCol col + FIColumn colInfo -> isValidCol $ pgiName colInfo FIRelationship (RelInfo rn _ _ remTab _) -> isValidRel rn remTab upsertable :: [ConstraintName] -> Bool -> Bool -> Bool @@ -114,8 +114,11 @@ isAggFld = flip elem (numAggOps <> compAggOps) mkGCtxRole' :: QualifiedTable + -- Postgres description + -> Maybe PGDescription + -- insert permission -> Maybe ([PGColumnInfo], RelationInfoMap) - -- ^ insert permission + -- select permission -> Maybe (Bool, [SelField]) -- ^ select permission -> Maybe [PGColumnInfo] @@ -132,7 +135,7 @@ mkGCtxRole' -> Maybe EnumValues -- ^ present iff this table is an enum table (see "Hasura.RQL.Schema.Enum") -> TyAgg -mkGCtxRole' tn insPermM selPermM updColsM +mkGCtxRole' tn descM insPermM selPermM updColsM delPermM pkeyCols constraints viM funcs enumValuesM = TyAgg (mkTyInfoMap allTypes) fieldMap scalars ordByCtx @@ -244,7 +247,7 @@ mkGCtxRole' tn insPermM selPermM updColsM && any (`isMutable` viM) [viIsInsertable, viIsUpdatable, viIsDeletable] -- table obj - selObjM = mkTableObj tn <$> selFldsM + selObjM = mkTableObj tn descM <$> selFldsM -- aggregate objs and order by inputs (aggObjs, aggOrdByInps) = case selPermM of @@ -372,7 +375,7 @@ getRootFldsRole' tn primCols constraints fields funcs insM selM updM delM viM = funcFldHelper f g pFltr pLimit hdrs = flip map funcs $ \fi -> ( f . FuncQOpCtx tn hdrs pFltr pLimit (fiName fi) $ mkFuncArgItemSeq fi - , g fi + , g fi $ fiDescription fi ) mkFuncArgItemSeq fi = Seq.fromList $ procFuncArgs (fiInputArgs fi) @@ -470,6 +473,7 @@ mkGCtxRole :: (MonadError QErr m) => TableCache PGColumnInfo -> QualifiedTable + -> Maybe PGDescription -> FieldInfoMap PGColumnInfo -> [PGCol] -> [ConstraintName] @@ -479,7 +483,7 @@ mkGCtxRole -> RoleName -> RolePermInfo -> m (TyAgg, RootFields, InsCtxMap) -mkGCtxRole tableCache tn fields pCols constraints funcs viM enumValuesM role permInfo = do +mkGCtxRole tableCache tn descM fields pCols constraints funcs viM enumValuesM role permInfo = do selPermM <- mapM (getSelPerm tableCache fields role) $ _permSel permInfo tabInsInfoM <- forM (_permIns permInfo) $ \ipi -> do ctx <- mkInsCtx role tableCache fields ipi $ _permUpd permInfo @@ -489,7 +493,7 @@ mkGCtxRole tableCache tn fields pCols constraints funcs viM enumValuesM role per let insPermM = snd <$> tabInsInfoM insCtxM = fst <$> tabInsInfoM updColsM = filterColInfos . upiCols <$> _permUpd permInfo - tyAgg = mkGCtxRole' tn insPermM selPermM updColsM + tyAgg = mkGCtxRole' tn descM insPermM selPermM updColsM (void $ _permDel permInfo) pColInfos constraints viM funcs enumValuesM rootFlds = getRootFldsRole tn pCols constraints fields funcs viM permInfo insCtxMap = maybe Map.empty (Map.singleton tn) insCtxM @@ -535,16 +539,16 @@ mkGCtxMapTable -> m (Map.HashMap RoleName (TyAgg, RootFields, InsCtxMap)) mkGCtxMapTable tableCache funcCache tabInfo = do m <- Map.traverseWithKey - (mkGCtxRole tableCache tn fields pkeyCols validConstraints tabFuncs viewInfo enumValues) + (mkGCtxRole tableCache tn descM fields pkeyCols validConstraints tabFuncs viewInfo enumValues) rolePerms adminInsCtx <- mkAdminInsCtx tn tableCache fields - let adminCtx = mkGCtxRole' tn (Just (colInfos, icRelations adminInsCtx)) + let adminCtx = mkGCtxRole' tn descM (Just (colInfos, icRelations adminInsCtx)) (Just (True, selFlds)) (Just colInfos) (Just ()) pkeyColInfos validConstraints viewInfo tabFuncs enumValues adminInsCtxMap = Map.singleton tn adminInsCtx return $ Map.insert adminRole (adminCtx, adminRootFlds, adminInsCtxMap) m where - TableInfo tn _ fields rolePerms constraints pkeyCols viewInfo _ enumValues = tabInfo + TableInfo tn descM _ fields rolePerms constraints pkeyCols viewInfo _ enumValues = tabInfo validConstraints = mkValidConstraints constraints colInfos = getValidCols fields validColNames = map pgiName colInfos diff --git a/server/src-lib/Hasura/GraphQL/Schema/BoolExp.hs b/server/src-lib/Hasura/GraphQL/Schema/BoolExp.hs index f9ab2a937cc17..d35e93f4b7329 100644 --- a/server/src-lib/Hasura/GraphQL/Schema/BoolExp.hs +++ b/server/src-lib/Hasura/GraphQL/Schema/BoolExp.hs @@ -283,7 +283,7 @@ mkBoolExpInp tn fields = ] mkFldExpInp = \case - Left (PGColumnInfo colName colTy _) -> + Left (PGColumnInfo colName colTy _ _) -> mk (mkColName colName) (mkCompExpTy colTy) Right (RelInfo relName _ _ remTab _, _, _, _, _) -> mk (mkRelName relName) (mkBoolExpTy remTab) diff --git a/server/src-lib/Hasura/GraphQL/Schema/Common.hs b/server/src-lib/Hasura/GraphQL/Schema/Common.hs index fda7b9326c45e..41bbddbdb1ece 100644 --- a/server/src-lib/Hasura/GraphQL/Schema/Common.hs +++ b/server/src-lib/Hasura/GraphQL/Schema/Common.hs @@ -15,9 +15,12 @@ module Hasura.GraphQL.Schema.Common , mkTableAggTy , mkColumnEnumVal + , mkDescriptionWith + , mkDescription ) where import qualified Data.HashMap.Strict as Map +import qualified Data.Text as T import qualified Language.GraphQL.Draft.Syntax as G import Hasura.GraphQL.Validate.Types @@ -63,3 +66,11 @@ mkTableAggTy = addTypeSuffix "_aggregate" . mkTableTy mkColumnEnumVal :: PGCol -> EnumValInfo mkColumnEnumVal (PGCol col) = EnumValInfo (Just "column name") (G.EnumValue $ G.Name col) False + +mkDescriptionWith :: Maybe PGDescription -> Text -> G.Description +mkDescriptionWith descM defaultTxt = G.Description $ case descM of + Nothing -> defaultTxt + Just (PGDescription descTxt) -> T.unlines [descTxt, "\n", defaultTxt] + +mkDescription :: PGDescription -> G.Description +mkDescription = G.Description . getPGDescription diff --git a/server/src-lib/Hasura/GraphQL/Schema/Function.hs b/server/src-lib/Hasura/GraphQL/Schema/Function.hs index 395b459e26394..2cf28ed670e22 100644 --- a/server/src-lib/Hasura/GraphQL/Schema/Function.hs +++ b/server/src-lib/Hasura/GraphQL/Schema/Function.hs @@ -91,14 +91,14 @@ mkFuncArgs funInfo = funcInpArgs = bool [funcInpArg] [] $ null funcArgs mkFuncQueryFld - :: FunctionInfo -> ObjFldInfo -mkFuncQueryFld funInfo = + :: FunctionInfo -> Maybe PGDescription -> ObjFldInfo +mkFuncQueryFld funInfo descM = mkHsraObjFldInfo (Just desc) fldName (mkFuncArgs funInfo) ty where retTable = fiReturnType funInfo funcName = fiName funInfo - desc = G.Description $ "execute function " <> funcName + desc = mkDescriptionWith descM $ "execute function " <> funcName <<> " which returns " <>> retTable fldName = qualObjectToName funcName @@ -116,14 +116,14 @@ function_aggregate( -} mkFuncAggQueryFld - :: FunctionInfo -> ObjFldInfo -mkFuncAggQueryFld funInfo = + :: FunctionInfo -> Maybe PGDescription -> ObjFldInfo +mkFuncAggQueryFld funInfo descM = mkHsraObjFldInfo (Just desc) fldName (mkFuncArgs funInfo) ty where funcName = fiName funInfo retTable = fiReturnType funInfo - desc = G.Description $ "execute function " <> funcName + desc = mkDescriptionWith descM $ "execute function " <> funcName <<> " and query aggregates on result of table type " <>> retTable diff --git a/server/src-lib/Hasura/GraphQL/Schema/Mutation/Common.hs b/server/src-lib/Hasura/GraphQL/Schema/Mutation/Common.hs index 87db87d213699..e2fa5a905d813 100644 --- a/server/src-lib/Hasura/GraphQL/Schema/Mutation/Common.hs +++ b/server/src-lib/Hasura/GraphQL/Schema/Mutation/Common.hs @@ -15,8 +15,8 @@ import Hasura.RQL.Types import Hasura.SQL.Types mkPGColInp :: PGColumnInfo -> InpValInfo -mkPGColInp (PGColumnInfo colName colTy _) = - InpValInfo Nothing (G.Name $ getPGColTxt colName) Nothing $ +mkPGColInp (PGColumnInfo colName colTy _ descM) = + InpValInfo (mkDescription <$> descM) (G.Name $ getPGColTxt colName) Nothing $ G.toGT $ mkColumnType colTy -- table_mutation_response diff --git a/server/src-lib/Hasura/GraphQL/Schema/Select.hs b/server/src-lib/Hasura/GraphQL/Schema/Select.hs index e5718d0b8d232..7173f1aef47fc 100644 --- a/server/src-lib/Hasura/GraphQL/Schema/Select.hs +++ b/server/src-lib/Hasura/GraphQL/Schema/Select.hs @@ -16,8 +16,8 @@ import qualified Data.HashMap.Strict as Map import qualified Data.HashSet as Set import qualified Language.GraphQL.Draft.Syntax as G -import Hasura.GraphQL.Schema.Common import Hasura.GraphQL.Schema.BoolExp +import Hasura.GraphQL.Schema.Common import Hasura.GraphQL.Schema.OrderBy import Hasura.GraphQL.Validate.Types import Hasura.Prelude @@ -58,9 +58,10 @@ mkPGColParams colType | otherwise = Map.empty mkPGColFld :: PGColumnInfo -> ObjFldInfo -mkPGColFld (PGColumnInfo colName colTy isNullable) = - mkHsraObjFldInfo Nothing n (mkPGColParams colTy) ty +mkPGColFld (PGColumnInfo colName colTy isNullable pgDesc) = + mkHsraObjFldInfo desc n (mkPGColParams colTy) ty where + desc = (G.Description . getPGDescription) <$> pgDesc n = G.Name $ getPGColTxt colName ty = bool notNullTy nullTy isNullable columnType = mkColumnType colTy @@ -136,15 +137,16 @@ type table { -} mkTableObj :: QualifiedTable + -> Maybe PGDescription -> [SelField] -> ObjTyInfo -mkTableObj tn allowedFlds = +mkTableObj tn descM allowedFlds = mkObjTyInfo (Just desc) (mkTableTy tn) Set.empty (mapFromL _fiName flds) TLHasuraType where flds = concatMap (either (pure . mkPGColFld) mkRelFld') allowedFlds mkRelFld' (relInfo, allowAgg, _, _, isNullable) = mkRelFld allowAgg relInfo isNullable - desc = G.Description $ "columns and relationships of " <>> tn + desc = mkDescriptionWith descM $ "columns and relationships of " <>> tn {- type table_aggregate { @@ -268,8 +270,9 @@ mkSelFldPKey tn cols = fldName = mkTableByPkName tn args = fromInpValL $ map colInpVal cols ty = G.toGT $ mkTableTy tn - colInpVal (PGColumnInfo n typ _) = - InpValInfo Nothing (mkColName n) Nothing $ G.toGT $ G.toNT $ mkColumnType typ + colInpVal (PGColumnInfo n typ _ descM) = + InpValInfo (mkDescription <$> descM) + (mkColName n) Nothing $ G.toGT $ G.toNT $ mkColumnType typ {- diff --git a/server/src-lib/Hasura/RQL/DDL/Schema.hs b/server/src-lib/Hasura/RQL/DDL/Schema.hs index aeefb130944af..980392a2fe0bb 100644 --- a/server/src-lib/Hasura/RQL/DDL/Schema.hs +++ b/server/src-lib/Hasura/RQL/DDL/Schema.hs @@ -82,7 +82,7 @@ runRunSQL (RunSQL t cascade mChkMDCnstcy) = do isAltrDropReplace = either throwErr return . matchRegex regex False where throwErr s = throw500 $ "compiling regex failed: " <> T.pack s - regex = "alter|drop|replace|create function" + regex = "alter|drop|replace|create function|comment on" data RunSQLRes = RunSQLRes diff --git a/server/src-lib/Hasura/RQL/DDL/Schema/Cache.hs b/server/src-lib/Hasura/RQL/DDL/Schema/Cache.hs index b69a2f517674f..76d30d3bd20a8 100644 --- a/server/src-lib/Hasura/RQL/DDL/Schema/Cache.hs +++ b/server/src-lib/Hasura/RQL/DDL/Schema/Cache.hs @@ -256,11 +256,13 @@ withMetadataCheck cascade action = do delFunctionFromCache qf -- Process altered functions - forM_ alteredFuncs $ \(qf, newTy) -> + forM_ alteredFuncs $ \(qf, newTy, newDescM) -> do when (newTy == FTVOLATILE) $ throw400 NotSupported $ "type of function " <> qf <<> " is altered to \"VOLATILE\" which is not supported now" + updateFunctionDescription qf newDescM + -- update the schema cache and hdb_catalog with the changes reloadRequired <- processSchemaChanges schemaDiff diff --git a/server/src-lib/Hasura/RQL/DDL/Schema/Diff.hs b/server/src-lib/Hasura/RQL/DDL/Schema/Diff.hs index e16d6414965e3..d229abdad1161 100644 --- a/server/src-lib/Hasura/RQL/DDL/Schema/Diff.hs +++ b/server/src-lib/Hasura/RQL/DDL/Schema/Diff.hs @@ -42,6 +42,7 @@ data PGColMeta , pcmDataType :: !PGScalarType , pcmIsNullable :: !Bool , pcmReferences :: ![QualifiedTable] + , pcmDescription :: !(Maybe PGDescription) } deriving (Show, Eq) $(deriveJSON (aesonDrop 3 snakeCase){omitNothingFields=True} ''PGColMeta) @@ -59,6 +60,7 @@ data TableMeta = TableMeta { tmOid :: !Int , tmTable :: !QualifiedTable + , tmDescription :: !(Maybe PGDescription) , tmColumns :: ![PGColMeta] , tmConstraints :: ![ConstraintMeta] , tmForeignKeys :: ![ForeignKey] @@ -67,8 +69,8 @@ data TableMeta fetchTableMeta :: Q.Tx [TableMeta] fetchTableMeta = do res <- Q.listQ $(Q.sqlFromFile "src-rsr/table_meta.sql") () False - forM res $ \(ts, tn, toid, cols, constrnts, fkeys) -> - return $ TableMeta toid (QualifiedObject ts tn) (Q.getAltJ cols) + forM res $ \(ts, tn, toid, descM, cols, constrnts, fkeys) -> + return $ TableMeta toid (QualifiedObject ts tn) descM (Q.getAltJ cols) (Q.getAltJ constrnts) (Q.getAltJ fkeys) getOverlap :: (Eq k, Hashable k) => (v -> k) -> [v] -> [v] -> [(v, v)] @@ -94,12 +96,13 @@ data TableDiff -- used for generating types on_conflict clauses -- TODO: this ideally should't be part of TableDiff , _tdUniqOrPriCons :: ![ConstraintName] + , _tdNewDescription :: !(Maybe PGDescription) } deriving (Show, Eq) getTableDiff :: TableMeta -> TableMeta -> TableDiff getTableDiff oldtm newtm = TableDiff mNewName droppedCols addedCols alteredCols - droppedFKeyConstraints uniqueOrPrimaryCons + droppedFKeyConstraints uniqueOrPrimaryCons mNewDesc where mNewName = bool (Just $ tmTable newtm) Nothing $ tmTable oldtm == tmTable newtm oldCols = tmColumns oldtm @@ -108,6 +111,8 @@ getTableDiff oldtm newtm = uniqueOrPrimaryCons = [cmName cm | cm <- tmConstraints newtm, isUniqueOrPrimary (cmType cm)] + mNewDesc = tmDescription newtm + droppedCols = map pcmColumnName $ getDifference pcmOrdinalPosition oldCols newCols @@ -116,8 +121,8 @@ getTableDiff oldtm newtm = existingCols = getOverlap pcmOrdinalPosition oldCols newCols - pcmToPci (PGColMeta colName _ colType isNullable references) - = PGRawColumnInfo colName colType isNullable references + pcmToPci (PGColMeta colName _ colType isNullable references descM) + = PGRawColumnInfo colName colType isNullable references descM alteredCols = flip map (filter (uncurry (/=)) existingCols) $ pcmToPci *** pcmToPci @@ -151,7 +156,7 @@ getTableChangeDeps ti tableDiff = do return $ droppedConsDeps <> droppedColDeps where tn = _tiName ti - TableDiff _ droppedCols _ _ droppedFKeyConstraints _ = tableDiff + TableDiff _ droppedCols _ _ droppedFKeyConstraints _ _ = tableDiff data SchemaDiff = SchemaDiff @@ -192,10 +197,11 @@ getSchemaChangeDeps schemaDiff = do data FunctionMeta = FunctionMeta - { fmOid :: !Int - , fmSchema :: !SchemaName - , fmName :: !FunctionName - , fmType :: !FunctionType + { fmOid :: !Int + , fmSchema :: !SchemaName + , fmName :: !FunctionName + , fmType :: !FunctionType + , fmDescription :: !(Maybe PGDescription) } deriving (Show, Eq) $(deriveJSON (aesonDrop 2 snakeCase) ''FunctionMeta) @@ -210,7 +216,8 @@ fetchFunctionMeta = 'oid', p.oid :: integer, 'schema', f.function_schema, 'name', f.function_name, - 'type', f.function_type + 'type', f.function_type, + 'description', f.description ) AS function_meta FROM hdb_catalog.hdb_function_agg f @@ -221,13 +228,13 @@ fetchFunctionMeta = ) WHERE f.function_schema <> 'hdb_catalog' - GROUP BY p.oid, f.function_schema, f.function_name, f.function_type + GROUP BY p.oid, f.function_schema, f.function_name, f.function_type, f.description |] () False data FunctionDiff = FunctionDiff { fdDropped :: ![QualifiedFunction] - , fdAltered :: ![(QualifiedFunction, FunctionType)] + , fdAltered :: ![(QualifiedFunction, FunctionType, Maybe PGDescription)] } deriving (Show, Eq) getFuncDiff :: [FunctionMeta] -> [FunctionMeta] -> FunctionDiff @@ -238,8 +245,9 @@ getFuncDiff oldMeta newMeta = alteredFuncs = mapMaybe mkAltered $ getOverlap fmOid oldMeta newMeta mkAltered (oldfm, newfm) = let isTypeAltered = fmType oldfm /= fmType newfm - alteredFunc = (funcFromMeta oldfm, fmType newfm) - in bool Nothing (Just alteredFunc) isTypeAltered + isDescriptionAltered = fmDescription oldfm /= fmDescription newfm + alteredFunc = (funcFromMeta oldfm, fmType newfm, fmDescription newfm) + in bool Nothing (Just alteredFunc) $ isTypeAltered || isDescriptionAltered getOverloadedFuncs :: [QualifiedFunction] -> [FunctionMeta] -> [QualifiedFunction] diff --git a/server/src-lib/Hasura/RQL/DDL/Schema/Function.hs b/server/src-lib/Hasura/RQL/DDL/Schema/Function.hs index 60f97069b0eee..343f2e0562df8 100644 --- a/server/src-lib/Hasura/RQL/DDL/Schema/Function.hs +++ b/server/src-lib/Hasura/RQL/DDL/Schema/Function.hs @@ -30,7 +30,7 @@ data PGTypType | PTDOMAIN | PTENUM | PTRANGE - | PTPSUEDO + | PTPSEUDO deriving (Show, Eq) $(deriveJSON defaultOptions{constructorTagModifier = drop 2} ''PGTypType) @@ -46,6 +46,7 @@ data RawFuncInfo , rfiInputArgNames :: ![T.Text] , rfiDefaultArgs :: !Int , rfiReturnsTable :: !Bool + , rfiDescription :: !(Maybe PGDescription) } deriving (Show, Eq) $(deriveJSON (aesonDrop 3 snakeCase) ''RawFuncInfo) @@ -95,10 +96,10 @@ mkFunctionInfo qf rawFuncInfo = do let funcArgsSeq = Seq.fromList funcArgs dep = SchemaDependency (SOTable retTable) DRTable retTable = QualifiedObject retSn (TableName retN) - return $ FunctionInfo qf False funTy funcArgsSeq retTable [dep] + return $ FunctionInfo qf False funTy funcArgsSeq retTable [dep] descM where - RawFuncInfo hasVariadic funTy retSn retN retTyTyp - retSet inpArgTyps inpArgNames defArgsNo returnsTab + RawFuncInfo hasVariadic funTy retSn retN retTyTyp retSet + inpArgTyps inpArgNames defArgsNo returnsTab descM = rawFuncInfo saveFunctionToCatalog :: QualifiedFunction -> Bool -> Q.TxE QErr () diff --git a/server/src-lib/Hasura/RQL/DDL/Schema/Table.hs b/server/src-lib/Hasura/RQL/DDL/Schema/Table.hs index 6a1fc9e23bb63..3d07037201b7c 100644 --- a/server/src-lib/Hasura/RQL/DDL/Schema/Table.hs +++ b/server/src-lib/Hasura/RQL/DDL/Schema/Table.hs @@ -171,6 +171,9 @@ processTableChanges ti tableDiff = do let tn = _tiName ti withOldTabName = do replaceConstraints tn + -- replace description + replaceDescription tn + -- for all the dropped columns procDroppedCols tn procAddedCols tn procAlteredCols sc tn @@ -188,10 +191,13 @@ processTableChanges ti tableDiff = do maybe withOldTabName withNewTabName mNewName where - TableDiff mNewName droppedCols addedCols alteredCols _ constraints = tableDiff + TableDiff mNewName droppedCols addedCols alteredCols _ constraints descM = tableDiff replaceConstraints tn = flip modTableInCache tn $ \tInfo -> return $ tInfo {_tiUniqOrPrimConstraints = constraints} + replaceDescription tn = flip modTableInCache tn $ \tInfo -> + return $ tInfo {_tiDescription = descM} + procDroppedCols tn = forM_ droppedCols $ \droppedCol -> -- Drop the column from the cache @@ -199,7 +205,8 @@ processTableChanges ti tableDiff = do procAddedCols tn = -- In the newly added columns check that there is no conflict with relationships - forM_ addedCols $ \rawInfo@(PGRawColumnInfo colName _ _ _) -> + forM_ addedCols $ \rawInfo -> do + let colName = prciName rawInfo case M.lookup (fromPGCol colName) $ _tiFieldInfoMap ti of Just (FIRelationship _) -> throw400 AlreadyExists $ "cannot add column " <> colName @@ -210,8 +217,8 @@ processTableChanges ti tableDiff = do addColToCache colName info tn procAlteredCols sc tn = fmap or $ forM alteredCols $ - \( PGRawColumnInfo oldName oldType _ _ - , newRawInfo@(PGRawColumnInfo newName newType _ _) ) -> do + \( PGRawColumnInfo oldName oldType _ _ _ + , newRawInfo@(PGRawColumnInfo newName newType _ _ _) ) -> do let performColumnUpdate = do newInfo <- processColumnInfoUsingCache tn newRawInfo updColInCache newName newInfo tn @@ -276,7 +283,7 @@ buildTableCache = processTableCache <=< buildRawTableCache catalogInfo <- onNothing maybeInfo $ throw400 NotExists $ "no such table/view exists in postgres: " <>> name - let CatalogTableInfo columns constraints primaryKeyColumnNames viewInfo = catalogInfo + let CatalogTableInfo columns constraints primaryKeyColumnNames viewInfo maybeDesc = catalogInfo columnFields = M.fromList . flip map columns $ \column -> (fromPGCol $ prciName column, FIColumn column) @@ -295,7 +302,9 @@ buildTableCache = processTableCache <=< buildRawTableCache , _tiPrimaryKeyCols = primaryKeyColumnNames , _tiViewInfo = viewInfo , _tiEventTriggerInfoMap = mempty - , _tiEnumValues = maybeEnumValues } + , _tiEnumValues = maybeEnumValues + , _tiDescription = maybeDesc + } pure (name, info) -- Step 2: Process the raw table cache to replace Postgres column types with logical column @@ -321,7 +330,9 @@ processColumnInfo enumTables tableName rawInfo = do pure PGColumnInfo { pgiName = prciName rawInfo , pgiType = resolvedType - , pgiIsNullable = prciIsNullable rawInfo } + , pgiIsNullable = prciIsNullable rawInfo + , pgiDescription = prciDescription rawInfo + } where resolveColumnType = case prciReferences rawInfo of diff --git a/server/src-lib/Hasura/RQL/DML/Internal.hs b/server/src-lib/Hasura/RQL/DML/Internal.hs index 4c9b6660256e5..21603796759d6 100644 --- a/server/src-lib/Hasura/RQL/DML/Internal.hs +++ b/server/src-lib/Hasura/RQL/DML/Internal.hs @@ -188,7 +188,8 @@ checkOnColExp -> AnnBoolExpFldSQL -> m AnnBoolExpFldSQL checkOnColExp spi sessVarBldr annFld = case annFld of - AVCol (PGColumnInfo cn _ _) _ -> do + AVCol colInfo _ -> do + let cn = pgiName colInfo checkSelOnCol spi cn return annFld AVRel relInfo nesAnn -> do diff --git a/server/src-lib/Hasura/RQL/DML/Returning.hs b/server/src-lib/Hasura/RQL/DML/Returning.hs index 9ae1b2567c3ff..e2c80e6381cb5 100644 --- a/server/src-lib/Hasura/RQL/DML/Returning.hs +++ b/server/src-lib/Hasura/RQL/DML/Returning.hs @@ -56,8 +56,8 @@ pgColsFromMutFld = \case MExp _ -> [] MRet selFlds -> flip mapMaybe selFlds $ \(_, annFld) -> case annFld of - FCol (PGColumnInfo col colTy _) _ -> Just (col, colTy) - _ -> Nothing + FCol (PGColumnInfo col colTy _ _) _ -> Just (col, colTy) + _ -> Nothing pgColsFromMutFlds :: MutFlds -> [(PGCol, PGColumnType)] pgColsFromMutFlds = concatMap (pgColsFromMutFld . snd) diff --git a/server/src-lib/Hasura/RQL/GBoolExp.hs b/server/src-lib/Hasura/RQL/GBoolExp.hs index 7bb62fc910d6f..b7c081e82630c 100644 --- a/server/src-lib/Hasura/RQL/GBoolExp.hs +++ b/server/src-lib/Hasura/RQL/GBoolExp.hs @@ -302,7 +302,7 @@ annColExp annColExp rhsParser colInfoMap (ColExp fieldName colVal) = do colInfo <- askFieldInfo colInfoMap fieldName case colInfo of - FIColumn (PGColumnInfo _ (PGColumnScalar PGJSON) _) -> + FIColumn (PGColumnInfo _ (PGColumnScalar PGJSON) _ _) -> throwError (err400 UnexpectedPayload "JSON column can not be part of where clause") FIColumn pgi -> AVCol pgi <$> parseOperationsExpression rhsParser colInfoMap pgi colVal @@ -326,8 +326,9 @@ convBoolRhs' tq = convColRhs :: S.Qual -> AnnBoolExpFldSQL -> State Word64 S.BoolExp convColRhs tableQual = \case - AVCol (PGColumnInfo cn _ _) opExps -> do - let bExps = map (mkColCompExp tableQual cn) opExps + AVCol colInfo opExps -> do + let cn = pgiName colInfo + bExps = map (mkColCompExp tableQual cn) opExps return $ foldr (S.BEBin S.AndOp) (S.BELit True) bExps AVRel (RelInfo _ _ colMapping relTN _) nesAnn -> do diff --git a/server/src-lib/Hasura/RQL/Types/Catalog.hs b/server/src-lib/Hasura/RQL/Types/Catalog.hs index 3c95ff66d5392..6c5d1238402c3 100644 --- a/server/src-lib/Hasura/RQL/Types/Catalog.hs +++ b/server/src-lib/Hasura/RQL/Types/Catalog.hs @@ -34,6 +34,7 @@ data CatalogTableInfo , _ctiConstraints :: ![ConstraintName] , _ctiPrimaryKeyColumns :: ![PGCol] , _ctiViewInfo :: !(Maybe ViewInfo) + , _ctiDescription :: !(Maybe PGDescription) } deriving (Show, Eq) $(deriveJSON (aesonDrop 4 snakeCase) ''CatalogTableInfo) diff --git a/server/src-lib/Hasura/RQL/Types/Column.hs b/server/src-lib/Hasura/RQL/Types/Column.hs index 908ca58c99796..baf84cdf66ee6 100644 --- a/server/src-lib/Hasura/RQL/Types/Column.hs +++ b/server/src-lib/Hasura/RQL/Types/Column.hs @@ -120,12 +120,13 @@ parsePGScalarValues columnType values = do -- 'pcirReferences' field and other table data to eventually resolve the type to a 'PGColumnType'. data PGRawColumnInfo = PGRawColumnInfo - { prciName :: !PGCol - , prciType :: !PGScalarType - , prciIsNullable :: !Bool - , prciReferences :: ![QualifiedTable] + { prciName :: !PGCol + , prciType :: !PGScalarType + , prciIsNullable :: !Bool + , prciReferences :: ![QualifiedTable] -- ^ only stores single-column references to primary key of foreign tables (used for detecting -- references to enum tables) + , prciDescription :: !(Maybe PGDescription) } deriving (Show, Eq) $(deriveJSON (aesonDrop 4 snakeCase) ''PGRawColumnInfo) @@ -133,9 +134,10 @@ $(deriveJSON (aesonDrop 4 snakeCase) ''PGRawColumnInfo) -- schema information to produce a 'PGColumnType'. data PGColumnInfo = PGColumnInfo - { pgiName :: !PGCol - , pgiType :: !PGColumnType - , pgiIsNullable :: !Bool + { pgiName :: !PGCol + , pgiType :: !PGColumnType + , pgiIsNullable :: !Bool + , pgiDescription :: !(Maybe PGDescription) } deriving (Show, Eq) $(deriveToJSON (aesonDrop 3 snakeCase) ''PGColumnInfo) diff --git a/server/src-lib/Hasura/RQL/Types/SchemaCache.hs b/server/src-lib/Hasura/RQL/Types/SchemaCache.hs index bf74123370875..28d49a67bd3ec 100644 --- a/server/src-lib/Hasura/RQL/Types/SchemaCache.hs +++ b/server/src-lib/Hasura/RQL/Types/SchemaCache.hs @@ -107,6 +107,7 @@ module Hasura.RQL.Types.SchemaCache , addFunctionToCache , askFunctionInfo , delFunctionFromCache + , updateFunctionDescription , replaceAllowlist ) where @@ -328,6 +329,7 @@ mutableView qt f mVI operation = data TableInfo columnInfo = TableInfo { _tiName :: !QualifiedTable + , _tiDescription :: !(Maybe PGDescription) , _tiSystemDefined :: !Bool , _tiFieldInfoMap :: !(FieldInfoMap columnInfo) , _tiRolePermInfoMap :: !RolePermInfoMap @@ -373,6 +375,7 @@ data FunctionInfo , fiInputArgs :: !(Seq.Seq FunctionArg) , fiReturnType :: !QualifiedTable , fiDeps :: ![SchemaDependency] + , fiDescription :: !(Maybe PGDescription) } deriving (Show, Eq) $(deriveToJSON (aesonDrop 2 snakeCase) ''FunctionInfo) @@ -663,17 +666,25 @@ delFunctionFromCache :: (QErrM m, CacheRWM m) => QualifiedFunction -> m () delFunctionFromCache qf = do + void $ askFunctionInfo qf sc <- askSchemaCache let functionCache = scFunctions sc - case M.lookup qf functionCache of - Nothing -> throw500 $ "function does not exist in cache " <>> qf - Just _ -> do - let newFunctionCache = M.delete qf functionCache - writeSchemaCache $ sc {scFunctions = newFunctionCache} + newFunctionCache = M.delete qf functionCache + writeSchemaCache $ sc {scFunctions = newFunctionCache} modDepMapInCache (removeFromDepMap objId) where objId = SOFunction qf +updateFunctionDescription + :: (QErrM m, CacheRWM m) + => QualifiedFunction -> Maybe PGDescription -> m () +updateFunctionDescription qf descM = do + fi <- askFunctionInfo qf + sc <- askSchemaCache + let newFuncInfo = fi{fiDescription = descM} + newFuncCache = M.insert qf newFuncInfo $ scFunctions sc + writeSchemaCache sc{scFunctions = newFuncCache} + addPermToCache :: (QErrM m, CacheRWM m) => QualifiedTable diff --git a/server/src-lib/Hasura/SQL/Types.hs b/server/src-lib/Hasura/SQL/Types.hs index 38102f399512f..18993e978d496 100644 --- a/server/src-lib/Hasura/SQL/Types.hs +++ b/server/src-lib/Hasura/SQL/Types.hs @@ -230,6 +230,10 @@ snakeCaseTable (QualifiedObject sn tn) = type QualifiedFunction = QualifiedObject FunctionName +newtype PGDescription + = PGDescription { getPGDescription :: T.Text } + deriving (Show, Eq, FromJSON, ToJSON, Q.FromCol) + newtype PGCol = PGCol { getPGColTxt :: T.Text } deriving (Show, Eq, Ord, FromJSON, ToJSON, Hashable, Q.ToPrepArg, Q.FromCol, ToJSONKey, FromJSONKey, Lift, Data) diff --git a/server/src-rsr/catalog_metadata.sql b/server/src-rsr/catalog_metadata.sql index 693ebf292b89b..1886fe4d95c95 100644 --- a/server/src-rsr/catalog_metadata.sql +++ b/server/src-rsr/catalog_metadata.sql @@ -29,6 +29,7 @@ from table_schema, table_name, jsonb_build_object( + 'description', description, 'columns', columns, 'primary_key_columns', primary_key_columns, 'constraints', constraints, diff --git a/server/src-rsr/initialise.sql b/server/src-rsr/initialise.sql index 647bb52bb6ef5..5465fb1b736fd 100644 --- a/server/src-rsr/initialise.sql +++ b/server/src-rsr/initialise.sql @@ -313,6 +313,7 @@ CREATE VIEW hdb_catalog.hdb_function_agg AS SELECT p.proname::text AS function_name, pn.nspname::text AS function_schema, + pd.description, CASE WHEN (p.provariadic = (0) :: oid) THEN false @@ -343,7 +344,7 @@ SELECT WHEN ((rt.typtype) :: text = ('d' :: character(1)) :: text) THEN 'DOMAIN' :: text WHEN ((rt.typtype) :: text = ('e' :: character(1)) :: text) THEN 'ENUM' :: text WHEN ((rt.typtype) :: text = ('r' :: character(1)) :: text) THEN 'RANGE' :: text - WHEN ((rt.typtype) :: text = ('p' :: character(1)) :: text) THEN 'PSUEDO' :: text + WHEN ((rt.typtype) :: text = ('p' :: character(1)) :: text) THEN 'PSEUDO' :: text ELSE NULL :: text END AS return_type_type, p.proretset AS returns_set, @@ -369,6 +370,7 @@ FROM JOIN pg_namespace pn ON (pn.oid = p.pronamespace) JOIN pg_type rt ON (rt.oid = p.prorettype) JOIN pg_namespace rtn ON (rtn.oid = rt.typnamespace) + LEFT JOIN pg_description pd ON p.oid = pd.objoid WHERE pn.nspname :: text NOT LIKE 'pg_%' AND pn.nspname :: text NOT IN ('information_schema', 'hdb_catalog', 'hdb_views') @@ -445,16 +447,21 @@ CREATE VIEW hdb_catalog.hdb_column AS , columns.is_nullable , columns.ordinal_position , coalesce(pkey_refs.ref_tables, '[]') AS primary_key_references + , col_description(pg_class.oid, columns.ordinal_position) AS description FROM information_schema.columns LEFT JOIN primary_key_references AS pkey_refs ON columns.table_schema = pkey_refs.src_table_schema AND columns.table_name = pkey_refs.src_table_name - AND columns.column_name = pkey_refs.src_column_name; + AND columns.column_name = pkey_refs.src_column_name +LEFT JOIN pg_class ON pg_class.relname = columns.table_name +LEFT JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace + AND pg_namespace.nspname = columns.table_schema; CREATE VIEW hdb_catalog.hdb_table_info_agg AS ( select tables.table_name as table_name, tables.table_schema as table_schema, + descriptions.description, coalesce(columns.columns, '[]') as columns, coalesce(pk.columns, '[]') as primary_key_columns, coalesce(constraints.constraints, '[]') as constraints, @@ -470,7 +477,8 @@ from 'name', name, 'type', type, 'is_nullable', is_nullable :: boolean, - 'references', primary_key_references + 'references', primary_key_references, + 'description', description ) ) as columns from @@ -523,6 +531,19 @@ from tables.table_schema = views.table_schema AND tables.table_name = views.table_name ) + left outer join ( + select + pc.relname as table_name, + pn.nspname as table_schema, + pd.description + from pg_class pc + left join pg_namespace pn on pn.oid = pc.relnamespace + left join pg_description pd on pd.objoid = pc.oid + where pd.objsubid = 0 + ) descriptions on ( + tables.table_schema = descriptions.table_schema + AND tables.table_name = descriptions.table_name + ) ); CREATE VIEW hdb_catalog.hdb_function_info_agg AS ( @@ -536,6 +557,7 @@ CREATE VIEW hdb_catalog.hdb_function_info_agg AS ( FROM ( SELECT + description, has_variadic, function_type, return_type_schema, diff --git a/server/src-rsr/migrate_from_12_to_13.sql b/server/src-rsr/migrate_from_12_to_13.sql index 7ab034f7ee1d3..437ed0819d070 100644 --- a/server/src-rsr/migrate_from_12_to_13.sql +++ b/server/src-rsr/migrate_from_12_to_13.sql @@ -33,7 +33,7 @@ SELECT WHEN ((rt.typtype) :: text = ('d' :: character(1)) :: text) THEN 'DOMAIN' :: text WHEN ((rt.typtype) :: text = ('e' :: character(1)) :: text) THEN 'ENUM' :: text WHEN ((rt.typtype) :: text = ('r' :: character(1)) :: text) THEN 'RANGE' :: text - WHEN ((rt.typtype) :: text = ('p' :: character(1)) :: text) THEN 'PSUEDO' :: text + WHEN ((rt.typtype) :: text = ('p' :: character(1)) :: text) THEN 'PSEUDO' :: text ELSE NULL :: text END AS return_type_type, p.proretset AS returns_set, diff --git a/server/src-rsr/migrate_from_21_to_22.sql b/server/src-rsr/migrate_from_21_to_22.sql index 4e5ca9db64e67..854262b0b2b30 100644 --- a/server/src-rsr/migrate_from_21_to_22.sql +++ b/server/src-rsr/migrate_from_21_to_22.sql @@ -36,7 +36,7 @@ SELECT WHEN ((rt.typtype) :: text = ('d' :: character(1)) :: text) THEN 'DOMAIN' :: text WHEN ((rt.typtype) :: text = ('e' :: character(1)) :: text) THEN 'ENUM' :: text WHEN ((rt.typtype) :: text = ('r' :: character(1)) :: text) THEN 'RANGE' :: text - WHEN ((rt.typtype) :: text = ('p' :: character(1)) :: text) THEN 'PSUEDO' :: text + WHEN ((rt.typtype) :: text = ('p' :: character(1)) :: text) THEN 'PSEUDO' :: text ELSE NULL :: text END AS return_type_type, p.proretset AS returns_set, diff --git a/server/src-rsr/migrate_from_22_to_23.sql b/server/src-rsr/migrate_from_22_to_23.sql new file mode 100644 index 0000000000000..fde7389dec7d8 --- /dev/null +++ b/server/src-rsr/migrate_from_22_to_23.sql @@ -0,0 +1,242 @@ +CREATE OR REPLACE VIEW hdb_catalog.hdb_column AS + WITH primary_key_references AS ( + SELECT fkey.table_schema AS src_table_schema + , fkey.table_name AS src_table_name + , fkey.columns->>0 AS src_column_name + , json_agg(json_build_object( + 'schema', fkey.ref_table_table_schema, + 'name', fkey.ref_table + )) AS ref_tables + FROM hdb_catalog.hdb_foreign_key_constraint AS fkey + JOIN hdb_catalog.hdb_primary_key AS pkey + ON pkey.table_schema = fkey.ref_table_table_schema + AND pkey.table_name = fkey.ref_table + AND pkey.columns::jsonb = fkey.ref_columns::jsonb + WHERE json_array_length(fkey.columns) = 1 + GROUP BY fkey.table_schema + , fkey.table_name + , fkey.columns->>0) + SELECT columns.table_schema + , columns.table_name + , columns.column_name AS name + , columns.udt_name AS type + , columns.is_nullable + , columns.ordinal_position + , coalesce(pkey_refs.ref_tables, '[]') AS primary_key_references + , col_description(pg_class.oid, columns.ordinal_position) AS description + FROM information_schema.columns +LEFT JOIN primary_key_references AS pkey_refs + ON columns.table_schema = pkey_refs.src_table_schema + AND columns.table_name = pkey_refs.src_table_name + AND columns.column_name = pkey_refs.src_column_name +LEFT JOIN pg_class ON pg_class.relname = columns.table_name +LEFT JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace + AND pg_namespace.nspname = columns.table_schema; + +DROP VIEW hdb_catalog.hdb_table_info_agg; + +CREATE VIEW hdb_catalog.hdb_table_info_agg AS ( +select + tables.table_name as table_name, + tables.table_schema as table_schema, + descriptions.description, + coalesce(columns.columns, '[]') as columns, + coalesce(pk.columns, '[]') as primary_key_columns, + coalesce(constraints.constraints, '[]') as constraints, + coalesce(views.view_info, 'null') as view_info +from + information_schema.tables as tables + left outer join ( + select + c.table_name, + c.table_schema, + json_agg( + json_build_object( + 'name', name, + 'type', type, + 'is_nullable', is_nullable :: boolean, + 'references', primary_key_references, + 'description', description + ) + ) as columns + from + hdb_catalog.hdb_column c + group by + c.table_schema, + c.table_name + ) columns on ( + tables.table_schema = columns.table_schema + AND tables.table_name = columns.table_name + ) + left outer join ( + select * from hdb_catalog.hdb_primary_key + ) pk on ( + tables.table_schema = pk.table_schema + AND tables.table_name = pk.table_name + ) + left outer join ( + select + c.table_schema, + c.table_name, + json_agg(constraint_name) as constraints + from + information_schema.table_constraints c + where + c.constraint_type = 'UNIQUE' + or c.constraint_type = 'PRIMARY KEY' + group by + c.table_schema, + c.table_name + ) constraints on ( + tables.table_schema = constraints.table_schema + AND tables.table_name = constraints.table_name + ) + left outer join ( + select + table_schema, + table_name, + json_build_object( + 'is_updatable', + (is_updatable::boolean OR is_trigger_updatable::boolean), + 'is_deletable', + (is_updatable::boolean OR is_trigger_deletable::boolean), + 'is_insertable', + (is_insertable_into::boolean OR is_trigger_insertable_into::boolean) + ) as view_info + from + information_schema.views v + ) views on ( + tables.table_schema = views.table_schema + AND tables.table_name = views.table_name + ) + left outer join ( + select + pc.relname as table_name, + pn.nspname as table_schema, + pd.description + from pg_class pc + left join pg_namespace pn on pn.oid = pc.relnamespace + left join pg_description pd on pd.objoid = pc.oid + where pd.objsubid = 0 + ) descriptions on ( + tables.table_schema = descriptions.table_schema + AND tables.table_name = descriptions.table_name + ) +); + +DROP VIEW hdb_catalog.hdb_function_info_agg; +DROP VIEW hdb_catalog.hdb_function_agg; + +CREATE VIEW hdb_catalog.hdb_function_agg AS +( +SELECT + p.proname::text AS function_name, + pn.nspname::text AS function_schema, + pd.description, + + CASE + WHEN (p.provariadic = (0) :: oid) THEN false + ELSE true + END AS has_variadic, + + CASE + WHEN ( + (p.provolatile) :: text = ('i' :: character(1)) :: text + ) THEN 'IMMUTABLE' :: text + WHEN ( + (p.provolatile) :: text = ('s' :: character(1)) :: text + ) THEN 'STABLE' :: text + WHEN ( + (p.provolatile) :: text = ('v' :: character(1)) :: text + ) THEN 'VOLATILE' :: text + ELSE NULL :: text + END AS function_type, + + pg_get_functiondef(p.oid) AS function_definition, + + rtn.nspname::text AS return_type_schema, + rt.typname::text AS return_type_name, + + CASE + WHEN ((rt.typtype) :: text = ('b' :: character(1)) :: text) THEN 'BASE' :: text + WHEN ((rt.typtype) :: text = ('c' :: character(1)) :: text) THEN 'COMPOSITE' :: text + WHEN ((rt.typtype) :: text = ('d' :: character(1)) :: text) THEN 'DOMAIN' :: text + WHEN ((rt.typtype) :: text = ('e' :: character(1)) :: text) THEN 'ENUM' :: text + WHEN ((rt.typtype) :: text = ('r' :: character(1)) :: text) THEN 'RANGE' :: text + WHEN ((rt.typtype) :: text = ('p' :: character(1)) :: text) THEN 'PSEUDO' :: text + ELSE NULL :: text + END AS return_type_type, + p.proretset AS returns_set, + ( SELECT + COALESCE(json_agg(q.type_name), '[]') + FROM + ( + SELECT + pt.typname AS type_name, + pat.ordinality + FROM + unnest( + COALESCE(p.proallargtypes, (p.proargtypes) :: oid []) + ) WITH ORDINALITY pat(oid, ordinality) + LEFT JOIN pg_type pt ON ((pt.oid = pat.oid)) + ORDER BY pat.ordinality ASC + ) q + ) AS input_arg_types, + to_json(COALESCE(p.proargnames, ARRAY [] :: text [])) AS input_arg_names, + p.pronargdefaults AS default_args +FROM + pg_proc p + JOIN pg_namespace pn ON (pn.oid = p.pronamespace) + JOIN pg_type rt ON (rt.oid = p.prorettype) + JOIN pg_namespace rtn ON (rtn.oid = rt.typnamespace) + LEFT JOIN pg_description pd ON p.oid = pd.objoid +WHERE + pn.nspname :: text NOT LIKE 'pg_%' + AND pn.nspname :: text NOT IN ('information_schema', 'hdb_catalog', 'hdb_views') + AND (NOT EXISTS ( + SELECT + 1 + FROM + pg_aggregate + WHERE + ((pg_aggregate.aggfnoid) :: oid = p.oid) + ) + ) +); + +CREATE VIEW hdb_catalog.hdb_function_info_agg AS ( + SELECT + function_name, + function_schema, + row_to_json ( + ( + SELECT + e + FROM + ( + SELECT + description, + has_variadic, + function_type, + return_type_schema, + return_type_name, + return_type_type, + returns_set, + input_arg_types, + input_arg_names, + default_args, + exists( + SELECT + 1 + FROM + information_schema.tables + WHERE + table_schema = return_type_schema + AND table_name = return_type_name + ) AS returns_table + ) AS e + ) + ) AS "function_info" + FROM + hdb_catalog.hdb_function_agg +); diff --git a/server/src-rsr/migrate_from_7_to_8.sql b/server/src-rsr/migrate_from_7_to_8.sql index fb8455043a3ac..c088a00e1a1bc 100644 --- a/server/src-rsr/migrate_from_7_to_8.sql +++ b/server/src-rsr/migrate_from_7_to_8.sql @@ -42,7 +42,7 @@ SELECT WHEN ((rt.typtype) :: text = ('d' :: character(1)) :: text) THEN 'DOMAIN' :: text WHEN ((rt.typtype) :: text = ('e' :: character(1)) :: text) THEN 'ENUM' :: text WHEN ((rt.typtype) :: text = ('r' :: character(1)) :: text) THEN 'RANGE' :: text - WHEN ((rt.typtype) :: text = ('p' :: character(1)) :: text) THEN 'PSUEDO' :: text + WHEN ((rt.typtype) :: text = ('p' :: character(1)) :: text) THEN 'PSEUDO' :: text ELSE NULL :: text END AS return_type_type, p.proretset AS returns_set, diff --git a/server/src-rsr/table_meta.sql b/server/src-rsr/table_meta.sql index 087094fe3b49e..0ae5db49fdf55 100644 --- a/server/src-rsr/table_meta.sql +++ b/server/src-rsr/table_meta.sql @@ -2,6 +2,7 @@ SELECT t.table_schema, t.table_name, t.table_oid, + t.description, coalesce(c.columns, '[]') as columns, coalesce(f.constraints, '[]') as constraints, coalesce(fk.fkeys, '[]') as foreign_keys @@ -10,10 +11,12 @@ FROM SELECT c.oid as table_oid, c.relname as table_name, - n.nspname as table_schema + n.nspname as table_schema, + pd.description as description FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace as n ON c.relnamespace = n.oid + LEFT JOIN pg_catalog.pg_description pd on (c.oid = pd.objoid and pd.objsubid = 0) ) t LEFT OUTER JOIN ( SELECT @@ -25,7 +28,8 @@ FROM 'data_type', type, 'is_nullable', is_nullable :: boolean, 'ordinal_position', ordinal_position, - 'references', primary_key_references + 'references', primary_key_references, + 'description', description ) ) as columns FROM