diff --git a/apps/docs/content/guides/api/rest/generating-types.mdx b/apps/docs/content/guides/api/rest/generating-types.mdx index 3f10c9dee37bd..c728c698795b4 100644 --- a/apps/docs/content/guides/api/rest/generating-types.mdx +++ b/apps/docs/content/guides/api/rest/generating-types.mdx @@ -46,6 +46,12 @@ or in case of local development: npx supabase gen types typescript --local > database.types.ts ``` +or in case of a self-hosted instance (see [Accessing Postgres](/docs/guides/self-hosting/docker#accessing-postgres) for more information): + +```bash +npx supabase gen types typescript --db-url postgres://postgres.[POOLER_TENANT_ID]:[POSTGRES_PASSWORD]@[your-domain-or-ip]:5432/postgres --schema public > database.types.ts +``` + These types are generated from your database schema. Given a table `public.movies`, the generated types will look like: ```sql diff --git a/apps/docs/content/guides/storage/buckets/fundamentals.mdx b/apps/docs/content/guides/storage/buckets/fundamentals.mdx index 72e27f0339f58..1a1ff75fb2439 100644 --- a/apps/docs/content/guides/storage/buckets/fundamentals.mdx +++ b/apps/docs/content/guides/storage/buckets/fundamentals.mdx @@ -17,7 +17,7 @@ When a bucket is set to **Private** all operations are subject to access control The only ways to download assets within a private bucket is to: -- Use the [download method](/docs/reference/javascript/storage-from-download) by providing a authorization header containing your user's JWT. The RLS policy you create on the `storage.objects` table will use this user to determine if they have access. +- Use the [download method](/docs/reference/javascript/storage-from-download) by providing an authorization header containing your user's JWT. The RLS policy you create on the `storage.objects` table will use this user to determine if they have access. - Create a signed URL with the [`createSignedUrl` method](/docs/reference/javascript/storage-from-createsignedurl) that can be accessed for a limited time. #### Example use cases: diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx index 043ee9cd303fa..c63dbd112e99c 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx @@ -4,12 +4,12 @@ import { noop } from 'lodash' import { useQueryState } from 'nuqs' import { createContext, - PropsWithChildren, useCallback, useContext, useEffect, useMemo, useState, + type PropsWithChildren, } from 'react' import { useFeaturePreviews } from './useFeaturePreviews' @@ -97,6 +97,12 @@ export const useIsBranching2Enabled = () => { return gitlessBranchingEnabled && flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_BRANCHING_2_0] } +export const useIsPgDeltaDiffEnabled = () => { + const { flags } = useFeaturePreviewContext() + const pgDeltaDiffEnabled = useFlag('pgdeltaDiff') + return pgDeltaDiffEnabled && flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_PG_DELTA_DIFF] +} + export const useIsAdvisorRulesEnabled = () => { const { flags } = useFeaturePreviewContext() const advisorRulesEnabled = useFlag('advisorRules') diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx index cbf5eb934fe4c..106f6ea8d1559 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx @@ -24,6 +24,7 @@ import { APISidePanelPreview } from './APISidePanelPreview' import { Branching2Preview } from './Branching2Preview' import { CLSPreview } from './CLSPreview' import { useFeaturePreviewContext, useFeaturePreviewModal } from './FeaturePreviewContext' +import { PgDeltaDiffPreview } from './PgDeltaDiffPreview' import { QueueOperationsPreview } from './QueueOperationsPreview' import { TableFilterBarPreview } from './TableFilterBarPreview' import { UnifiedLogsPreview } from './UnifiedLogsPreview' @@ -33,6 +34,7 @@ const FEATURE_PREVIEW_KEY_TO_CONTENT: { [key: string]: ReactNode } = { [LOCAL_STORAGE_KEYS.UI_PREVIEW_BRANCHING_2_0]: , + [LOCAL_STORAGE_KEYS.UI_PREVIEW_PG_DELTA_DIFF]: , [LOCAL_STORAGE_KEYS.UI_PREVIEW_ADVISOR_RULES]: , [LOCAL_STORAGE_KEYS.UI_PREVIEW_API_SIDE_PANEL]: , [LOCAL_STORAGE_KEYS.UI_PREVIEW_CLS]: , diff --git a/apps/studio/components/interfaces/App/FeaturePreview/PgDeltaDiffPreview.tsx b/apps/studio/components/interfaces/App/FeaturePreview/PgDeltaDiffPreview.tsx new file mode 100644 index 0000000000000..10d3743388671 --- /dev/null +++ b/apps/studio/components/interfaces/App/FeaturePreview/PgDeltaDiffPreview.tsx @@ -0,0 +1,32 @@ +import { InlineLink } from 'components/ui/InlineLink' + +const PG_DELTA_REPO_URL = 'https://github.com/supabase/pg-toolbelt' + +export const PgDeltaDiffPreview = () => { + return ( +
+

