@@ -57,40 +63,7 @@ const ComputeMetric = ({ slug, metric, usage, relativeToSubscription }: ComputeM
{usageMeta?.unit_price_desc}
- {usageMeta?.project_allocations && usageMeta.project_allocations.length > 0 && (
-
-
-
- | Project |
- Usage |
-
-
-
- {usageMeta.project_allocations.map((allocation) => (
-
- | {allocation.name} |
-
- {formatUsage(usageMeta.metric as PricingMetric, allocation.usage)}
- |
-
- ))}
-
-
-
-
- | Total (Hours) |
-
- {formatUsage(
- usageMeta.metric as PricingMetric,
- usageMeta.usage_original
- )}
- |
-
-
-
- )}
-
-
+
Every project is a dedicated server and database. For every hour your project
is active, it incurs compute costs based on the compute size of your project.
@@ -104,6 +77,38 @@ const ComputeMetric = ({ slug, metric, usage, relativeToSubscription }: ComputeM
+
+ {usageMeta && sortedProjectAllocations.length > 0 && (
+
+
+
+ | Project |
+ Usage |
+
+
+
+ {sortedProjectAllocations.map((allocation) => (
+
+ | {allocation.name} |
+
+ {formatUsage(usageMeta.metric as PricingMetric, allocation)}
+ |
+
+ ))}
+
+
+
+
+ | Total (Hours) |
+
+ {formatUsage(usageMeta.metric as PricingMetric, {
+ usage: usageMeta.usage_original,
+ })}
+ |
+
+
+
+ )}
diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/UpcomingInvoice.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/UpcomingInvoice.tsx
index 9a9a2fe5cdf..d3fe1d030e1 100644
--- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/UpcomingInvoice.tsx
+++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingBreakdown/UpcomingInvoice.tsx
@@ -193,7 +193,7 @@ const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => {
{fee.usage_original
- ? `${formatUsage(fee.usage_metric!, fee.usage_original)}`
+ ? `${formatUsage(fee.usage_metric!, { usage: fee.usage_original })}`
: fee.quantity
? fee.quantity
: null}
@@ -223,7 +223,7 @@ const UpcomingInvoice = ({ slug }: UpcomingInvoiceProps) => {
{breakdown.project_name}
|
- {formatUsage(fee.usage_metric!, breakdown.usage)}
+ {formatUsage(fee.usage_metric!, breakdown)}
|
|
|
diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/helpers.ts b/apps/studio/components/interfaces/Organization/BillingSettings/helpers.ts
index 431fe037dd0..62677c4ba83 100644
--- a/apps/studio/components/interfaces/Organization/BillingSettings/helpers.ts
+++ b/apps/studio/components/interfaces/Organization/BillingSettings/helpers.ts
@@ -6,11 +6,32 @@ const pricingMetricBytes = [
PricingMetric.STORAGE_SIZE,
]
-export const formatUsage = (pricingMetric: PricingMetric, usage: number) => {
+export const formatUsage = (
+ pricingMetric: PricingMetric,
+ allocation: { usage: number; hours?: number }
+) => {
+ if (allocation.hours) {
+ return (
+ allocation.usage.toLocaleString() +
+ ' (' +
+ Math.round(allocation.usage / allocation.hours).toLocaleString() +
+ 'x' +
+ allocation.hours.toLocaleString() +
+ ' hours)'
+ )
+ }
+
if (pricingMetricBytes.includes(pricingMetric)) {
- return +(usage / 1e9).toFixed(2).toLocaleString()
+ const formattedUsage = +(allocation.usage / 1e9).toFixed(2).toLocaleString()
+
+ // To avoid very low usage displaying as "0", we will show "<0.01" instead
+ if (allocation.usage > 0 && formattedUsage === 0) {
+ return '<0.01'
+ } else {
+ return formattedUsage
+ }
} else {
- return usage.toLocaleString()
+ return allocation.usage.toLocaleString()
}
}
diff --git a/apps/studio/components/interfaces/Organization/IntegrationSettings/SidePanelGitHubRepoLinker.tsx b/apps/studio/components/interfaces/Organization/IntegrationSettings/SidePanelGitHubRepoLinker.tsx
index cba30fb97df..1a3b423f313 100644
--- a/apps/studio/components/interfaces/Organization/IntegrationSettings/SidePanelGitHubRepoLinker.tsx
+++ b/apps/studio/components/interfaces/Organization/IntegrationSettings/SidePanelGitHubRepoLinker.tsx
@@ -36,7 +36,7 @@ const SidePanelGitHubRepoLinker = ({ projectRef }: SidePanelGitHubRepoLinkerProp
const sidePanelStateSnapshot = useSidePanelsStateSnapshot()
const { data: gitHubAuthorization, isLoading: isLoadingGitHubAuthorization } =
- useGitHubAuthorizationQuery()
+ useGitHubAuthorizationQuery({ enabled: sidePanelStateSnapshot.githubConnectionsOpen })
// [Alaister]: temp override with
until the typegen is fixed
const { data: githubReposData, isLoading: isLoadingGitHubRepos } = useGitHubRepositoriesQuery<
diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/OAuthApps.tsx b/apps/studio/components/interfaces/Organization/OAuthApps/OAuthApps.tsx
index 8744880f1a8..512de2049f4 100644
--- a/apps/studio/components/interfaces/Organization/OAuthApps/OAuthApps.tsx
+++ b/apps/studio/components/interfaces/Organization/OAuthApps/OAuthApps.tsx
@@ -61,7 +61,7 @@ const OAuthApps = () => {
if (!canReadOAuthApps) {
return (
-
+
)
}
diff --git a/apps/studio/components/interfaces/Organization/Usage/TotalUsage.tsx b/apps/studio/components/interfaces/Organization/Usage/TotalUsage.tsx
index f56de7d4206..f664536c08a 100644
--- a/apps/studio/components/interfaces/Organization/Usage/TotalUsage.tsx
+++ b/apps/studio/components/interfaces/Organization/Usage/TotalUsage.tsx
@@ -58,9 +58,13 @@ const TotalUsage = ({
)
const sortedBillingMetrics = useMemo(() => {
- if (!usage) return BILLING_BREAKDOWN_METRICS
+ if (!usage) return []
- return BILLING_BREAKDOWN_METRICS.slice().sort((a, b) => {
+ const breakdownMetrics = BILLING_BREAKDOWN_METRICS.filter((metric) =>
+ usage.usages.some((usage) => !usage.available_in_plan || usage.metric === metric.key)
+ )
+
+ return breakdownMetrics.slice().sort((a, b) => {
const usageMetaA = usage.usages.find((x) => x.metric === a.key)
const usageRatioA =
typeof usageMetaA !== 'number'
@@ -92,8 +96,8 @@ const TotalUsage = ({
section={{
name: 'Usage Summary',
description: isUsageBillingEnabled
- ? 'Your plan includes a limited amount of usage. If the usage on your organization exceeds these quotas, your subscription will be charged for the overages. It may take up to 24 hours for usage stats to update.'
- : 'Your plan includes a limited amount of usage. If the usage on your organization exceeds these quotas, you may experience restrictions, as you are currently not billed for overages. It may take up to 24 hours for usage stats to update.',
+ ? `Your plan includes a limited amount of usage. If the usage on your organization exceeds these quotas, your subscription will be charged for the overages. It may take up to ${subscription?.usage_based_billing_project_addons ? '1 hour' : '24 hours'} for usage stats to update.`
+ : `Your plan includes a limited amount of usage. If the usage on your organization exceeds these quotas, you may experience restrictions, as you are currently not billed for overages. It may take up to ${subscription?.usage_based_billing_project_addons ? '1 hour' : '24 hours'} for usage stats to update.`,
links: [
{
name: 'How billing works',
@@ -184,7 +188,8 @@ const TotalUsage = ({
diff --git a/apps/studio/components/interfaces/Organization/Usage/Usage.constants.tsx b/apps/studio/components/interfaces/Organization/Usage/Usage.constants.tsx
index 60317e059eb..fbf88f42ebb 100644
--- a/apps/studio/components/interfaces/Organization/Usage/Usage.constants.tsx
+++ b/apps/studio/components/interfaces/Organization/Usage/Usage.constants.tsx
@@ -47,7 +47,7 @@ export interface CategoryAttribute {
key: string // Property from organization usage
attributes: Attribute[] // For querying against stats-daily / infra-monitoring
name: string
- unit: 'bytes' | 'absolute' | 'percentage' | 'hours'
+ unit: 'bytes' | 'absolute' | 'percentage' | 'hours' | 'gigabytes'
links?: {
name: string
url: string
@@ -56,7 +56,7 @@ export interface CategoryAttribute {
chartPrefix?: 'Max' | 'Average' | 'Cumulative'
chartSuffix?: string
chartDescription: string
- additionalInfo?: (subscription?: OrgSubscription, usage?: OrgUsageResponse) => JSX.Element | null
+ additionalInfo?: (usage?: OrgUsageResponse) => JSX.Element | null
}
export type CategoryMetaKey = 'bandwidth' | 'sizeCount' | 'activity' | 'compute'
@@ -68,7 +68,9 @@ export interface CategoryMeta {
attributes: CategoryAttribute[]
}
-export const USAGE_CATEGORIES: CategoryMeta[] = [
+export const USAGE_CATEGORIES: (subscription?: OrgSubscription) => CategoryMeta[] = (
+ subscription
+) => [
{
key: 'bandwidth',
name: 'Bandwidth',
@@ -106,15 +108,25 @@ export const USAGE_CATEGORIES: CategoryMeta[] = [
chartPrefix: 'Average',
unit: 'bytes',
description:
- 'Database size refers to the monthly average storage usage, as reported by Postgres. Paid Plans use auto-scaling disks.\nBilling is based on the average daily database size used in GB throughout the billing period. Billing is independent of the provisioned disk size.',
+ subscription?.usage_based_billing_project_addons === true
+ ? 'Database size refers to the monthly average database space usage, as reported by Postgres. Paid Plans use auto-scaling disks and are billed based on provisioned disk size, rather than database space used.'
+ : 'Database size refers to the monthly average database space usage, as reported by Postgres. Paid Plans use auto-scaling disks.\nBilling is based on the average daily database size used in GB throughout the billing period. Billing is independent of the provisioned disk size.',
links: [
{
name: 'Documentation',
url: 'https://supabase.com/docs/guides/platform/database-size',
},
+ ...(subscription?.usage_based_billing_project_addons === true
+ ? [
+ {
+ name: 'Disk Management',
+ url: 'https://supabase.com/docs/guides/platform/database-size#disk-management',
+ },
+ ]
+ : []),
],
chartDescription: 'The data refreshes every 24 hours.',
- additionalInfo: (subscription?: OrgSubscription, usage?: OrgUsageResponse) => {
+ additionalInfo: (usage?: OrgUsageResponse) => {
const usageMeta = usage?.usages.find((x) => x.metric === PricingMetric.DATABASE_SIZE)
const usageRatio =
typeof usageMeta !== 'number'
diff --git a/apps/studio/components/interfaces/Organization/Usage/Usage.utils.ts b/apps/studio/components/interfaces/Organization/Usage/Usage.utils.ts
index 4e68b9e278a..62dcce33ae1 100644
--- a/apps/studio/components/interfaces/Organization/Usage/Usage.utils.ts
+++ b/apps/studio/components/interfaces/Organization/Usage/Usage.utils.ts
@@ -38,6 +38,8 @@ export const ChartYFormatterCompactNumber = (number: number | string, unit: stri
const formattedBytes = formatBytesCompact(number).replace(/\s/g, '')
return formattedBytes === '0bytes' ? '0' : formattedBytes
+ } else if (unit === 'gigabytes') {
+ return compactNumberFormatter.format(number) + 'GB'
} else {
return compactNumberFormatter.format(number)
}
@@ -53,6 +55,8 @@ export const ChartTooltipValueFormatter = (number: number | string, unit: string
const formattedBytes = formatBytesPrecision(number).replace(/\s/g, '')
return formattedBytes === '0bytes' ? '0' : formattedBytes
+ } else if (unit === 'gigabytes') {
+ return compactNumberFormatter.format(number) + 'GB'
} else {
return compactNumberFormatter.format(number)
}
diff --git a/apps/studio/components/interfaces/Organization/Usage/UsageBarChart.tsx b/apps/studio/components/interfaces/Organization/Usage/UsageBarChart.tsx
index 003943ddfa5..937f36c09af 100644
--- a/apps/studio/components/interfaces/Organization/Usage/UsageBarChart.tsx
+++ b/apps/studio/components/interfaces/Organization/Usage/UsageBarChart.tsx
@@ -21,7 +21,7 @@ export interface UsageBarChartProps {
data: DataPoint[]
name: string // Used within the tooltip
attributes: Attribute[]
- unit: 'bytes' | 'absolute' | 'percentage' | 'hours'
+ unit: 'bytes' | 'absolute' | 'percentage' | 'hours' | 'gigabytes'
yLimit?: number
yMin?: number
yLeftMargin?: number
diff --git a/apps/studio/components/interfaces/Organization/Usage/UsageChartTooltips.tsx b/apps/studio/components/interfaces/Organization/Usage/UsageChartTooltips.tsx
index da6f20200c6..685c7afb46e 100644
--- a/apps/studio/components/interfaces/Organization/Usage/UsageChartTooltips.tsx
+++ b/apps/studio/components/interfaces/Organization/Usage/UsageChartTooltips.tsx
@@ -4,7 +4,7 @@ import { Attribute, COLOR_MAP } from './Usage.constants'
export interface SingleAttributeTooltipContentProps {
name: string
- unit: 'bytes' | 'percentage' | 'absolute' | 'hours'
+ unit: 'bytes' | 'percentage' | 'absolute' | 'hours' | 'gigabytes'
value: any
isAfterToday: boolean
tooltipFormatter?: (value: any) => any
@@ -37,7 +37,7 @@ export interface MultiAttributeTooltipContentProps {
values: Payload
[]
isAfterToday: boolean
tooltipFormatter?: (value: any) => any
- unit: 'bytes' | 'percentage' | 'absolute' | 'hours'
+ unit: 'bytes' | 'percentage' | 'absolute' | 'hours' | 'gigabytes'
}
const AttributeContent = ({
@@ -51,7 +51,7 @@ const AttributeContent = ({
attributeMeta?: Payload
sumValue: number
tooltipFormatter?: (value: any) => any
- unit: 'bytes' | 'percentage' | 'absolute' | 'hours'
+ unit: 'bytes' | 'percentage' | 'absolute' | 'hours' | 'gigabytes'
}) => {
const attrValue = Number(attributeMeta?.value ?? 0)
const percentageContribution = ((attrValue / sumValue) * 100).toFixed(1)
diff --git a/apps/studio/components/interfaces/Organization/Usage/UsageSection/AttributeUsage.tsx b/apps/studio/components/interfaces/Organization/Usage/UsageSection/AttributeUsage.tsx
index e0a01bd76c1..bebc034cb83 100644
--- a/apps/studio/components/interfaces/Organization/Usage/UsageSection/AttributeUsage.tsx
+++ b/apps/studio/components/interfaces/Organization/Usage/UsageSection/AttributeUsage.tsx
@@ -93,7 +93,7 @@ const AttributeUsage = ({
{isSuccess && (
<>
- {usageMeta?.available_in_plan ? (
+ {!usageMeta || usageMeta?.available_in_plan ? (
<>
{!projectRef && (
@@ -150,7 +150,7 @@ const AttributeUsage = ({
- {currentBillingCycleSelected && !usageMeta.unlimited && (
+ {currentBillingCycleSelected && usageMeta && !usageMeta.unlimited && (
)}
- {
-
+
+ {usageMeta && (
Included in {subscription?.plan?.name.toLowerCase()} plan
@@ -179,43 +179,42 @@ const AttributeUsage = ({
Unlimited
) : (
- {attribute.unit === 'bytes'
+ {attribute.unit === 'bytes' || attribute.unit === 'gigabytes'
? `${usageMeta.pricing_free_units ?? 0} GB`
: (usageMeta.pricing_free_units ?? 0).toLocaleString()}
)}
- {currentBillingCycleSelected && (
-
-
- {attribute.chartPrefix || 'Used '} in period
-
-
- {attribute.unit === 'bytes'
- ? `${(usageMeta?.usage ?? 0).toFixed(2)} GB`
- : (usageMeta?.usage ?? 0).toLocaleString()}
-
-
- )}
- {currentBillingCycleSelected &&
- (usageMeta?.pricing_free_units ?? 0) > 0 && (
-
-
Overage in period
-
- {(usageMeta?.pricing_free_units ?? 0) === -1 || usageExcess < 0
- ? `0${attribute.unit === 'bytes' ? ' GB' : ''}`
- : attribute.unit === 'bytes'
- ? `${usageExcess.toFixed(2)} GB`
- : usageExcess.toLocaleString()}
-
-
- )}
-
- }
+ )}
+ {currentBillingCycleSelected && usageMeta && (
+
+
+ {attribute.chartPrefix || 'Used '} in period
+
+
+ {attribute.unit === 'bytes' || attribute.unit === 'gigabytes'
+ ? `${(usageMeta?.usage ?? 0).toFixed(2)} GB`
+ : (usageMeta?.usage ?? 0).toLocaleString()}
+
+
+ )}
+ {currentBillingCycleSelected && (usageMeta?.pricing_free_units ?? 0) > 0 && (
+
+
Overage in period
+
+ {(usageMeta?.pricing_free_units ?? 0) === -1 || usageExcess < 0
+ ? `0${attribute.unit === 'bytes' || attribute.unit === 'gigabytes' ? ' GB' : ''}`
+ : attribute.unit === 'bytes' || attribute.unit === 'gigabytes'
+ ? `${usageExcess.toFixed(2)} GB`
+ : usageExcess.toLocaleString()}
+
+
+ )}
+
)}
- {attribute.additionalInfo?.(subscription, usage)}
+ {attribute.additionalInfo?.(usage)}
diff --git a/apps/studio/components/interfaces/Organization/Usage/UsageSection/UsageSection.tsx b/apps/studio/components/interfaces/Organization/Usage/UsageSection/UsageSection.tsx
index f5a4fe47b8f..21f118c093f 100644
--- a/apps/studio/components/interfaces/Organization/Usage/UsageSection/UsageSection.tsx
+++ b/apps/studio/components/interfaces/Organization/Usage/UsageSection/UsageSection.tsx
@@ -35,7 +35,9 @@ const UsageSection = ({
isSuccess: isSuccessUsage,
} = useOrgUsageQuery({ orgSlug })
- const categoryMeta = USAGE_CATEGORIES.find((category) => category.key === categoryKey)
+ const categoryMeta = USAGE_CATEGORIES(subscription).find(
+ (category) => category.key === categoryKey
+ )
if (!categoryMeta) return null
return (
diff --git a/apps/studio/components/layouts/SignInLayout/SignInLayout.tsx b/apps/studio/components/layouts/SignInLayout/SignInLayout.tsx
index cf65385a68b..056ecd46d17 100644
--- a/apps/studio/components/layouts/SignInLayout/SignInLayout.tsx
+++ b/apps/studio/components/layouts/SignInLayout/SignInLayout.tsx
@@ -63,11 +63,6 @@ const SignInLayout = ({
await queryClient.resetQueries()
router.push(getReturnToPath())
- } else {
- // if the user doesn't have a token, he needs to go back to the sign-in page
- const redirectTo = buildPathWithParams('/sign-in')
- router.replace(redirectTo)
- return
}
})
.catch(() => {}) // catch all errors thrown by auth methods
diff --git a/apps/studio/components/ui/NoPermission.tsx b/apps/studio/components/ui/NoPermission.tsx
index ab96619caca..3fb1a0b8554 100644
--- a/apps/studio/components/ui/NoPermission.tsx
+++ b/apps/studio/components/ui/NoPermission.tsx
@@ -22,7 +22,7 @@ const NoPermission = ({ resourceText, isFullPage = false }: NoPermissionProps) =
You need additional permissions to {resourceText}
- Contact your organization owner or adminstrator for assistance.
+ Contact your organization owner or administrator for assistance.
diff --git a/apps/studio/data/analytics/org-daily-stats-query.ts b/apps/studio/data/analytics/org-daily-stats-query.ts
index 8b74d5e8d30..fa4e3797cc1 100644
--- a/apps/studio/data/analytics/org-daily-stats-query.ts
+++ b/apps/studio/data/analytics/org-daily-stats-query.ts
@@ -21,6 +21,7 @@ export enum PricingMetric {
EGRESS = 'EGRESS',
DATABASE_SIZE = 'DATABASE_SIZE',
STORAGE_SIZE = 'STORAGE_SIZE',
+ DISK_SIZE_GB_HOURS = 'DISK_SIZE_GB_HOURS',
MONTHLY_ACTIVE_USERS = 'MONTHLY_ACTIVE_USERS',
MONTHLY_ACTIVE_SSO_USERS = 'MONTHLY_ACTIVE_SSO_USERS',
FUNCTION_INVOCATIONS = 'FUNCTION_INVOCATIONS',
diff --git a/apps/studio/data/database/database-table-definition.ts b/apps/studio/data/database/database-table-definition.ts
index bc968581287..0e09cb40507 100644
--- a/apps/studio/data/database/database-table-definition.ts
+++ b/apps/studio/data/database/database-table-definition.ts
@@ -2,38 +2,66 @@ import minify from 'pg-minify'
// Derived from https://github.com/MichaelDBA/pg_get_tabledef
// NOTE: when updating, \n must be replaced with \\n in the SQL below
+// public.x is replaced with pg_temp.x
+
+// REMOVE (as it breaks the encoding for some reason):
+// SELECT REPLACE(REPLACE(setting, '"$user"', '$user'), '$user', '"$user"') INTO search_path_old
+// FROM pg_settings
+// WHERE name = 'search_path';
export const CREATE_PG_GET_TABLEDEF_SQL = minify(
/* SQL */ `
DROP TYPE IF EXISTS pg_temp.tabledefs CASCADE;
- CREATE TYPE pg_temp.tabledefs AS ENUM ('PKEY_INTERNAL','PKEY_EXTERNAL','FKEYS_INTERNAL', 'FKEYS_EXTERNAL', 'FKEYS_COMMENTED', 'FKEYS_NONE', 'INCLUDE_TRIGGERS', 'NO_TRIGGERS');
+ CREATE TYPE pg_temp.tabledefs AS ENUM ('PKEY_INTERNAL','PKEY_EXTERNAL','FKEYS_INTERNAL', 'FKEYS_EXTERNAL', 'COMMENTS', 'FKEYS_NONE', 'INCLUDE_TRIGGERS', 'NO_TRIGGERS');
+ -- SELECT * FROM pg_temp.pg_get_coldef('sample','orders','id');
-- DROP FUNCTION pg_temp.pg_get_coldef(text,text,text,boolean);
CREATE OR REPLACE FUNCTION pg_temp.pg_get_coldef(
in_schema text,
- in_table text,
+ in_table text,
in_column text,
- oldway boolean default False
+ oldway boolean default False
)
RETURNS text
LANGUAGE plpgsql VOLATILE
AS
$$
DECLARE
- coldef text;
+ v_coldef text;
+ v_dt1 text;
+ v_dt2 text;
+ v_dt3 text;
+ v_nullable boolean;
+ v_position int;
+ v_identity text;
+ v_generated text;
+ v_hasdflt boolean;
+ v_dfltexpr text;
+
BEGIN
IF oldway THEN
- SELECT pg_catalog.format_type(a.atttypid, a.atttypmod) INTO coldef FROM pg_namespace n, pg_class c, pg_attribute a, pg_type t
+ SELECT pg_catalog.format_type(a.atttypid, a.atttypmod) INTO v_coldef FROM pg_namespace n, pg_class c, pg_attribute a, pg_type t
WHERE n.nspname = in_schema AND n.oid = c.relnamespace AND c.relname = in_table AND a.attname = in_column and a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid ORDER BY a.attnum;
+ -- RAISE NOTICE 'DEBUG: oldway=%',v_coldef;
ELSE
-- a.attrelid::regclass::text, a.attname
SELECT CASE WHEN a.atttypid = ANY ('{int,int8,int2}'::regtype[]) AND EXISTS (SELECT FROM pg_attrdef ad WHERE ad.adrelid = a.attrelid AND ad.adnum = a.attnum AND
pg_get_expr(ad.adbin, ad.adrelid) = 'nextval(''' || (pg_get_serial_sequence (a.attrelid::regclass::text, a.attname))::regclass || '''::regclass)') THEN CASE a.atttypid
WHEN 'int'::regtype THEN 'serial' WHEN 'int8'::regtype THEN 'bigserial' WHEN 'int2'::regtype THEN 'smallserial' END ELSE format_type(a.atttypid, a.atttypmod) END AS data_type
- INTO coldef FROM pg_namespace n, pg_class c, pg_attribute a, pg_type t
+ INTO v_coldef FROM pg_namespace n, pg_class c, pg_attribute a, pg_type t
WHERE n.nspname = in_schema AND n.oid = c.relnamespace AND c.relname = in_table AND a.attname = in_column and a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid ORDER BY a.attnum;
+ -- RAISE NOTICE 'DEBUG: newway=%',v_coldef;
+
+ -- Issue#24: not implemented yet
+ -- might replace with this below to do more detailed parsing...
+ -- SELECT a.atttypid::regtype AS dt1, format_type(a.atttypid, a.atttypmod) as dt2, t.typname as dt3, CASE WHEN not(a.attnotnull) THEN True ELSE False END AS nullable,
+ -- a.attnum, a.attidentity, a.attgenerated, a.atthasdef, pg_get_expr(ad.adbin, ad.adrelid) dfltexpr
+ -- INTO v_dt1, v_dt2, v_dt3, v_nullable, v_position, v_identity, v_generated, v_hasdflt, v_dfltexpr
+ -- FROM pg_attribute a JOIN pg_class c ON (a.attrelid = c.oid) JOIN pg_type t ON (a.atttypid = t.oid) LEFT JOIN pg_attrdef ad ON (a.attrelid = ad.adrelid AND a.attnum = ad.adnum)
+ -- WHERE c.relkind in ('r','p') AND a.attnum > 0 AND NOT a.attisdropped AND c.relnamespace::regnamespace::text = in_schema AND c.relname = in_table AND a.attname = in_column;
+ -- RAISE NOTICE 'schema=% table=% column=% dt1=% dt2=% dt3=% nullable=% pos=% identity=% generated=% HasDefault=% DeftExpr=%', in_schema, in_table, in_column, v_dt1,v_dt2,v_dt3,v_nullable,v_position,v_identity,v_generated,v_hasdflt,v_dfltexpr;
END IF;
- RETURN coldef;
+ RETURN v_coldef;
END;
$$;
@@ -57,11 +85,12 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
v_constraintrec record;
v_trigrec record;
v_indexrec record;
- v_primary boolean := False;
+ v_rec record;
v_constraint_name text;
v_constraint_def text;
v_pkey_def text := '';
- v_fkey_defs text;
+ v_fkey_def text := '';
+ v_fkey_defs text := '';
v_trigger text := '';
v_partition_key text := '';
v_partbound text;
@@ -69,6 +98,7 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
v_parent_schema text;
v_persist text;
v_temp text := '';
+ v_temp2 text;
v_relopts text;
v_tablespace text;
v_pgversion int;
@@ -82,17 +112,22 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
bVerbose boolean := False;
v_cnt1 integer;
v_cnt2 integer;
+ search_path_old text := '';
+ search_path_new text := '';
+ v_partial boolean;
+ v_pos integer;
-- assume defaults for ENUMs at the getgo
pkcnt int := 0;
fkcnt int := 0;
trigcnt int := 0;
- pktype tabledefs := 'PKEY_INTERNAL';
- fktype tabledefs := 'FKEYS_INTERNAL';
- trigtype tabledefs := 'NO_TRIGGERS';
+ cmtcnt int := 0;
+ pktype pg_temp.tabledefs := 'PKEY_INTERNAL';
+ fktype pg_temp.tabledefs := 'FKEYS_INTERNAL';
+ trigtype pg_temp.tabledefs := 'NO_TRIGGERS';
arglen integer;
vargs text;
- avarg tabledefs;
+ avarg pg_temp.tabledefs;
-- exception variables
v_ret text;
@@ -120,8 +155,8 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
-- select array_to_string($4, ',', '***') INTO vargs;
IF bVerbose THEN RAISE NOTICE 'arguments=%', $4; END IF;
FOREACH avarg IN ARRAY $4 LOOP
- IF bVerbose THEN RAISE INFO 'arg=%', avarg; END IF;
- IF avarg = 'FKEYS_INTERNAL' OR avarg = 'FKEYS_EXTERNAL' OR avarg = 'FKEYS_COMMENTED' THEN
+ IF bVerbose THEN RAISE NOTICE 'arg=%', avarg; END IF;
+ IF avarg = 'FKEYS_INTERNAL' OR avarg = 'FKEYS_EXTERNAL' OR avarg = 'FKEYS_NONE' THEN
fkcnt = fkcnt + 1;
fktype = avarg;
ELSEIF avarg = 'INCLUDE_TRIGGERS' OR avarg = 'NO_TRIGGERS' THEN
@@ -130,6 +165,9 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
ELSEIF avarg = 'PKEY_EXTERNAL' THEN
pkcnt = pkcnt + 1;
pktype = avarg;
+ ELSEIF avarg = 'COMMENTS' THEN
+ cmtcnt = cmtcnt + 1;
+
END IF;
END LOOP;
IF fkcnt > 1 THEN
@@ -141,12 +179,24 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
ELSEIF pkcnt > 1 THEN
RAISE WARNING 'Only one pkey option can be provided. You provided %', pkcnt;
RETURN '';
+ ELSEIF cmtcnt > 1 THEN
+ RAISE WARNING 'Only one comments option can be provided. You provided %', cmtcnt;
+ RETURN '';
+
END IF;
END IF;
SELECT c.oid, (select setting from pg_settings where name = 'server_version_num') INTO v_table_oid, v_pgversion FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind in ('r','p') AND c.relname = in_table AND n.nspname = in_schema;
-
+
+ -- set search_path = public before we do anything to force explicit schema qualification but dont forget to set it back before exiting...
+ SELECT setting INTO search_path_old FROM pg_settings WHERE name = 'search_path';
+
+ -- RAISE NOTICE 'DEBUG tableddl: saving old search_path: ***%***', search_path_old;
+ EXECUTE 'SET search_path = "public"';
+ SELECT setting INTO search_path_new FROM pg_settings WHERE name = 'search_path';
+ -- RAISE NOTICE 'DEBUG tableddl: using new search path=***%***', search_path_new;
+
-- throw an error if table was not found
IF (v_table_oid IS NULL) THEN
RAISE EXCEPTION 'table does not exist';
@@ -206,7 +256,9 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
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');
--Issue#19 put double-quotes around SQL keyword column names
- SELECT COUNT(*) INTO v_cnt2 FROM pg_get_keywords() WHERE word = v_colrec.column_name AND catcode = 'R';
+ -- Issue#121: fix keyword lookup for table name not column name that does not apply here
+ -- SELECT COUNT(*) INTO v_cnt2 FROM pg_get_keywords() WHERE word = v_colrec.column_name AND catcode = 'R';
+ SELECT COUNT(*) INTO v_cnt2 FROM pg_get_keywords() WHERE word = in_table AND catcode = 'R';
IF bInheritance THEN
-- inheritance-based
@@ -235,7 +287,7 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
-- Jump to constraints and index section to add the check constraints and indexes and perhaps FKeys
END IF;
END IF;
- IF bVerbose THEN RAISE INFO '(1)tabledef so far: %', v_table_ddl; END IF;
+ IF bVerbose THEN RAISE NOTICE '(1)tabledef so far: %', v_table_ddl; END IF;
IF NOT bPartition THEN
-- see if this is unlogged or temporary table
@@ -262,14 +314,15 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
v_table_ddl := 'CREATE ' || v_temp || ' TABLE ' || in_schema || '.' || in_table || ' (' || E'\\n';
END IF;
END IF;
- -- RAISE INFO 'DEBUG2: tabledef so far: %', v_table_ddl;
+ -- RAISE NOTICE 'DEBUG2: tabledef so far: %', v_table_ddl;
-- define all of the columns in the table unless we are in progress creating an inheritance-based child table
IF NOT bPartition THEN
FOR v_colrec IN
- SELECT c.column_name, c.data_type, c.udt_name, c.udt_schema, c.character_maximum_length, c.is_nullable, c.column_default, c.numeric_precision, c.numeric_scale, c.is_identity, c.identity_generation
+ SELECT c.column_name, c.data_type, c.udt_name, c.udt_schema, c.character_maximum_length, c.is_nullable, c.column_default, c.numeric_precision, c.numeric_scale, c.is_identity, c.identity_generation, c.is_generated, c.generation_expression
FROM information_schema.columns c WHERE (table_schema, table_name) = (in_schema, in_table) ORDER BY ordinal_position
LOOP
- IF bVerbose THEN RAISE INFO '(col loop) name=% type=% udt_name=% udt_schema=%', v_colrec.column_name, v_colrec.data_type, v_colrec.udt_name, v_colrec.udt_schema; END IF;
+ IF bVerbose THEN RAISE NOTICE '(col loop) name=% type=% udt_name=% default=% is_generated=% gen_expr=%', v_colrec.column_name, v_colrec.data_type, v_colrec.udt_name, v_colrec.column_default, v_colrec.is_generated, v_colrec.generation_expression; END IF;
+
-- v17 fix: handle case-sensitive for pg_get_serial_sequence that requires SQL Identifier handling
-- SELECT CASE WHEN pg_get_serial_sequence(v_qualified, v_colrec.column_name) IS NOT NULL THEN True ELSE False END into bSerial;
SELECT CASE WHEN pg_get_serial_sequence(quote_ident(in_schema) || '.' || quote_ident(in_table), v_colrec.column_name) IS NOT NULL THEN True ELSE False END into bSerial;
@@ -279,8 +332,8 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
SELECT pg_get_serial_sequence(quote_ident(in_schema) || '.' || quote_ident(in_table), v_colrec.column_name) into v_temp;
IF v_temp IS NULL THEN v_temp = 'NA'; END IF;
SELECT pg_temp.pg_get_coldef(in_schema, in_table,v_colrec.column_name) INTO v_diag1;
- --RAISE NOTICE 'DEBUG table: % Column: % datatype: % Serial=% serialval=% coldef=%', v_qualified, v_colrec.column_name, v_colrec.data_type, bSerial, v_temp, v_diag1;
- --RAISE NOTICE 'DEBUG tabledef: %', v_table_ddl;
+ RAISE NOTICE 'DEBUG table: % Column: % datatype: % Serial=% serialval=% coldef=%', v_qualified, v_colrec.column_name, v_colrec.data_type, bSerial, v_temp, v_diag1;
+ RAISE NOTICE 'DEBUG tabledef: %', v_table_ddl;
END IF;
--Issue#17 put double-quotes around case-sensitive column names
@@ -295,32 +348,64 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
ELSE
v_table_ddl := v_table_ddl || ' ' || v_colrec.column_name || ' ';
END IF;
-
- v_table_ddl := v_table_ddl ||
- CASE WHEN v_colrec.udt_name in ('geometry', 'box2d', 'box2df', 'box3d', 'geography', 'geometry_dump', 'gidx', 'spheroid', 'valid_detail')
- THEN v_colrec.udt_name
- WHEN v_colrec.data_type = 'USER-DEFINED'
- THEN v_colrec.udt_schema || '.' || v_colrec.udt_name
- WHEN v_colrec.data_type = 'ARRAY'
- -- Issue#6 fix: handle arrays
- THEN pg_temp.pg_get_coldef(in_schema, in_table,v_colrec.column_name)
- -- v17 fix: handle case-sensitive for pg_get_serial_sequence that requires SQL Identifier handling
- -- WHEN pg_get_serial_sequence(v_qualified, v_colrec.column_name) IS NOT NULL
- WHEN pg_get_serial_sequence(quote_ident(in_schema) || '.' || quote_ident(in_table), v_colrec.column_name) IS NOT NULL
- -- Issue#8 fix: handle serial. Note: NOT NULL is implied so no need to declare it explicitly
- THEN pg_temp.pg_get_coldef(in_schema, in_table,v_colrec.column_name)
- ELSE v_colrec.data_type END
- || CASE WHEN v_colrec.is_identity = 'YES' THEN CASE WHEN v_colrec.identity_generation = 'ALWAYS' THEN ' GENERATED ALWAYS AS IDENTITY' ELSE ' GENERATED BY DEFAULT AS IDENTITY' END ELSE '' END
- || CASE WHEN v_colrec.character_maximum_length IS NOT NULL THEN ('(' || v_colrec.character_maximum_length || ')')
- WHEN v_colrec.numeric_precision > 0 AND v_colrec.numeric_scale > 0 THEN '(' || v_colrec.numeric_precision || ',' || v_colrec.numeric_scale || ')'
- ELSE '' END || ' '
- || CASE WHEN bSerial THEN '' ELSE CASE WHEN v_colrec.is_nullable = 'NO' THEN 'NOT NULL' ELSE 'NULL' END END
- || CASE WHEN bSerial THEN '' ELSE CASE WHEN v_colrec.column_default IS NOT null THEN (' DEFAULT ' || v_colrec.column_default) ELSE '' END END
- || ',' || E'\\n';
+
+ -- Issue#23: Handle autogenerated columns and rewrite as a simpler IF THEN ELSE branch instead of a much more complex embedded CASE STATEMENT
+ IF v_colrec.is_generated = 'ALWAYS' and v_colrec.generation_expression IS NOT NULL THEN
+ -- searchable tsvector GENERATED ALWAYS AS (to_tsvector('simple'::regconfig, COALESCE(translate(email, '@.-'::citext, ' '::text), ''::text)) ) STORED
+ v_temp = v_colrec.data_type || ' GENERATED ALWAYS AS (' || v_colrec.generation_expression || ') STORED ';
+ ELSEIF v_colrec.udt_name in ('geometry', 'box2d', 'box2df', 'box3d', 'geography', 'geometry_dump', 'gidx', 'spheroid', 'valid_detail') THEN
+ v_temp = v_colrec.udt_name;
+ ELSEIF v_colrec.data_type = 'USER-DEFINED' THEN
+ v_temp = v_colrec.udt_schema || '.' || v_colrec.udt_name;
+ ELSEIF v_colrec.data_type = 'ARRAY' THEN
+ -- Issue#6 fix: handle arrays
+ v_temp = pg_temp.pg_get_coldef(in_schema, in_table,v_colrec.column_name);
+ -- v17 fix: handle case-sensitive for pg_get_serial_sequence that requires SQL Identifier handling
+ -- WHEN pg_get_serial_sequence(v_qualified, v_colrec.column_name) IS NOT NULL
+ ELSEIF pg_get_serial_sequence(quote_ident(in_schema) || '.' || quote_ident(in_table), v_colrec.column_name) IS NOT NULL THEN
+ -- Issue#8 fix: handle serial. Note: NOT NULL is implied so no need to declare it explicitly
+ v_temp = pg_temp.pg_get_coldef(in_schema, in_table,v_colrec.column_name);
+ ELSE
+ v_temp = v_colrec.data_type;
+ END IF;
+ -- RAISE NOTICE 'column def1=%', v_temp;
+
+ -- handle IDENTITY columns
+ IF v_colrec.is_identity = 'YES' THEN
+ IF v_colrec.identity_generation = 'ALWAYS' THEN
+ v_temp = v_temp || ' GENERATED ALWAYS AS IDENTITY';
+ ELSE
+ v_temp = v_temp || ' GENERATED BY DEFAULT AS IDENTITY';
+ END IF;
+ ELSEIF v_colrec.character_maximum_length IS NOT NULL THEN
+ v_temp = v_temp || ('(' || v_colrec.character_maximum_length || ')');
+ ELSEIF v_colrec.numeric_precision > 0 AND v_colrec.numeric_scale > 0 THEN
+ v_temp = v_temp || '(' || v_colrec.numeric_precision || ',' || v_colrec.numeric_scale || ')';
+ END IF;
+
+ -- Handle NULL/NOT NULL
+ IF bSerial THEN
+ v_temp = v_temp || ' NOT NULL';
+ ELSEIF v_colrec.is_nullable = 'NO' THEN
+ v_temp = v_temp || ' NOT NULL';
+ ELSEIF v_colrec.is_nullable = 'YES' THEN
+ v_temp = v_temp || ' NULL';
+ END IF;
+
+ -- Handle defaults
+ IF v_colrec.column_default IS NOT null AND NOT bSerial THEN
+ -- RAISE NOTICE 'Setting default for column, %', v_colrec.column_name;
+ v_temp = v_temp || (' DEFAULT ' || v_colrec.column_default);
+ END IF;
+ v_temp = v_temp || ',' || E'\\n';
+ -- RAISE NOTICE 'column def2=%', v_temp;
+ v_table_ddl := v_table_ddl || v_temp;
+ -- RAISE NOTICE 'tabledef=%', v_table_ddl;
+
END LOOP;
END IF;
- IF bVerbose THEN RAISE INFO '(2)tabledef so far: %', v_table_ddl; END IF;
-
+ IF bVerbose THEN RAISE NOTICE '(2)tabledef so far: %', v_table_ddl; END IF;
+
-- define all the constraints: conparentid does not exist pre PGv11
IF v_pgversion < 110000 THEN
FOR v_constraintrec IN
@@ -335,42 +420,61 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
pg_get_constraintdef(con.oid) as constraint_definition
FROM pg_catalog.pg_constraint con JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace
WHERE nsp.nspname = in_schema AND rel.relname = in_table ORDER BY type_rank
- LOOP
+ LOOP
+ v_constraint_name := v_constraintrec.constraint_name;
+ v_constraint_def := v_constraintrec.constraint_definition;
IF v_constraintrec.type_rank = 1 THEN
- v_primary := True;
- IF pkcnt = 0 THEN
+ IF pkcnt = 0 OR pktype = 'PKEY_INTERNAL' THEN
+ -- internal def
v_constraint_name := v_constraintrec.constraint_name;
v_constraint_def := v_constraintrec.constraint_definition;
+ v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
+ || 'CONSTRAINT' || ' '
+ || v_constraint_name || ' '
+ || v_constraint_def
+ || ',' || E'\\n';
ELSE
-- Issue#16 handle external PG def
- v_constraint_name := v_constraintrec.constraint_name;
SELECT 'ALTER TABLE ONLY ' || in_schema || '.' || c.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' INTO v_pkey_def
- FROM pg_catalog.pg_constraint r, pg_class c, pg_namespace n where r.conrelid = c.oid and r.contype = 'p' and n.oid = r.connamespace and n.nspname = in_schema AND c.relname = in_table;
+ FROM pg_catalog.pg_constraint r, pg_class c, pg_namespace n where r.conrelid = c.oid and r.contype = 'p' and n.oid = r.connamespace and n.nspname = in_schema AND c.relname = in_table and r.conname = v_constraint_name;
END IF;
IF bPartition THEN
continue;
END IF;
+ ELSIF v_constraintrec.type_rank = 3 THEN
+ -- handle foreign key constraints
+ --Issue#22 fix: added FKEY_NONE check
+ IF fktype = 'FKEYS_NONE' THEN
+ -- skip
+ continue;
+ ELSIF fkcnt = 0 OR fktype = 'FKEYS_INTERNAL' THEN
+ -- internal def
+ v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
+ || 'CONSTRAINT' || ' '
+ || v_constraint_name || ' '
+ || v_constraint_def
+ || ',' || E'\\n';
+ ELSE
+ -- external def
+ SELECT 'ALTER TABLE ONLY ' || n.nspname || '.' || c2.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' INTO v_fkey_def
+ FROM pg_constraint r, pg_class c1, pg_namespace n, pg_class c2 where r.conrelid = c1.oid and r.contype = 'f' and n.nspname = in_schema and n.oid = r.connamespace and r.conrelid = c2.oid and c2.relname = in_table;
+ v_fkey_defs = v_fkey_defs || v_fkey_def || E'\\n';
+ END IF;
ELSE
- v_constraint_name := v_constraintrec.constraint_name;
- v_constraint_def := v_constraintrec.constraint_definition;
+ -- handle all other constraints besides PKEY and FKEYS as internal defs by default
+ v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
+ || 'CONSTRAINT' || ' '
+ || v_constraint_name || ' '
+ || v_constraint_def
+ || ',' || E'\\n';
END IF;
- if bVerbose THEN RAISE INFO 'DEBUG4: constraint name=% constraint_def=%', v_constraint_name,v_constraint_def; END IF;
+ if bVerbose THEN RAISE NOTICE 'DEBUG4: constraint name=% constraint_def=%', v_constraint_name,v_constraint_def; END IF;
constraintarr := constraintarr || v_constraintrec.constraint_name:: text;
- IF fktype <> 'FKEYS_INTERNAL' AND v_constraintrec.constraint_type = 'f' THEN
- continue;
- END IF;
-
- IF pkcnt = 0 THEN
- v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
- || 'CONSTRAINT' || ' '
- || v_constraint_name || ' '
- || v_constraint_def
- || ',' || E'\\n';
- END IF;
END LOOP;
-
ELSE
+ -- handle PG versions 11 and up
+ -- Issue#20: Fix logic for external PKEY and FKEYS
FOR v_constraintrec IN
SELECT con.conname as constraint_name, con.contype as constraint_type,
CASE
@@ -386,51 +490,73 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
--Issue#13 added this condition:
AND con.conparentid = 0
ORDER BY type_rank
- LOOP
+ LOOP
+ v_constraint_name := v_constraintrec.constraint_name;
+ v_constraint_def := v_constraintrec.constraint_definition;
IF v_constraintrec.type_rank = 1 THEN
- v_primary := True;
- IF pkcnt = 0 THEN
+ IF pkcnt = 0 OR pktype = 'PKEY_INTERNAL' THEN
+ -- internal def
v_constraint_name := v_constraintrec.constraint_name;
v_constraint_def := v_constraintrec.constraint_definition;
+ v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
+ || 'CONSTRAINT' || ' '
+ || v_constraint_name || ' '
+ || v_constraint_def
+ || ',' || E'\\n';
ELSE
-- Issue#16 handle external PG def
- v_constraint_name := v_constraintrec.constraint_name;
SELECT 'ALTER TABLE ONLY ' || in_schema || '.' || c.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' INTO v_pkey_def
FROM pg_catalog.pg_constraint r, pg_class c, pg_namespace n where r.conrelid = c.oid and r.contype = 'p' and n.oid = r.connamespace and n.nspname = in_schema AND c.relname = in_table;
END IF;
IF bPartition THEN
continue;
- END IF;
+ END IF;
+ ELSIF v_constraintrec.type_rank = 3 THEN
+ -- handle foreign key constraints
+ --Issue#22 fix: added FKEY_NONE check
+ IF fktype = 'FKEYS_NONE' THEN
+ -- skip
+ continue;
+ ELSIF fkcnt = 0 OR fktype = 'FKEYS_INTERNAL' THEN
+ -- internal def
+ v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
+ || 'CONSTRAINT' || ' '
+ || v_constraint_name || ' '
+ || v_constraint_def
+ || ',' || E'\\n';
+ ELSE
+ -- external def
+ SELECT 'ALTER TABLE ONLY ' || n.nspname || '.' || c2.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' INTO v_fkey_def
+ FROM pg_constraint r, pg_class c1, pg_namespace n, pg_class c2 where r.conrelid = c1.oid and r.contype = 'f' and n.nspname = in_schema and n.oid = r.connamespace and r.conrelid = c2.oid and c2.relname = in_table and
+ r.conname = v_constraint_name and r.conparentid = 0;
+ v_fkey_defs = v_fkey_defs || v_fkey_def || E'\\n';
+ END IF;
ELSE
- v_constraint_name := v_constraintrec.constraint_name;
- v_constraint_def := v_constraintrec.constraint_definition;
+ -- handle all other constraints besides PKEY and FKEYS as internal defs by default
+ v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
+ || 'CONSTRAINT' || ' '
+ || v_constraint_name || ' '
+ || v_constraint_def
+ || ',' || E'\\n';
END IF;
- -- SELECT 'ALTER TABLE ONLY ' || c.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' as pkeyddl FROM pg_catalog.pg_constraint r, pg_class c, pg_namespace n where r.conrelid = c.oid and r.contype = 'p' and n.oid = r.connamespace and n.nspname = 'sample' AND c.relname = 'extensions_table';
- if bVerbose THEN RAISE INFO 'DEBUG4: constraint name=% constraint_def=%', v_constraint_name,v_constraint_def; END IF;
+ if bVerbose THEN RAISE NOTICE 'DEBUG4: constraint name=% constraint_def=%', v_constraint_name,v_constraint_def; END IF;
constraintarr := constraintarr || v_constraintrec.constraint_name:: text;
- IF fktype <> 'FKEYS_INTERNAL' AND v_constraintrec.constraint_type = 'f' THEN
- continue;
- END IF;
-
- IF pkcnt = 0 THEN
- v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
- || 'CONSTRAINT' || ' '
- || v_constraint_name || ' '
- || v_constraint_def
- || ',' || E'\\n';
- END IF;
END LOOP;
END IF;
- IF bVerbose THEN RAISE INFO '(3)tabledef so far: %', v_table_ddl; END IF;
- -- drop the last comma before ending the create statement
- v_table_ddl = substr(v_table_ddl, 0, length(v_table_ddl) - 1) || E'\\n';
+ -- drop the last comma before ending the create statement, which should be right before the carriage return character
+ -- Issue#24: make sure the comma is there before removing it
+ select substring(v_table_ddl, length(v_table_ddl) - 1, 1) INTO v_temp;
+ IF v_temp = ',' THEN
+ v_table_ddl = substr(v_table_ddl, 0, length(v_table_ddl) - 1) || E'\\n';
+ END IF;
+ IF bVerbose THEN RAISE NOTICE '(3)tabledef so far: %', trim(v_table_ddl); END IF;
-- ---------------------------------------------------------------------------
-- at this point we have everything up to the last table-enclosing parenthesis
-- ---------------------------------------------------------------------------
- IF bVerbose THEN RAISE INFO '(4)tabledef so far: %', v_table_ddl; END IF;
+ IF bVerbose THEN RAISE NOTICE '(4)tabledef so far: %', v_table_ddl; END IF;
-- See if this is an inheritance-based child table and finish up the table create.
IF bPartition and bInheritance THEN
@@ -458,7 +584,7 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
END IF;
END IF;
- IF bVerbose THEN RAISE INFO '(5)tabledef so far: %', v_table_ddl; END IF;
+ IF bVerbose THEN RAISE NOTICE '(5)tabledef so far: %', v_table_ddl; END IF;
-- Add closing paren for regular tables
-- IF NOT bPartition THEN
@@ -471,19 +597,24 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
v_table_ddl := v_table_ddl || v_pkey_def || E'\\n';
END IF;
- IF bVerbose THEN RAISE INFO '(6)tabledef so far: %', v_table_ddl; END IF;
+ -- Issue#20
+ IF v_fkey_defs <> '' THEN
+ v_table_ddl := v_table_ddl || v_fkey_defs || E'\\n';
+ END IF;
+
+ IF bVerbose THEN RAISE NOTICE '(6)tabledef so far: %', v_table_ddl; END IF;
-- create indexes
FOR v_indexrec IN
SELECT indexdef, COALESCE(tablespace, 'pg_default') as tablespace, indexname FROM pg_indexes WHERE (schemaname, tablename) = (in_schema, in_table)
LOOP
- -- RAISE INFO 'DEBUG6: indexname=%', v_indexrec.indexname;
+ -- RAISE NOTICE 'DEBUG6: indexname=% indexdef=%', v_indexrec.indexname, v_indexrec.indexdef;
-- loop through constraints and skip ones already defined
bSkip = False;
FOREACH constraintelement IN ARRAY constraintarr
LOOP
IF constraintelement = v_indexrec.indexname THEN
- -- RAISE INFO 'DEBUG7: skipping index, %', v_indexrec.indexname;
+ -- RAISE NOTICE 'DEBUG7: skipping index, %', v_indexrec.indexname;
bSkip = True;
EXIT;
END IF;
@@ -492,48 +623,47 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
-- Add IF NOT EXISTS clause so partition index additions will not be created if declarative partition in effect and index already created on parent
v_indexrec.indexdef := REPLACE(v_indexrec.indexdef, 'CREATE INDEX', 'CREATE INDEX IF NOT EXISTS');
- -- RAISE INFO 'DEBUG8: adding index, %', v_indexrec.indexname;
+ -- Fix Issue#26: do it for unique/primary key indexes as well
+ v_indexrec.indexdef := REPLACE(v_indexrec.indexdef, 'CREATE UNIQUE INDEX', 'CREATE UNIQUE INDEX IF NOT EXISTS');
+ -- RAISE NOTICE 'DEBUG8: adding index, %', v_indexrec.indexname;
-- NOTE: cannot specify default tablespace for partitioned relations
IF v_partition_key IS NOT NULL AND v_partition_key <> '' THEN
v_table_ddl := v_table_ddl || v_indexrec.indexdef || ';' || E'\\n';
ELSE
- v_table_ddl := v_table_ddl || v_indexrec.indexdef || ' TABLESPACE ' || v_indexrec.tablespace || ';' || E'\\n';
+ -- Issue#25: see if partial index or not
+ select CASE WHEN i.indpred IS NOT NULL THEN True ELSE False END INTO v_partial
+ FROM pg_index i JOIN pg_class c1 ON (i.indexrelid = c1.oid) JOIN pg_class c2 ON (i.indrelid = c2.oid)
+ WHERE c1.relnamespace::regnamespace::text = in_schema AND c2.relnamespace::regnamespace::text = in_schema AND c2.relname = in_table AND c1.relname = v_indexrec.indexname;
+ IF v_partial THEN
+ -- Put tablespace def before WHERE CLAUSE
+ v_temp = v_indexrec.indexdef;
+ v_pos = POSITION(' WHERE ' IN v_temp);
+ v_temp2 = SUBSTRING(v_temp, v_pos);
+ v_temp = SUBSTRING(v_temp, 1, v_pos);
+ v_table_ddl := v_table_ddl || v_temp || ' TABLESPACE ' || v_indexrec.tablespace || v_temp2 || ';' || E'\\n';
+ ELSE
+ v_table_ddl := v_table_ddl || v_indexrec.indexdef || ' TABLESPACE ' || v_indexrec.tablespace || ';' || E'\\n';
+ END IF;
END IF;
END LOOP;
- IF bVerbose THEN RAISE INFO '(7)tabledef so far: %', v_table_ddl; END IF;
-
- -- Handle external foreign key defs here if applicable.
- IF fktype = 'FKEYS_EXTERNAL' THEN
- -- Issue#13 fix here too for conparentid = 0. and had to change to a loop to handle multiple return set, not a select into variable syntax.
- -- Also had to account for PG V10 where there is no conparentid
- IF v_pgversion < 110000 THEN
- FOR v_constraintrec IN
- SELECT 'ALTER TABLE ONLY ' || n.nspname || '.' || c2.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' as fkeydef
- FROM pg_constraint r, pg_class c1, pg_namespace n, pg_class c2 where r.conrelid = c1.oid and r.contype = 'f' and n.nspname = in_schema and n.oid = r.connamespace and r.conrelid = c2.oid and c2.relname = in_table
+ IF bVerbose THEN RAISE NOTICE '(7)tabledef so far: %', v_table_ddl; END IF;
+
+ -- Issue#20: added logic for table and column comments
+ IF cmtcnt > 0 THEN
+ FOR v_rec IN
+ SELECT c.relname, 'COMMENT ON ' || CASE WHEN c.relkind in ('r','p') AND a.attname IS NULL THEN 'TABLE ' WHEN c.relkind in ('r','p') AND a.attname IS NOT NULL THEN 'COLUMN ' WHEN c.relkind = 'f' THEN 'FOREIGN TABLE '
+ WHEN c.relkind = 'm' THEN 'MATERIALIZED VIEW ' WHEN c.relkind = 'v' THEN 'VIEW ' WHEN c.relkind = 'i' THEN 'INDEX ' WHEN c.relkind = 'S' THEN 'SEQUENCE ' ELSE 'XX' END || n.nspname || '.' ||
+ CASE WHEN c.relkind in ('r','p') AND a.attname IS NOT NULL THEN quote_ident(c.relname) || '.' || a.attname ELSE quote_ident(c.relname) END || ' IS ' || quote_literal(d.description) || ';' as ddl
+ FROM pg_class c JOIN pg_namespace n ON (n.oid = c.relnamespace) LEFT JOIN pg_description d ON (c.oid = d.objoid) LEFT JOIN pg_attribute a ON (c.oid = a.attrelid AND a.attnum > 0 and a.attnum = d.objsubid)
+ WHERE d.description IS NOT NULL AND n.nspname = in_schema AND c.relname = in_table ORDER BY 2 desc, ddl
LOOP
- v_table_ddl := v_table_ddl || v_constraintrec.fkeydef || ';' || E'\\n';
- IF bVerbose THEN RAISE INFO 'keydef = %', v_constraintrec.fkeydef; END IF;
- END LOOP;
- ELSE
- FOR v_constraintrec IN
- SELECT 'ALTER TABLE ONLY ' || n.nspname || '.' || c2.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' as fkeydef
- FROM pg_constraint r, pg_class c1, pg_namespace n, pg_class c2 where r.conrelid = c1.oid and r.contype = 'f' and n.nspname = in_schema and n.oid = r.connamespace and r.conrelid = c2.oid and c2.relname = in_table and r.conparentid = 0
- LOOP
- v_table_ddl := v_table_ddl || v_constraintrec.fkeydef || E'\\n';
- IF bVerbose THEN RAISE INFO 'keydef = %', v_constraintrec.fkeydef; END IF;
- END LOOP;
- END IF;
-
- ELSIF fktype = 'FKEYS_COMMENTED' THEN
- SELECT '-- ALTER TABLE ONLY ' || n.nspname || '.' || c2.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' into v_fkey_defs
- FROM pg_constraint r, pg_class c1, pg_namespace n, pg_class c2 where r.conrelid = c1.oid and r.contype = 'f' and n.nspname = in_schema and n.oid = r.connamespace and r.conrelid = c2.oid and c2.relname = in_table;
- IF v_fkey_defs IS NOT NULL THEN
- v_table_ddl := v_table_ddl || v_fkey_defs;
- END IF;
+ --RAISE NOTICE 'comments:%', v_rec.ddl;
+ v_table_ddl = v_table_ddl || v_rec.ddl || E'\\n';
+ END LOOP;
END IF;
- IF bVerbose THEN RAISE INFO '(8)tabledef so far: %', v_table_ddl; END IF;
+ IF bVerbose THEN RAISE NOTICE '(8)tabledef so far: %', v_table_ddl; END IF;
IF trigtype = 'INCLUDE_TRIGGERS' THEN
-- Issue#14: handle multiple triggers for a table
@@ -543,12 +673,21 @@ export const CREATE_PG_GET_TABLEDEF_SQL = minify(
LOOP
v_table_ddl := v_table_ddl || v_trigrec.triggerdef;
v_table_ddl := v_table_ddl || E'\\n';
- IF bVerbose THEN RAISE INFO 'triggerdef = %', v_trigrec.triggerdef; END IF;
+ IF bVerbose THEN RAISE NOTICE 'triggerdef = %', v_trigrec.triggerdef; END IF;
END LOOP;
END IF;
+ IF bVerbose THEN RAISE NOTICE '(9)tabledef so far: %', v_table_ddl; END IF;
-- add empty line
v_table_ddl := v_table_ddl || E'\\n';
+ IF bVerbose THEN RAISE NOTICE '(10)tabledef so far: %', v_table_ddl; END IF;
+
+ -- reset search_path back to what it was
+ IF search_path_old = '' THEN
+ SELECT set_config('search_path', '', false) into v_temp;
+ ELSE
+ EXECUTE 'SET search_path = ' || search_path_old;
+ END IF;
RETURN v_table_ddl;
diff --git a/apps/studio/pages/project/[ref]/logs/edge-logs.tsx b/apps/studio/pages/project/[ref]/logs/edge-logs.tsx
index 59055f4de34..923dca7a204 100644
--- a/apps/studio/pages/project/[ref]/logs/edge-logs.tsx
+++ b/apps/studio/pages/project/[ref]/logs/edge-logs.tsx
@@ -19,6 +19,6 @@ export const LogPage: NextPageWithLayout = () => {
)
}
-LogPage.getLayout = (page) =>
{page}
+LogPage.getLayout = (page) =>
{page}
export default LogPage
diff --git a/apps/studio/pages/project/[ref]/logs/pooler-logs.tsx b/apps/studio/pages/project/[ref]/logs/pooler-logs.tsx
index 8275ea21191..37c02c2e667 100644
--- a/apps/studio/pages/project/[ref]/logs/pooler-logs.tsx
+++ b/apps/studio/pages/project/[ref]/logs/pooler-logs.tsx
@@ -23,6 +23,6 @@ export const LogPage: NextPageWithLayout = () => {
)
}
-LogPage.getLayout = (page) =>
{page}
+LogPage.getLayout = (page) =>
{page}
export default LogPage
diff --git a/apps/studio/pages/project/[ref]/logs/postgres-logs.tsx b/apps/studio/pages/project/[ref]/logs/postgres-logs.tsx
index afddad783a4..b029ee105c3 100644
--- a/apps/studio/pages/project/[ref]/logs/postgres-logs.tsx
+++ b/apps/studio/pages/project/[ref]/logs/postgres-logs.tsx
@@ -19,6 +19,6 @@ export const LogPage: NextPageWithLayout = () => {
)
}
-LogPage.getLayout = (page) =>
{page}
+LogPage.getLayout = (page) =>
{page}
export default LogPage
diff --git a/apps/studio/pages/project/[ref]/logs/postgrest-logs.tsx b/apps/studio/pages/project/[ref]/logs/postgrest-logs.tsx
index 29acc7044db..d75a9fe7189 100644
--- a/apps/studio/pages/project/[ref]/logs/postgrest-logs.tsx
+++ b/apps/studio/pages/project/[ref]/logs/postgrest-logs.tsx
@@ -19,6 +19,6 @@ export const LogPage: NextPageWithLayout = () => {
)
}
-LogPage.getLayout = (page) =>
{page}
+LogPage.getLayout = (page) =>
{page}
export default LogPage
diff --git a/apps/studio/pages/project/[ref]/logs/realtime-logs.tsx b/apps/studio/pages/project/[ref]/logs/realtime-logs.tsx
index 6dfcdb2a72b..636ffa945da 100644
--- a/apps/studio/pages/project/[ref]/logs/realtime-logs.tsx
+++ b/apps/studio/pages/project/[ref]/logs/realtime-logs.tsx
@@ -19,6 +19,6 @@ export const LogPage: NextPageWithLayout = () => {
)
}
-LogPage.getLayout = (page) =>
{page}
+LogPage.getLayout = (page) =>
{page}
export default LogPage
diff --git a/apps/studio/pages/project/[ref]/logs/storage-logs.tsx b/apps/studio/pages/project/[ref]/logs/storage-logs.tsx
index e0f63ee0271..41a65f98281 100644
--- a/apps/studio/pages/project/[ref]/logs/storage-logs.tsx
+++ b/apps/studio/pages/project/[ref]/logs/storage-logs.tsx
@@ -19,6 +19,6 @@ export const LogPage: NextPageWithLayout = () => {
)
}
-LogPage.getLayout = (page) =>
{page}
+LogPage.getLayout = (page) =>
{page}
export default LogPage
diff --git a/apps/studio/styles/monaco.scss b/apps/studio/styles/monaco.scss
index 9abbd76e8db..7ebacfe0974 100644
--- a/apps/studio/styles/monaco.scss
+++ b/apps/studio/styles/monaco.scss
@@ -47,6 +47,10 @@ span.th.tr.td.textarea {
cursor: inherit;
}
+.find-widget {
+ margin-right: 30px;
+}
+
.grid-monaco-editor,
.monaco-editor .suggest-widget {
.main {
diff --git a/apps/www/components/Hero/Hero.tsx b/apps/www/components/Hero/Hero.tsx
index e7bdbfe1d26..4aeb48315bc 100644
--- a/apps/www/components/Hero/Hero.tsx
+++ b/apps/www/components/Hero/Hero.tsx
@@ -5,7 +5,6 @@ import { useTelemetryProps } from 'common/hooks/useTelemetryProps'
import gaEvents from '~/lib/gaEvents'
import { Button, IconBookOpen } from 'ui'
import SectionContainer from '~/components/Layouts/SectionContainer'
-import HeroFrameworks from './HeroFrameworks'
const Hero = () => {
const router = useRouter()
@@ -57,8 +56,6 @@ const Hero = () => {
-
-