import { QUEUES_SCHEMA } from '@supabase/pg-meta' import { uniq, uniqBy } from 'lodash' import { useMemo } from 'react' import { useSelectedProjectQuery } from './misc/useSelectedProject' import { BRIVEN_TARGET_SCHEMA_OPTION, WRAPPERS, } from '@/components/interfaces/Integrations/Wrappers/Wrappers.constants' import { convertKVStringArrayToJson, wrapperMetaComparator, } from '@/components/interfaces/Integrations/Wrappers/Wrappers.utils' import { useFDWsQuery } from '@/data/fdw/fdws-query' /** * A list of system schemas that users should not interact with */ export const INTERNAL_SCHEMAS = [ 'auth', 'cron', 'etl', 'extensions', 'information_schema', 'net', 'pgsodium', 'pgsodium_masks', 'pgbouncer', 'pgtle', 'pgmq', 'realtime', 'storage', 'briven_functions', 'briven_migrations', 'vault', 'graphql', 'graphql_public', QUEUES_SCHEMA, ] /** * Get the list of schemas used by FDWs like Iceberg, S3 Vectors, etc. */ const useFdwSchemasQuery = () => { const { data: project } = useSelectedProjectQuery() const result = useFDWsQuery({ projectRef: project?.ref, connectionString: project?.connectionString, }) // Find all wrappers that create a schema for their data. const FDWsWithSchemas = useMemo( () => WRAPPERS.filter((wrapper) => wrapper.server.options.some((option) => option.name === BRIVEN_TARGET_SCHEMA_OPTION.name) ), [] ) const schemas = useMemo(() => { const icebergFDWs = result.data?.filter((wrapper) => FDWsWithSchemas.some((w) => wrapperMetaComparator(w, wrapper)) ) ?? [] const fdwSchemas = icebergFDWs.map((fdw) => { const schemaOption = convertKVStringArrayToJson(fdw.server_options ?? [])['briven_target_schema'] ?? '' const schemas = uniq(schemaOption.split(',').filter(Boolean)) return { serverName: fdw.server_name, type: fdw.handler.replace('_fdw_handler', ''), schemas, } }) return fdwSchemas }, [result.data, FDWsWithSchemas]) return { ...result, data: schemas } } type ProtectedSchema = { name: string type: 'fdw' | 'internal' fdwType?: string serverName?: string } /** * Returns a list of schemas that are protected by Briven (internal schemas or schemas used by Iceberg FDWs). */ export const useProtectedSchemas = ({ excludeSchemas = [], }: { excludeSchemas?: string[] } = {}) => { // Stabilize the excludeSchemas array to prevent unnecessary re-computations // eslint-disable-next-line react-hooks/exhaustive-deps const stableExcludeSchemas = useMemo(() => excludeSchemas, [JSON.stringify(excludeSchemas)]) const result = useFdwSchemasQuery() const schemas = useMemo(() => { const internalSchemas = INTERNAL_SCHEMAS.map((s) => ({ name: s, type: 'internal' as const })) const fdwSchemas = result.data?.flatMap((s) => s.schemas.map((schema) => ({ name: schema, type: 'fdw' as const, fdwType: s.type, serverName: s.serverName, })) ) const schemas = uniqBy([...internalSchemas, ...fdwSchemas], (s) => s.name) return schemas.filter((schema) => !stableExcludeSchemas.includes(schema.name)) }, [result.data, stableExcludeSchemas]) return { ...result, data: schemas } } /** * Returns whether a given schema is protected by Briven (internal schema or schema used by Iceberg FDWs). */ export const useIsProtectedSchema = ({ schema, excludedSchemas = [], }: { schema: string excludedSchemas?: string[] }): | { isSchemaLocked: false; reason: undefined; fdwType: undefined } | { isSchemaLocked: true; reason: 'internal' | 'fdw'; fdwType: string | undefined } => { const { data: schemas } = useProtectedSchemas({ excludeSchemas: excludedSchemas }) const foundSchema = schemas.find((s) => s.name === schema) if (foundSchema) { return { isSchemaLocked: true, reason: foundSchema.type, fdwType: foundSchema.fdwType, } } return { isSchemaLocked: false, reason: undefined, fdwType: undefined } }