+ Use the pg-delta project to generate + schema diffs instead of migra when creating migrations from branch comparisons. pg-delta is + in alpha and is designed to better handle RLS (Row Level Security) and other schema + constructs. +

+
+

Please note:

+
    +
  • pg-delta is in alpha; behavior may change.
  • +
  • + Generated migrations may contain errors. Review each migration carefully before + executing it. +
  • +
+
+
+

Enabling this preview will:

+
    +
  • Use pg-delta to compute schema diffs when comparing branches, instead of migra
  • +
+
+
+ ) +} diff --git a/apps/studio/components/interfaces/App/FeaturePreview/useFeaturePreviews.ts b/apps/studio/components/interfaces/App/FeaturePreview/useFeaturePreviews.ts index f16028a0ac4ab..ce54a339df146 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/useFeaturePreviews.ts +++ b/apps/studio/components/interfaces/App/FeaturePreview/useFeaturePreviews.ts @@ -18,6 +18,7 @@ export const useFeaturePreviews = (): FeaturePreview[] => { const advisorRulesEnabled = useFlag('advisorRules') const isUnifiedLogsPreviewAvailable = useFlag('unifiedLogs') const tableEditorNewFilterBar = useFlag('tableEditorNewFilterBar') + const pgDeltaDiffEnabled = useFlag('pgdeltaDiff') return [ { @@ -47,6 +48,16 @@ export const useFeaturePreviews = (): FeaturePreview[] => { isPlatformOnly: true, isDefaultOptIn: false, }, + + { + key: LOCAL_STORAGE_KEYS.UI_PREVIEW_PG_DELTA_DIFF, + name: 'PG Delta Diff', + discussionsUrl: undefined, + isNew: true, + isPlatformOnly: true, + isDefaultOptIn: true, + enabled: pgDeltaDiffEnabled, + }, { key: LOCAL_STORAGE_KEYS.UI_PREVIEW_API_SIDE_PANEL, name: 'Project API documentation', diff --git a/apps/studio/components/interfaces/BranchManagement/DatabaseDiffPanel.tsx b/apps/studio/components/interfaces/BranchManagement/DatabaseDiffPanel.tsx index d4162cbe5a629..9b85ce3dca8d9 100644 --- a/apps/studio/components/interfaces/BranchManagement/DatabaseDiffPanel.tsx +++ b/apps/studio/components/interfaces/BranchManagement/DatabaseDiffPanel.tsx @@ -1,4 +1,4 @@ -import { CircleAlert, Database, Download, Wind } from 'lucide-react' +import { CircleAlert, Database, Download, Loader2, Wind } from 'lucide-react' import Link from 'next/link' import { toast } from 'sonner' @@ -19,7 +19,26 @@ export const DatabaseDiffPanel = ({ error, currentBranchRef, }: DatabaseDiffPanelProps) => { - if (isLoading) return + if (isLoading) { + return ( +
+
+
+ + Loading database diff… +
+
+ +
+
+
+ ) + } if (error) return ( @@ -45,8 +64,8 @@ export const DatabaseDiffPanel = ({ } return ( - - + + - - + +
+ +
) diff --git a/apps/studio/data/branches/branch-diff-query.ts b/apps/studio/data/branches/branch-diff-query.ts index bec96019640a8..14f26c8a5d8ba 100644 --- a/apps/studio/data/branches/branch-diff-query.ts +++ b/apps/studio/data/branches/branch-diff-query.ts @@ -9,16 +9,22 @@ export type BranchDiffVariables = { branchRef: string projectRef: string includedSchemas?: string + pgdelta?: boolean } export async function getBranchDiff({ branchRef, includedSchemas, -}: Pick) { + pgdelta, +}: Pick) { + const query: { included_schemas?: string; pgdelta?: string } = {} + if (includedSchemas) query.included_schemas = includedSchemas + if (pgdelta === true) query.pgdelta = 'true' + const { data: diffData, error } = await get('/v1/branches/{branch_id_or_ref}/diff', { params: { path: { branch_id_or_ref: branchRef }, - query: includedSchemas ? { included_schemas: includedSchemas } : undefined, + query: Object.keys(query).length > 0 ? query : undefined, }, headers: { Accept: 'text/plain', @@ -41,15 +47,15 @@ export async function getBranchDiff({ type BranchDiffData = Awaited> export const useBranchDiffQuery = ( - { branchRef, projectRef, includedSchemas }: BranchDiffVariables, + { branchRef, projectRef, includedSchemas, pgdelta }: BranchDiffVariables, { enabled = true, ...options }: Omit, 'queryKey' | 'queryFn'> = {} ) => useQuery({ - queryKey: branchKeys.diff(projectRef, branchRef), - queryFn: () => getBranchDiff({ branchRef, includedSchemas }), + queryKey: branchKeys.diff(projectRef, branchRef, pgdelta), + queryFn: () => getBranchDiff({ branchRef, includedSchemas, pgdelta }), enabled: IS_PLATFORM && enabled && Boolean(branchRef), ...options, }) diff --git a/apps/studio/data/branches/branch-merge-mutation.ts b/apps/studio/data/branches/branch-merge-mutation.ts index 9155bf4146438..105b4845ce3ef 100644 --- a/apps/studio/data/branches/branch-merge-mutation.ts +++ b/apps/studio/data/branches/branch-merge-mutation.ts @@ -11,11 +11,16 @@ export type BranchMergeVariables = { branchProjectRef: string baseProjectRef: string migration_version?: string + pgdelta?: boolean } -export async function mergeBranch({ branchProjectRef, migration_version }: BranchMergeVariables) { +export async function mergeBranch({ + branchProjectRef, + migration_version, + pgdelta, +}: BranchMergeVariables) { // Step 1: Get the diff output from the branch - const diffContent = await getBranchDiff({ branchRef: branchProjectRef }) + const diffContent = await getBranchDiff({ branchRef: branchProjectRef, pgdelta }) let migrationCreated = false diff --git a/apps/studio/data/branches/keys.ts b/apps/studio/data/branches/keys.ts index 91950e8fa8381..1272479f1525f 100644 --- a/apps/studio/data/branches/keys.ts +++ b/apps/studio/data/branches/keys.ts @@ -2,6 +2,6 @@ export const branchKeys = { list: (projectRef: string | undefined) => ['projects', projectRef, 'branches'] as const, detail: (projectRef: string | undefined, branchRef: string | undefined) => ['projects', projectRef, 'branches', branchRef] as const, - diff: (projectRef: string | undefined, branchRef: string | undefined) => - ['projects', projectRef, 'branch', branchRef, 'diff'] as const, + diff: (projectRef: string | undefined, branchRef: string | undefined, pgdelta?: boolean) => + ['projects', projectRef, 'branch', branchRef, 'diff', pgdelta] as const, } diff --git a/apps/studio/hooks/branches/useBranchMergeDiff.ts b/apps/studio/hooks/branches/useBranchMergeDiff.ts index ec7deb6f7c22b..4fc167c40d59f 100644 --- a/apps/studio/hooks/branches/useBranchMergeDiff.ts +++ b/apps/studio/hooks/branches/useBranchMergeDiff.ts @@ -1,3 +1,4 @@ +import { useIsPgDeltaDiffEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { useBranchDiffQuery } from 'data/branches/branch-diff-query' import { useMigrationsQuery } from 'data/database/migrations-query' import { useMemo } from 'react' @@ -51,6 +52,8 @@ export const useBranchMergeDiff = ({ parentBranchConnectionString, currentBranchCreatedAt, }: UseBranchMergeDiffProps): BranchMergeDiffResult => { + const pgDeltaDiffEnabled = useIsPgDeltaDiffEnabled() + // Get database diff const { data: diffContent, @@ -62,6 +65,7 @@ export const useBranchMergeDiff = ({ { branchRef: currentBranchRef || '', projectRef: parentProjectRef || '', + pgdelta: pgDeltaDiffEnabled, }, { enabled: !!currentBranchRef && !!parentProjectRef, diff --git a/apps/studio/pages/project/[ref]/merge.tsx b/apps/studio/pages/project/[ref]/merge.tsx index 556a26813210c..6cf6338395698 100644 --- a/apps/studio/pages/project/[ref]/merge.tsx +++ b/apps/studio/pages/project/[ref]/merge.tsx @@ -40,6 +40,7 @@ import { NavMenu, NavMenuItem, } from 'ui' +import { useIsPgDeltaDiffEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { ConfirmationModal } from 'ui-patterns/Dialogs/ConfirmationModal' const MergePage: NextPageWithLayout = () => { @@ -47,6 +48,7 @@ const MergePage: NextPageWithLayout = () => { const { ref, workflow_run_id: currentWorkflowRunId } = useParams() const { data: project } = useSelectedProjectQuery() const { data: selectedOrg } = useSelectedOrganizationQuery() + const pgDeltaDiffEnabled = useIsPgDeltaDiffEnabled() const [isSubmitting, setIsSubmitting] = useState(false) const [workflowFinalStatus, setWorkflowFinalStatus] = useState<'SUCCESS' | 'FAILED' | null>(null) @@ -297,6 +299,7 @@ const MergePage: NextPageWithLayout = () => { branchProjectRef: ref, baseProjectRef: parentProjectRef, migration_version: undefined, + pgdelta: pgDeltaDiffEnabled, }) } @@ -483,7 +486,7 @@ const MergePage: NextPageWithLayout = () => { breadcrumbs={breadcrumbs} primaryActions={primaryActions} size="full" - className="border-b-0 pb-0" + className="h-full border-b-0 pb-0" >
@@ -570,18 +573,20 @@ const MergePage: NextPageWithLayout = () => {
- - {currentTab === 'database' ? ( - - ) : ( - - )} + +
+ {currentTab === 'database' ? ( + + ) : ( + + )} +