From c56942951952f8985fcdbf0837b0caa2ce8e8f58 Mon Sep 17 00:00:00 2001 From: Ivan Vasilov Date: Fri, 25 Aug 2023 21:16:19 +0200 Subject: [PATCH 1/4] Fix the exporting of CSV to handle JSON columns. --- .../interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx b/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx index f93ac122ee5..28d3bb324a6 100644 --- a/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx +++ b/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx @@ -3,7 +3,7 @@ import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectConte import { useStore } from 'hooks' import { copyToClipboard } from 'lib/helpers' import Telemetry from 'lib/telemetry' -import { compact, isString, map } from 'lodash' +import { compact, isObject, isString, map } from 'lodash' import { useRouter } from 'next/router' import { useMemo, useRef } from 'react' import { CSVLink } from 'react-csv' @@ -34,6 +34,10 @@ const ResultsDropdown = ({ id }: ResultsDropdownProps) => { // escape all quotation marks return v.replaceAll(/\n/g, '\\n').replaceAll(/"/g, '""') } + if (isObject(v)) { + // replace all quotation marks with two quotation marks to escape them. + return JSON.stringify(v).replaceAll(/\"/g, '""') + } return v }) }) From 685481ab586e15a66ba1f27795210b7a04cfbddb Mon Sep 17 00:00:00 2001 From: Oliver Rice Date: Fri, 25 Aug 2023 14:44:29 -0500 Subject: [PATCH 2/4] add template for dbdev installer --- .../SQLEditor/SQLEditor.constants.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts b/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts index 4c020c819c5..ca4213e6081 100644 --- a/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts +++ b/studio/components/interfaces/SQLEditor/SQLEditor.constants.ts @@ -1230,6 +1230,48 @@ begin limit match_count; end; $$; +`.trim(), + }, + { + id: 24, + type: 'template', + title: 'dbdev', + description: 'dbdev is a client for installing 3rd party packages into your database.', + sql: ` +create extension if not exists http with schema extensions; +create extension if not exists pg_tle; +select pgtle.uninstall_extension_if_exists('supabase-dbdev'); +drop extension if exists "supabase-dbdev"; +select + pgtle.install_extension( + 'supabase-dbdev', + resp.contents ->> 'version', + 'PostgreSQL package manager', + resp.contents ->> 'sql' + ) +from http( + ( + 'GET', + 'https://api.database.dev/rest/v1/' + || 'package_versions?select=sql,version' + || '&package_name=eq.supabase-dbdev' + || '&order=version.desc' + || '&limit=1', + array[ + ('apiKey', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhtdXB0cHBsZnZpaWZyYndtbXR2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2ODAxMDczNzIsImV4cCI6MTk5NTY4MzM3Mn0.z2CN0mvO2No8wSi46Gw59DFGCTJrzM0AQKsu_5k134s')::http_header + ], + null, + null + ) +) x, +lateral ( + select + ((row_to_json(x) -> 'content') #>> '{}')::json -> 0 +) resp(contents); +create extension "supabase-dbdev"; +select dbdev.install('supabase-dbdev'); +drop extension if exists "supabase-dbdev"; +create extension "supabase-dbdev"; `.trim(), }, ] From cffef07287c912656e2b47701e70525aba11e43f Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Sat, 26 Aug 2023 17:05:17 +0800 Subject: [PATCH 3/4] Fix permissions issue upon creating organization, along with some small style fixes --- .../interfaces/Organization/NewOrg/NewOrgForm.tsx | 2 +- .../organizations/organization-create-mutation.ts | 6 +++++- .../organizations/organization-delete-mutation.ts | 6 +++++- studio/pages/new-with-subscription.tsx | 3 +-- studio/pages/new/[slug].tsx | 12 ++++++++---- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx b/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx index 97894c4453d..09cee368ca8 100644 --- a/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx +++ b/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx @@ -108,7 +108,7 @@ const NewOrgForm = ({ onPaymentMethodReset }: NewOrgFormProps) => { payment_method: paymentMethodId, V2: true, }) - } finally { + } catch (error) { setNewOrgLoading(false) } } diff --git a/studio/data/organizations/organization-create-mutation.ts b/studio/data/organizations/organization-create-mutation.ts index f0594df216f..17faec33c0e 100644 --- a/studio/data/organizations/organization-create-mutation.ts +++ b/studio/data/organizations/organization-create-mutation.ts @@ -2,6 +2,7 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react import { toast } from 'react-hot-toast' import { post } from 'data/fetchers' +import { permissionKeys } from 'data/permissions/keys' import { ResponseError } from 'types' import { organizationKeys } from './keys' @@ -54,7 +55,10 @@ export const useOrganizationCreateMutation = ({ (vars) => createOrganization(vars), { async onSuccess(data, variables, context) { - await queryClient.invalidateQueries(organizationKeys.list()) + await Promise.all([ + queryClient.invalidateQueries(organizationKeys.list()), + queryClient.invalidateQueries(permissionKeys.list()), + ]) await onSuccess?.(data, variables, context) }, async onError(data, variables, context) { diff --git a/studio/data/organizations/organization-delete-mutation.ts b/studio/data/organizations/organization-delete-mutation.ts index 3b0dcfa7e44..db4af223347 100644 --- a/studio/data/organizations/organization-delete-mutation.ts +++ b/studio/data/organizations/organization-delete-mutation.ts @@ -4,6 +4,7 @@ import { toast } from 'react-hot-toast' import { del } from 'data/fetchers' import { ResponseError } from 'types' import { organizationKeys } from './keys' +import { permissionKeys } from 'data/permissions/keys' export type OrganizationDeleteVariables = { slug: string @@ -33,7 +34,10 @@ export const useOrganizationDeleteMutation = ({ (vars) => deleteOrganization(vars), { async onSuccess(data, variables, context) { - await queryClient.invalidateQueries(organizationKeys.list()) + await Promise.all([ + queryClient.invalidateQueries(organizationKeys.list()), + queryClient.invalidateQueries(permissionKeys.list()), + ]) await onSuccess?.(data, variables, context) }, async onError(data, variables, context) { diff --git a/studio/pages/new-with-subscription.tsx b/studio/pages/new-with-subscription.tsx index c2d7d982ee9..3c34a7433ce 100644 --- a/studio/pages/new-with-subscription.tsx +++ b/studio/pages/new-with-subscription.tsx @@ -9,7 +9,7 @@ import { useCallback, useEffect, useState } from 'react' import { NewOrgForm } from 'components/interfaces/Organization' import { WizardLayout } from 'components/layouts' import { useSetupIntent } from 'data/stripe/setup-intent-mutation' -import { useFlag, useStore } from 'hooks' +import { useFlag } from 'hooks' import { STRIPE_PUBLIC_KEY } from 'lib/constants' import { useIsHCaptchaLoaded } from 'stores/hcaptcha-loaded-store' import { NextPageWithLayout } from 'types' @@ -20,7 +20,6 @@ const stripePromise = loadStripe(STRIPE_PUBLIC_KEY) * No org selected yet, create a new one */ const Wizard: NextPageWithLayout = () => { - const { ui } = useStore() const router = useRouter() const { isDarkMode } = useTheme() diff --git a/studio/pages/new/[slug].tsx b/studio/pages/new/[slug].tsx index 3dbced890a0..6f2e202f666 100644 --- a/studio/pages/new/[slug].tsx +++ b/studio/pages/new/[slug].tsx @@ -94,7 +94,11 @@ const Wizard: NextPageWithLayout = () => { enabled: currentOrg?.subscription_id != undefined, }) - const { mutate: createProject, isLoading: isCreatingNewProject } = useProjectCreateMutation({ + const { + mutate: createProject, + isLoading: isCreatingNewProject, + isSuccess: isSuccessNewProject, + } = useProjectCreateMutation({ onSuccess: (res) => { router.push(`/project/${res.ref}/building`) }, @@ -260,7 +264,7 @@ const Wizard: NextPageWithLayout = () => {
From cb99d12f9bb56e7b3736bd8fb352fa199a13e3f3 Mon Sep 17 00:00:00 2001 From: Alaister Young Date: Mon, 28 Aug 2023 14:42:55 +1000 Subject: [PATCH 4/4] chore: update pg_get_tabledef --- studio/data/database/database-query-constants.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/studio/data/database/database-query-constants.ts b/studio/data/database/database-query-constants.ts index f222e85a9bd..75c6261e1c8 100644 --- a/studio/data/database/database-query-constants.ts +++ b/studio/data/database/database-query-constants.ts @@ -218,9 +218,10 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify( END IF; IF bPartition THEN --v17 fix for case-sensitive tables + -- SELECT count(*) INTO v_cnt FROM information_schema.tables t WHERE EXISTS (SELECT REGEXP_MATCHES(s.table_name, '([A-Z]+)','g') FROM information_schema.tables s + -- WHERE t.table_schema=s.table_schema AND t.table_name=s.table_name AND t.table_schema = quote_ident(in_schema) AND t.table_name = quote_ident(in_table) AND t.table_type = 'BASE TABLE'); SELECT count(*) INTO v_cnt FROM information_schema.tables t WHERE EXISTS (SELECT REGEXP_MATCHES(s.table_name, '([A-Z]+)','g') FROM information_schema.tables s - WHERE t.table_schema=s.table_schema AND t.table_name=s.table_name AND t.table_schema = quote_ident(in_schema) AND t.table_type = 'BASE TABLE'); - + WHERE t.table_schema=s.table_schema AND t.table_name=s.table_name AND t.table_schema = in_schema AND t.table_name = in_table AND t.table_type = 'BASE TABLE'); IF bInheritance THEN -- inheritance-based IF v_cnt > 0 THEN @@ -265,8 +266,10 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify( -- start the create definition for regular tables unless we are in progress creating an inheritance-based child table IF NOT bPartition THEN --v17 fix for case-sensitive tables + -- SELECT count(*) INTO v_cnt FROM information_schema.tables t WHERE EXISTS (SELECT REGEXP_MATCHES(s.table_name, '([A-Z]+)','g') FROM information_schema.tables s + -- WHERE t.table_schema=s.table_schema AND t.table_name=s.table_name AND t.table_schema = quote_ident(in_schema) AND t.table_name = quote_ident(in_table) AND t.table_type = 'BASE TABLE'); SELECT count(*) INTO v_cnt FROM information_schema.tables t WHERE EXISTS (SELECT REGEXP_MATCHES(s.table_name, '([A-Z]+)','g') FROM information_schema.tables s - WHERE t.table_schema=s.table_schema AND t.table_name=s.table_name AND t.table_schema = quote_ident(in_schema) AND t.table_type = 'BASE TABLE'); + WHERE t.table_schema=s.table_schema AND t.table_name=s.table_name AND t.table_schema = in_schema AND t.table_name = in_table AND t.table_type = 'BASE TABLE'); IF v_cnt > 0 THEN v_table_ddl := 'CREATE ' || v_temp || ' TABLE ' || in_schema || '."' || in_table || '" (' || E'\\n'; ELSE