Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions apps/docs/content/guides/database/extensions/postgis.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -436,12 +436,6 @@ val data = supabase.postgrest.rpc(

## Troubleshooting

<Admonition type="caution">

The [official PostGIS documentation](https://postgis.net/documentation/tips/tip-move-postgis-schema/) for relocating the schema will cause issues for Supabase projects. These issues might not be apparent immediately but will eventually surface. To relocate your schema, use the following steps instead.

</Admonition>

As of PostGIS 2.3 or newer, the PostGIS extension is no longer relocatable from one schema to another. If you need to move it from one schema to another for any reason (e.g. from the public schema to the extensions schema for security reasons), you would normally run a ALTER EXTENSION to relocate the schema. However, you will now to do the following steps:

1. Backup your Database to prevent data loss - You can do this through the [CLI](https://supabase.com/docs/reference/cli/supabase-db-dump) or Postgres backup tools such as [pg_dumpall](https://www.postgresql.org/docs/current/backup-dump.html#BACKUP-DUMP-ALL)
Expand All @@ -452,6 +446,28 @@ As of PostGIS 2.3 or newer, the PostGIS extension is no longer relocatable from

4. Restore dropped data via the Backup if necessary from step 1 with your tool of choice.

Alternatively, you can contact the [Supabase Support Team](https://supabase.com/dashboard/support/new) and ask them to run the following SQL on your instance:

```sql
BEGIN;
UPDATE pg_extension
SET extrelocatable = true
WHERE extname = 'postgis';

ALTER EXTENSION postgis
SET SCHEMA extensions;

ALTER EXTENSION postgis
UPDATE TO "<POSTGIS_VERSION>next";

ALTER EXTENSION postgis UPDATE;

UPDATE pg_extension
SET extrelocatable = false
WHERE extname = 'postgis';
COMMIT;
```

## Resources

- [Official PostGIS documentation](https://postgis.net/documentation/)
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { toast } from 'sonner'

Expand All @@ -6,24 +7,22 @@ import Table from 'components/to-be-cleaned/Table'
import AlertError from 'components/ui/AlertError'
import { useDeleteDestinationMutation } from 'data/replication/delete-destination-mutation'
import { useReplicationPipelineStatusQuery } from 'data/replication/pipeline-status-query'
import { ReplicationPipelinesData } from 'data/replication/pipelines-query'
import { Pipeline } from 'data/replication/pipelines-query'
import { useStopPipelineMutation } from 'data/replication/stop-pipeline-mutation'
import {
PipelineStatusRequestStatus,
usePipelineRequestStatus,
} from 'state/replication-pipeline-request-status'
import { ResponseError } from 'types'
import { Button } from 'ui'
import ShimmeringLoader from 'ui-patterns/ShimmeringLoader'
import DeleteDestination from './DeleteDestination'
import DestinationPanel from './DestinationPanel'
import { getStatusName, PIPELINE_ERROR_MESSAGES } from './Pipeline.utils'
import { PipelineStatus, PipelineStatusName } from './PipelineStatus'
import { STATUS_REFRESH_FREQUENCY_MS } from './Replication.constants'
import { RowMenu } from './RowMenu'

export type Pipeline = ReplicationPipelinesData['pipelines'][0]

const refreshFrequencyMs: number = 2000

interface DestinationRowProps {
sourceId: number | undefined
destinationId: number
Expand All @@ -34,7 +33,6 @@ interface DestinationRowProps {
isLoading: boolean
isError: boolean
isSuccess: boolean
onSelectPipeline?: (pipelineId: number, destinationName: string) => void
}

export const DestinationRow = ({
Expand All @@ -47,7 +45,6 @@ export const DestinationRow = ({
isLoading: isPipelineLoading,
isError: isPipelineError,
isSuccess: isPipelineSuccess,
onSelectPipeline,
}: DestinationRowProps) => {
const { ref: projectRef } = useParams()
const [showDeleteDestinationForm, setShowDeleteDestinationForm] = useState(false)
Expand All @@ -64,7 +61,7 @@ export const DestinationRow = ({
projectRef,
pipelineId: pipeline?.id,
},
{ refetchInterval: refreshFrequencyMs }
{ refetchInterval: STATUS_REFRESH_FREQUENCY_MS }
)
const { getRequestStatus, updatePipelineStatus } = usePipelineRequestStatus()
const requestStatus = pipeline?.id
Expand Down Expand Up @@ -107,12 +104,7 @@ export const DestinationRow = ({
<AlertError error={pipelineError} subject={PIPELINE_ERROR_MESSAGES.RETRIEVE_PIPELINE} />
)}
{isPipelineSuccess && (
<Table.tr
className="hover:!bg-surface-200 transition"
onClick={() => {
if (pipeline) onSelectPipeline?.(pipeline.id, destinationName)
}}
>
<Table.tr>
<Table.td>{isPipelineLoading ? <ShimmeringLoader /> : destinationName}</Table.td>
<Table.td>{isPipelineLoading ? <ShimmeringLoader /> : type}</Table.td>
<Table.td>
Expand All @@ -138,6 +130,11 @@ export const DestinationRow = ({
</Table.td>
<Table.td>
<div className="flex items-center justify-end gap-x-2">
<Button asChild type="default">
<Link href={`/project/${projectRef}/database/replication/${pipeline?.id}`}>
View status
</Link>
</Button>
<RowMenu
pipeline={pipeline}
pipelineStatus={pipelineStatusData?.status}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { noop } from 'lodash'
import { Plus, Search } from 'lucide-react'
import { useState } from 'react'

Expand All @@ -14,11 +13,7 @@ import NewDestinationPanel from './DestinationPanel'
import { DestinationRow } from './DestinationRow'
import { PIPELINE_ERROR_MESSAGES } from './Pipeline.utils'

interface DestinationsProps {
onSelectPipeline?: (pipelineId: number, destinationName: string) => void
}

export const Destinations = ({ onSelectPipeline = noop }: DestinationsProps) => {
export const Destinations = () => {
const [showNewDestinationPanel, setShowNewDestinationPanel] = useState(false)
const [filterString, setFilterString] = useState<string>('')
const { ref: projectRef } = useParams()
Expand Down Expand Up @@ -122,7 +117,6 @@ export const Destinations = ({ onSelectPipeline = noop }: DestinationsProps) =>
isLoading={isPipelinesLoading}
isError={isPipelinesError}
isSuccess={isPipelinesSuccess}
onSelectPipeline={onSelectPipeline}
/>
)
})}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const STATUS_REFRESH_FREQUENCY_MS: number = 5000
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,15 @@ import { Badge, Button, cn, copyToClipboard, Input_Shadcn_ } from 'ui'
import { GenericSkeletonLoader } from 'ui-patterns'
import { getStatusName, PIPELINE_ERROR_MESSAGES } from './Pipeline.utils'
import { PipelineStatus } from './PipelineStatus'
import { STATUS_REFRESH_FREQUENCY_MS } from './Replication.constants'
import { TableState } from './ReplicationPipelineStatus.types'
import { getDisabledStateConfig, getStatusConfig } from './ReplicationPipelineStatus.utils'

interface ReplicationPipelineStatusProps {
pipelineId: number
destinationName?: string
onSelectBack: () => void
}

export const ReplicationPipelineStatus = ({
pipelineId,
destinationName,
onSelectBack,
}: ReplicationPipelineStatusProps) => {
const { ref: projectRef } = useParams()
export const ReplicationPipelineStatus = () => {
const { ref: projectRef, pipelineId: _pipelineId } = useParams()
const [filterString, setFilterString] = useState<string>('')

const pipelineId = Number(_pipelineId)
const { getRequestStatus, updatePipelineStatus, setRequestStatus } = usePipelineRequestStatus()
const requestStatus = getRequestStatus(pipelineId)

Expand All @@ -60,7 +52,7 @@ export const ReplicationPipelineStatus = ({
{ projectRef, pipelineId },
{
enabled: !!pipelineId,
refetchInterval: 2000, // Poll every 2 seconds
refetchInterval: STATUS_REFRESH_FREQUENCY_MS,
}
)

Expand All @@ -73,13 +65,14 @@ export const ReplicationPipelineStatus = ({
{ projectRef, pipelineId },
{
enabled: !!pipelineId,
refetchInterval: 2000, // Poll every 2 seconds
refetchInterval: STATUS_REFRESH_FREQUENCY_MS,
}
)

const { mutateAsync: startPipeline, isLoading: isStartingPipeline } = useStartPipelineMutation()
const { mutateAsync: stopPipeline, isLoading: isStoppingPipeline } = useStopPipelineMutation()

const destinationName = pipeline?.destination_name
const statusName = getStatusName(pipelineStatusData?.status)
const config = getDisabledStateConfig({ requestStatus, statusName })

Expand Down Expand Up @@ -138,36 +131,13 @@ export const ReplicationPipelineStatus = ({
updatePipelineStatus(pipelineId, statusName)
}, [pipelineId, statusName, updatePipelineStatus])

if (isPipelineError) {
return (
<div className="flex flex-col gap-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-x-3">
<Button
type="outline"
onClick={onSelectBack}
icon={<ChevronLeft />}
style={{ padding: '5px' }}
/>
<h3 className="text-xl font-semibold">Pipeline Status</h3>
</div>
</div>
<AlertError error={pipelineError} subject={PIPELINE_ERROR_MESSAGES.RETRIEVE_PIPELINE} />
</div>
)
}

return (
<div className="space-y-4">
{/* Header with back button and filters */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-x-3">
<Button
type="outline"
onClick={onSelectBack}
icon={<ChevronLeft />}
style={{ padding: '5px' }}
/>
<Button asChild type="outline" icon={<ChevronLeft />} style={{ padding: '5px' }}>
<Link href={`/project/${projectRef}/database/replication`} />
</Button>
<div>
<div className="flex items-center gap-x-3">
<h3 className="text-xl font-semibold">{destinationName || 'Pipeline'}</h3>
Expand All @@ -192,13 +162,14 @@ export const ReplicationPipelineStatus = ({
className="pl-7 h-[26px] text-xs"
placeholder="Search for tables"
value={filterString}
disabled={isPipelineError}
onChange={(e) => setFilterString(e.target.value)}
/>
</div>
<Button
type={statusName === 'stopped' ? 'primary' : 'default'}
onClick={() => onTogglePipeline()}
loading={isStartingPipeline || isStoppingPipeline}
loading={isPipelineError || isStartingPipeline || isStoppingPipeline}
disabled={!['failed', 'started', 'stopped'].includes(statusName ?? '')}
>
{statusName === 'stopped' ? 'Enable' : 'Disable'} pipeline
Expand All @@ -208,6 +179,10 @@ export const ReplicationPipelineStatus = ({

{(isPipelineLoading || isStatusLoading) && <GenericSkeletonLoader />}

{isPipelineError && (
<AlertError error={pipelineError} subject={PIPELINE_ERROR_MESSAGES.RETRIEVE_PIPELINE} />
)}

{isStatusError && (
<AlertError
error={statusError}
Expand Down
41 changes: 6 additions & 35 deletions apps/studio/components/interfaces/Database/Replication/RowMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { toast } from 'sonner'

import { useParams } from 'common'
import AlertError from 'components/ui/AlertError'
import { Pipeline } from 'data/replication/pipelines-query'
import { useStartPipelineMutation } from 'data/replication/start-pipeline-mutation'
import { useStopPipelineMutation } from 'data/replication/stop-pipeline-mutation'
import {
Expand All @@ -19,7 +20,6 @@ import {
DropdownMenuTrigger,
} from 'ui'
import ShimmeringLoader from 'ui-patterns/ShimmeringLoader'
import { Pipeline } from './DestinationRow'
import { PIPELINE_ERROR_MESSAGES } from './Pipeline.utils'
import { PipelineStatusName } from './PipelineStatus'

Expand Down Expand Up @@ -103,55 +103,26 @@ export const RowMenu = ({

<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
type="default"
className="px-1.5"
icon={<MoreVertical />}
onClick={(e) => e.stopPropagation()}
/>
<Button type="default" className="px-1.5" icon={<MoreVertical />} />
</DropdownMenuTrigger>
<DropdownMenuContent side="bottom" align="end" className="w-52">
{pipelineEnabled ? (
<DropdownMenuItem
className="space-x-2"
onClick={(e) => {
e.stopPropagation()
onDisablePipeline()
}}
>
<DropdownMenuItem className="space-x-2" onClick={onDisablePipeline}>
<Pause size={14} />
<p>Disable pipeline</p>
</DropdownMenuItem>
) : (
<DropdownMenuItem
className="space-x-2"
onClick={(e) => {
e.stopPropagation()
onEnablePipeline()
}}
>
<DropdownMenuItem className="space-x-2" onClick={onEnablePipeline}>
<Play size={14} />
<p>Enable pipeline</p>
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
<DropdownMenuItem
className="space-x-2"
onClick={(e) => {
e.stopPropagation()
onEditClick()
}}
>
<DropdownMenuItem className="space-x-2" onClick={onEditClick}>
<Edit size={14} />
<p>Edit destination</p>
</DropdownMenuItem>
<DropdownMenuItem
className="space-x-2"
onClick={(e) => {
e.stopPropagation()
onDeleteClick()
}}
>
<DropdownMenuItem className="space-x-2" onClick={onDeleteClick}>
<Trash stroke="red" size={14} />
<p>Delete destination</p>
</DropdownMenuItem>
Expand Down
Loading
Loading