diff --git a/apps/docs/content/guides/functions/background-tasks.mdx b/apps/docs/content/guides/functions/background-tasks.mdx index b09e7046a5f08..923e70e4a1019 100644 --- a/apps/docs/content/guides/functions/background-tasks.mdx +++ b/apps/docs/content/guides/functions/background-tasks.mdx @@ -55,6 +55,19 @@ Deno.serve(async (req) => { }) ``` +## Handling errors + +We recommend using `try`/`catch` blocks within your background task function to handle errors. + +You can also add an event listener to [`unhandledrejection`](https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event) to handle any promises without a rejection handler. + +```tsx +addEventListener('unhandledrejection', (ev) => { + console.log('unhandledrejection', ev.reason) + ev.preventDefault() +}) +``` + The maximum duration is capped based on the wall-clock, CPU, and memory limits. The function will shut down when it reaches one of these [limits](/docs/guides/functions/limits). @@ -73,9 +86,3 @@ To prevent that, you can update the `supabase/config.toml` with the following se [edge_runtime] policy = "per_worker" ``` - - - -When running with `per_worker` policy, Function won't auto-reload on edits. You will need to manually restart it by running `supabase functions serve`. - - diff --git a/apps/docs/content/troubleshooting/refresh-postgrest-schema.mdx b/apps/docs/content/troubleshooting/refresh-postgrest-schema.mdx new file mode 100644 index 0000000000000..94465972a60bc --- /dev/null +++ b/apps/docs/content/troubleshooting/refresh-postgrest-schema.mdx @@ -0,0 +1,33 @@ +--- +title = "Reload/refresh postgrest schema" +topics = [ + "database", +] +keywords = [ + "schema", + "postgrest", +] + +# Optionally, list the error messages associated with this issue. +[[errors]] +http_status_code = 400 +code = "bad_request" +message = "Could not find a relationship between X and Y in the schema cache" + +--- + +To refresh your PostgREST schema, go to your project **Dashboard**, and open the **SQL Editor**. + +Then, paste the following SQL statement: + +```sql +NOTIFY pgrst, 'reload schema'; +``` + +And click on **Run**. + +You should see a confirmation message under the **Results** tab: + +``` +Success. No rows returned +``` diff --git a/apps/studio/components/grid/components/footer/Footer.tsx b/apps/studio/components/grid/components/footer/Footer.tsx index a6281989b9c03..b99eb0fb06b80 100644 --- a/apps/studio/components/grid/components/footer/Footer.tsx +++ b/apps/studio/components/grid/components/footer/Footer.tsx @@ -5,7 +5,7 @@ import { useTableEditorQuery } from 'data/table-editor/table-editor-query' import { isTableLike, isViewLike } from 'data/table-editor/table-editor-types' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { useUrlState } from 'hooks/ui/useUrlState' -import { Pagination } from './pagination' +import { Pagination } from './pagination/Pagination' export const Footer = () => { const { id: _id } = useParams() diff --git a/apps/studio/components/grid/components/footer/pagination/Pagination.tsx b/apps/studio/components/grid/components/footer/pagination/Pagination.tsx index 4c7170c1868a6..e99e79d015693 100644 --- a/apps/studio/components/grid/components/footer/pagination/Pagination.tsx +++ b/apps/studio/components/grid/components/footer/pagination/Pagination.tsx @@ -1,12 +1,14 @@ import { THRESHOLD_COUNT } from '@supabase/pg-meta/src/sql/studio/get-count-estimate' -import { ArrowLeft, ArrowRight, HelpCircle } from 'lucide-react' +import { ArrowLeft, ArrowRight, HelpCircle, Loader2 } from 'lucide-react' import { useEffect, useState } from 'react' import { useParams } from 'common' import { useTableFilter } from 'components/grid/hooks/useTableFilter' +import { useTableSort } from 'components/grid/hooks/useTableSort' import { useTableEditorQuery } from 'data/table-editor/table-editor-query' -import { isTable } from 'data/table-editor/table-editor-types' +import { isForeignTable, isTable } from 'data/table-editor/table-editor-types' import { useTableRowsCountQuery } from 'data/table-rows/table-rows-count-query' +import { useTableRowsQuery } from 'data/table-rows/table-rows-query' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { RoleImpersonationState } from 'lib/role-impersonation' import { useRoleImpersonationStateSnapshot } from 'state/role-impersonation-state' @@ -24,38 +26,55 @@ const rowsPerPageOptions = [ { value: 1000, label: '1000 rows' }, ] -const Pagination = () => { +const RowCountSelector = ({ + onRowsPerPageChange, +}: { + onRowsPerPageChange: (value: number | string) => void +}) => { + const tableEditorSnap = useTableEditorStateSnapshot() + + return ( + + + + ) +} + +export const Pagination = () => { const { id: _id } = useParams() const id = _id ? Number(_id) : undefined + const { sorts } = useTableSort() + const { filters } = useTableFilter() + const { data: project } = useSelectedProjectQuery() const tableEditorSnap = useTableEditorStateSnapshot() const snap = useTableEditorTableStateSnapshot() + const roleImpersonationState = useRoleImpersonationStateSnapshot() const { data: selectedTable } = useTableEditorQuery({ projectRef: project?.ref, connectionString: project?.connectionString, id, }) + const isForeignTableSelected = isForeignTable(selectedTable) + const page = snap.page // rowsCountEstimate is only applicable to table entities const rowsCountEstimate = isTable(selectedTable) ? selectedTable.live_rows_estimate : null - const { filters } = useTableFilter() - const page = snap.page - - const roleImpersonationState = useRoleImpersonationStateSnapshot() + const [value, setValue] = useState(page.toString()) const [isConfirmNextModalOpen, setIsConfirmNextModalOpen] = useState(false) const [isConfirmPreviousModalOpen, setIsConfirmPreviousModalOpen] = useState(false) const [isConfirmFetchExactCountModalOpen, setIsConfirmFetchExactCountModalOpen] = useState(false) - const [value, setValue] = useState(page.toString()) - - // keep input value in-sync with actual page - useEffect(() => { - setValue(String(page)) - }, [page]) - const { data, isLoading, isSuccess, isError, isFetching, error } = useTableRowsCountQuery( { projectRef: project?.ref, @@ -67,14 +86,33 @@ const Pagination = () => { }, { keepPreviousData: true, + enabled: !isForeignTableSelected, } ) - const count = data?.count ?? 0 const countString = data?.is_estimate ? formatEstimatedCount(count) : count.toLocaleString() const maxPages = Math.ceil(count / tableEditorSnap.rowsPerPage) const totalPages = count > 0 ? maxPages : 1 + // [Joshen] This is only applicable for foreign tables, as we use the number of rows on the page to determine + // if we've reached the last page (and hence disable the next button) + const { data: rowsData, isLoading: isLoadingRows } = useTableRowsQuery( + { + projectRef: project?.ref, + connectionString: project?.connectionString, + tableId: id, + sorts, + filters, + page: snap.page, + limit: tableEditorSnap.rowsPerPage, + roleImpersonationState: roleImpersonationState as RoleImpersonationState, + }, + { + enabled: isForeignTableSelected, + } + ) + const isLastPage = (rowsData?.rows ?? []).length < tableEditorSnap.rowsPerPage + const onPreviousPage = () => { if (page > 1) { if (snap.selectedRows.size >= 1) { @@ -123,16 +161,23 @@ const Pagination = () => { tableEditorSnap.setRowsPerPage(isNaN(rowsPerPage) ? 100 : rowsPerPage) } + // keep input value in-sync with actual page + useEffect(() => { + setValue(String(page)) + }, [page]) + useEffect(() => { - if (page && page > totalPages) { + if (!isForeignTableSelected && page && page > totalPages) { snap.setPage(totalPages) } - }, [page, totalPages]) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isForeignTableSelected, page, totalPages]) useEffect(() => { if (id !== undefined) { snap.setEnforceExactCount(rowsCountEstimate !== null && rowsCountEstimate <= THRESHOLD_COUNT) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [id]) useEffect(() => { @@ -141,11 +186,60 @@ const Pagination = () => { if (isError && snap.enforceExactCount && error?.code === 408) { snap.setEnforceExactCount(false) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [isError, snap.enforceExactCount, error?.code]) + if (isForeignTableSelected) { + return ( +
+
+ ) + } + return (
- {isLoading &&

Loading records count...

} + {isLoading && ( +
+ +

Loading records count...

+
+ )} {isSuccess && ( <> @@ -190,51 +284,44 @@ const Pagination = () => { onClick={onNextPage} /> - - - +
-
-

- {`${countString} ${count === 0 || count > 1 ? `records` : 'record'}`}{' '} - {data.is_estimate ? '(estimated)' : ''} -

- - {data.is_estimate && ( - - -
+ {!isForeignTableSelected && ( +
+

+ {`${countString} ${count === 0 || count > 1 ? `records` : 'record'}`}{' '} + {data.is_estimate ? '(estimated)' : ''} +

+ + {data.is_estimate && ( + + +
+ )} )} @@ -294,4 +381,3 @@ const Pagination = () => { ) } -export default Pagination diff --git a/apps/studio/components/grid/components/footer/pagination/index.ts b/apps/studio/components/grid/components/footer/pagination/index.ts deleted file mode 100644 index 1b98eba60447c..0000000000000 --- a/apps/studio/components/grid/components/footer/pagination/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as Pagination } from './Pagination' diff --git a/apps/studio/components/grid/components/grid/Grid.tsx b/apps/studio/components/grid/components/grid/Grid.tsx index 5608e5b97cae4..f6742bf201a72 100644 --- a/apps/studio/components/grid/components/grid/Grid.tsx +++ b/apps/studio/components/grid/components/grid/Grid.tsx @@ -72,6 +72,7 @@ export const Grid = memo( snap.setSelectedCellPosition({ idx: args.column.idx, rowIdx: args.rowIdx }) } + const page = snap.page const table = snap.table const tableEntityType = snap.originalTable?.entity_type const isForeignTable = tableEntityType === ENTITY_TYPE.FOREIGN_TABLE @@ -154,7 +155,20 @@ export const Grid = memo( {isSuccess && ( <> - {(filters ?? []).length === 0 ? ( + {page > 1 ? ( +
+

This page does not have any data

+
+ +
+
+ ) : (filters ?? []).length === 0 ? (

{isDraggedOver ? 'Drop your CSV file here' : 'This table is empty'} @@ -194,7 +208,7 @@ export const Grid = memo( )}

) : ( -
+

The filters applied have returned no results from this table

diff --git a/apps/www/data/career.json b/apps/www/data/career.json index 2b9d14fb06832..897553de4b6a4 100644 --- a/apps/www/data/career.json +++ b/apps/www/data/career.json @@ -1,7 +1,7 @@ { "company": [ { - "number": "120+", + "number": "180+", "text": "team members\n\nin 35+ countries" }, { @@ -9,15 +9,15 @@ "text": "languages spoken" }, { - "number": "$396M", + "number": "$496M", "text": "in funding" }, { - "number": "350,000+", + "number": "430,000+", "text": "community members" }, { - "number": "20,000+", + "number": "30,000+", "text": "memes posted\n\n(and counting)" } ], diff --git a/apps/www/pages/careers.tsx b/apps/www/pages/careers.tsx index 6feac83d2bfa8..58ee7f1465a26 100644 --- a/apps/www/pages/careers.tsx +++ b/apps/www/pages/careers.tsx @@ -184,7 +184,7 @@ const CareerPage = ({ jobs, placeholderJob, contributors }: CareersPageProps) =>
-

+

What is Supabase

@@ -199,7 +199,7 @@ const CareerPage = ({ jobs, placeholderJob, contributors }: CareersPageProps) =>

supabase team