Skip to content

Commit 1032d13

Browse files
ivasilovjoshenlim
andauthored
fix: Small fixes in Analytics and Vector Buckets (supabase#40386)
* When creating a s3 vectors fdw, add the target schema as a server option. * Fix a bug in the row link, it didn't work when you middle click. * Refactor the protected schema logic to include schemas from all fdws. * Refactor the protected schema logic to include fdw type and name. Change the ProtectedSchemaWarning to show different messages for the 2 fdw types. --------- Co-authored-by: Joshen Lim <[email protected]>
1 parent 849c29f commit 1032d13

File tree

5 files changed

+72
-31
lines changed

5 files changed

+72
-31
lines changed

apps/studio/components/interfaces/Database/ProtectedSchemaWarning.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export const ProtectedSchemaWarning = ({
6565
entity: string
6666
}) => {
6767
const [showModal, setShowModal] = useState(false)
68-
const { isSchemaLocked, reason } = useIsProtectedSchema({ schema })
68+
const { isSchemaLocked, reason, fdwType } = useIsProtectedSchema({ schema })
6969

7070
if (!isSchemaLocked) return null
7171

@@ -81,11 +81,16 @@ export const ProtectedSchemaWarning = ({
8181
size === 'sm' ? '[&>div>p]:text-xs' : '[&>div>p]:text-sm'
8282
)}
8383
>
84-
{reason === 'fdw' ? (
84+
{reason === 'fdw' && fdwType === 'iceberg' ? (
8585
<p>
8686
The <code className="text-xs">{schema}</code> schema is used by Supabase to connect to
8787
analytics buckets and is read-only through the dashboard.
8888
</p>
89+
) : reason === 'fdw' && fdwType === 's3_vectors' ? (
90+
<p>
91+
The <code className="text-xs">{schema}</code> schema is used by Supabase to connect to
92+
vector buckets and is read-only through the dashboard.
93+
</p>
8994
) : (
9095
<>
9196
<p className="mb-2">

apps/studio/components/interfaces/Integrations/Wrappers/Wrappers.constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const WRAPPER_HANDLERS = {
2727
ORB: 'wasm_fdw_handler',
2828
}
2929

30-
const SUPABASE_TARGET_SCHEMA_OPTION: ServerOption = {
30+
export const SUPABASE_TARGET_SCHEMA_OPTION: ServerOption = {
3131
name: 'supabase_target_schema',
3232
label: 'Target Schema',
3333
required: false,
@@ -1458,6 +1458,7 @@ export const WRAPPERS: WrapperMeta[] = [
14581458
secureEntry: false,
14591459
defaultValue: '',
14601460
},
1461+
SUPABASE_TARGET_SCHEMA_OPTION,
14611462
],
14621463
},
14631464
canTargetSchema: true,

apps/studio/components/interfaces/Storage/AnalyticsBuckets/index.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1+
import Link from 'next/link'
2+
import { useRouter } from 'next/navigation'
3+
import { useState } from 'react'
4+
15
import { useParams } from 'common'
26
import { ScaffoldHeader, ScaffoldSection, ScaffoldSectionTitle } from 'components/layouts/Scaffold'
37
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
48
import { useAnalyticsBucketsQuery } from 'data/storage/analytics-buckets-query'
59
import { Bucket as BucketIcon } from 'icons'
610
import { BASE_PATH } from 'lib/constants'
711
import { ChevronRight, ExternalLink, Search } from 'lucide-react'
8-
import Link from 'next/link'
9-
import { useRouter } from 'next/navigation'
10-
import { useState } from 'react'
1112
import {
1213
Badge,
1314
Button,
@@ -48,7 +49,7 @@ export const AnalyticsBuckets = () => {
4849
) => {
4950
const url = `/project/${ref}/storage/analytics/buckets/${encodeURIComponent(bucketId)}`
5051
if (event.metaKey || event.ctrlKey) {
51-
window.open(url, '_blank')
52+
window.open(`${BASE_PATH}${url}`, '_blank')
5253
} else {
5354
router.push(url)
5455
}

apps/studio/data/storage/s3-vectors-wrapper-create-mutation.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { WRAPPERS } from 'components/interfaces/Integrations/Wrappers/Wrappers.c
44
import { getVectorURI } from 'components/interfaces/Storage/StorageSettings/StorageSettings.utils'
55
import {
66
getVectorBucketFDWName,
7+
getVectorBucketFDWSchemaName,
78
getVectorBucketS3KeyName,
89
} from 'components/interfaces/Storage/VectorBuckets/VectorBuckets.utils'
910
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
@@ -50,6 +51,7 @@ export const useS3VectorsWrapperCreateMutation = () => {
5051
vault_secret_access_key: createS3KeyData?.secret_key,
5152
aws_region: settings!.region,
5253
endpoint_url: getVectorURI(project?.ref ?? '', protocol, endpoint),
54+
supabase_target_schema: getVectorBucketFDWSchemaName(bucketName),
5355
},
5456
mode: 'skip',
5557
tables: [],

apps/studio/hooks/useProtectedSchemas.ts

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import { uniq } from 'lodash'
1+
import { uniq, uniqBy } from 'lodash'
22
import { useMemo } from 'react'
33

4-
import { WRAPPER_HANDLERS } from 'components/interfaces/Integrations/Wrappers/Wrappers.constants'
4+
import {
5+
SUPABASE_TARGET_SCHEMA_OPTION,
6+
WRAPPERS,
7+
} from 'components/interfaces/Integrations/Wrappers/Wrappers.constants'
58
import {
69
convertKVStringArrayToJson,
710
wrapperMetaComparator,
@@ -34,35 +37,56 @@ export const INTERNAL_SCHEMAS = [
3437
]
3538

3639
/**
37-
* Get the list of schemas used by IcebergFDWs
40+
* Get the list of schemas used by FDWs like Iceberg, S3 Vectors, etc.
3841
*/
39-
const useIcebergFdwSchemasQuery = () => {
42+
const useFdwSchemasQuery = () => {
4043
const { data: project } = useSelectedProjectQuery()
4144
const result = useFDWsQuery({
4245
projectRef: project?.ref,
4346
connectionString: project?.connectionString,
4447
})
4548

49+
// Find all wrappers that create a schema for their data.
50+
const FDWsWithSchemas = useMemo(
51+
() =>
52+
WRAPPERS.filter((wrapper) =>
53+
wrapper.server.options.some((option) => option.name === SUPABASE_TARGET_SCHEMA_OPTION.name)
54+
),
55+
[]
56+
)
57+
4658
const schemas = useMemo(() => {
47-
const icebergFDWs = result.data?.filter((wrapper) =>
48-
wrapperMetaComparator(
49-
{ handlerName: WRAPPER_HANDLERS.ICEBERG, server: { options: [] } },
50-
wrapper
51-
)
52-
)
59+
const icebergFDWs =
60+
result.data?.filter((wrapper) =>
61+
FDWsWithSchemas.some((w) => wrapperMetaComparator(w, wrapper))
62+
) ?? []
63+
64+
const fdwSchemas = icebergFDWs.map((fdw) => {
65+
const schemaOption =
66+
convertKVStringArrayToJson(fdw.server_options)['supabase_target_schema'] ?? ''
5367

54-
const fdwSchemas = icebergFDWs
55-
?.map((fdw) => convertKVStringArrayToJson(fdw.server_options))
56-
.map((options) => options['supabase_target_schema'])
57-
.flatMap((s) => s?.split(','))
58-
.filter(Boolean)
68+
const schemas = uniq(schemaOption.split(',').filter(Boolean))
5969

60-
return uniq(fdwSchemas)
61-
}, [result.data])
70+
return {
71+
serverName: fdw.server_name,
72+
type: fdw.handler.replace('_fdw_handler', ''),
73+
schemas,
74+
}
75+
})
76+
77+
return fdwSchemas
78+
}, [result.data, FDWsWithSchemas])
6279

6380
return { ...result, data: schemas }
6481
}
6582

83+
type ProtectedSchema = {
84+
name: string
85+
type: 'fdw' | 'internal'
86+
fdwType?: string
87+
serverName?: string
88+
}
89+
6690
/**
6791
* Returns a list of schemas that are protected by Supabase (internal schemas or schemas used by Iceberg FDWs).
6892
*/
@@ -73,13 +97,20 @@ export const useProtectedSchemas = ({
7397
// eslint-disable-next-line react-hooks/exhaustive-deps
7498
const stableExcludeSchemas = useMemo(() => excludeSchemas, [JSON.stringify(excludeSchemas)])
7599

76-
const result = useIcebergFdwSchemasQuery()
100+
const result = useFdwSchemasQuery()
77101

78-
const schemas = useMemo<{ name: string; type: 'fdw' | 'internal' }[]>(() => {
102+
const schemas = useMemo<ProtectedSchema[]>(() => {
79103
const internalSchemas = INTERNAL_SCHEMAS.map((s) => ({ name: s, type: 'internal' as const }))
80-
const icebergFdwSchemas = result.data?.map((s) => ({ name: s, type: 'fdw' as const }))
104+
const fdwSchemas = result.data?.flatMap((s) =>
105+
s.schemas.map((schema) => ({
106+
name: schema,
107+
type: 'fdw' as const,
108+
fdwType: s.type,
109+
serverName: s.serverName,
110+
}))
111+
)
81112

82-
const schemas = uniq([...internalSchemas, ...icebergFdwSchemas])
113+
const schemas = uniqBy([...internalSchemas, ...fdwSchemas], (s) => s.name)
83114
return schemas.filter((schema) => !stableExcludeSchemas.includes(schema.name))
84115
}, [result.data, stableExcludeSchemas])
85116

@@ -96,8 +127,8 @@ export const useIsProtectedSchema = ({
96127
schema: string
97128
excludedSchemas?: string[]
98129
}):
99-
| { isSchemaLocked: false; reason: undefined }
100-
| { isSchemaLocked: true; reason: 'fdw' | 'internal' } => {
130+
| { isSchemaLocked: false; reason: undefined; fdwType: undefined }
131+
| { isSchemaLocked: true; reason: 'internal' | 'fdw'; fdwType: string | undefined } => {
101132
const { data: schemas } = useProtectedSchemas({ excludeSchemas: excludedSchemas })
102133

103134
const foundSchema = schemas.find((s) => s.name === schema)
@@ -106,7 +137,8 @@ export const useIsProtectedSchema = ({
106137
return {
107138
isSchemaLocked: true,
108139
reason: foundSchema.type,
140+
fdwType: foundSchema.fdwType,
109141
}
110142
}
111-
return { isSchemaLocked: false, reason: undefined }
143+
return { isSchemaLocked: false, reason: undefined, fdwType: undefined }
112144
}

0 commit comments

Comments
 (0)