mirror of
https://github.com/chartdb/chartdb.git
synced 2026-02-09 13:14:31 -06:00
fix: PostgreSQL serial type parsing (#1016)
* fix: PostgreSQL serial type parsing * fix
This commit is contained in:
3959
package-lock.json
generated
3959
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,8 @@ import { cn } from '@/lib/utils';
|
||||
import { badgeVariants } from './badge-variants';
|
||||
|
||||
export interface BadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
extends
|
||||
React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof badgeVariants> {}
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
|
||||
@@ -27,7 +27,8 @@ export interface ButtonAlternative {
|
||||
}
|
||||
|
||||
export interface ButtonWithAlternativesProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
extends
|
||||
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean;
|
||||
alternatives: Array<ButtonAlternative>;
|
||||
|
||||
@@ -6,7 +6,8 @@ import { cn } from '@/lib/utils';
|
||||
import { buttonVariants } from './button-variants';
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
extends
|
||||
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean;
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@ import {
|
||||
import type { DatabaseType } from '@/lib/domain/database-type';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface DiagramIconProps
|
||||
extends React.ComponentPropsWithoutRef<'div'> {
|
||||
export interface DiagramIconProps extends React.ComponentPropsWithoutRef<'div'> {
|
||||
databaseType: DatabaseType;
|
||||
databaseEdition?: DatabaseEdition;
|
||||
imgClassName?: string;
|
||||
|
||||
@@ -45,7 +45,8 @@ const sheetVariants = cva(
|
||||
);
|
||||
|
||||
interface SheetContentProps
|
||||
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
||||
extends
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
||||
VariantProps<typeof sheetVariants> {}
|
||||
|
||||
const SheetContent = React.forwardRef<
|
||||
|
||||
@@ -30,7 +30,8 @@ const loaderVariants = cva('animate-spin text-primary', {
|
||||
});
|
||||
|
||||
interface SpinnerContentProps
|
||||
extends VariantProps<typeof spinnerVariants>,
|
||||
extends
|
||||
VariantProps<typeof spinnerVariants>,
|
||||
VariantProps<typeof loaderVariants> {
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
|
||||
@@ -2,8 +2,7 @@ import React from 'react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface TextareaProps
|
||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
|
||||
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||
({ className, ...props }, ref) => {
|
||||
|
||||
@@ -2,8 +2,7 @@ import React from 'react';
|
||||
import { Skeleton } from '../skeleton/skeleton';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface TreeItemSkeletonProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {}
|
||||
export interface TreeItemSkeletonProps extends React.HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
export const TreeItemSkeleton: React.FC<TreeItemSkeletonProps> = ({
|
||||
className,
|
||||
|
||||
@@ -64,9 +64,8 @@ export const SmartQueryInstructions: React.FC<SmartQueryInstructionsProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
const loadScripts = async () => {
|
||||
const { importMetadataScripts } = await import(
|
||||
'@/lib/data/import-metadata/scripts/scripts'
|
||||
);
|
||||
const { importMetadataScripts } =
|
||||
await import('@/lib/data/import-metadata/scripts/scripts');
|
||||
setImportMetadataScripts(importMetadataScripts);
|
||||
};
|
||||
loadScripts();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { fixMetadataJson, isStringMetadataJson } from '../utils';
|
||||
|
||||
describe('fixMetadataJson', () => {
|
||||
@@ -280,6 +280,10 @@ describe('isStringMetadataJson', () => {
|
||||
});
|
||||
|
||||
it('should return false for valid JSON but missing required fields', () => {
|
||||
const consoleErrorSpy = vi
|
||||
.spyOn(console, 'error')
|
||||
.mockImplementation(() => {});
|
||||
|
||||
const incompleteMetadata = JSON.stringify({
|
||||
fk_info: [],
|
||||
pk_info: [],
|
||||
@@ -287,6 +291,8 @@ describe('isStringMetadataJson', () => {
|
||||
});
|
||||
|
||||
expect(isStringMetadataJson(incompleteMetadata)).toBe(false);
|
||||
|
||||
consoleErrorSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should return false for empty string', () => {
|
||||
@@ -294,8 +300,14 @@ describe('isStringMetadataJson', () => {
|
||||
});
|
||||
|
||||
it('should return false for null-like values', () => {
|
||||
const consoleErrorSpy = vi
|
||||
.spyOn(console, 'error')
|
||||
.mockImplementation(() => {});
|
||||
|
||||
expect(isStringMetadataJson('null')).toBe(false);
|
||||
expect(isStringMetadataJson('undefined')).toBe(false);
|
||||
|
||||
consoleErrorSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { fromMySQL } from '../mysql';
|
||||
|
||||
describe('MySQL Default Value Import', () => {
|
||||
@@ -173,6 +173,10 @@ describe('MySQL Default Value Import', () => {
|
||||
|
||||
describe('Complex Real-World Example', () => {
|
||||
it('should handle complex table with multiple default types', async () => {
|
||||
const consoleErrorSpy = vi
|
||||
.spyOn(console, 'error')
|
||||
.mockImplementation(() => {});
|
||||
|
||||
const sql = `
|
||||
CREATE TABLE adventurer_profiles (
|
||||
adventurer_id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
@@ -223,6 +227,8 @@ describe('MySQL Default Value Import', () => {
|
||||
(c) => c.name === 'inventory_data'
|
||||
);
|
||||
expect(inventoryColumn?.default).toBe('NULL');
|
||||
|
||||
consoleErrorSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -433,8 +433,7 @@ export async function fromMySQL(sqlContent: string): Promise<SQLParserResult> {
|
||||
colName
|
||||
);
|
||||
if (col) {
|
||||
col.primaryKey =
|
||||
true;
|
||||
col.primaryKey = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ describe('PostgreSQL ALTER TABLE ADD COLUMN Tests', () => {
|
||||
(col) => col.name === 'price'
|
||||
);
|
||||
expect(priceColumn).toBeDefined();
|
||||
expect(priceColumn?.default).toBe('0');
|
||||
expect(priceColumn?.default).toBe('0.00');
|
||||
});
|
||||
|
||||
it('should not add duplicate columns', async () => {
|
||||
|
||||
@@ -843,6 +843,12 @@ export async function fromPostgres(
|
||||
normalizedBaseType = 'INTEGER';
|
||||
isSerialType = true;
|
||||
}
|
||||
} else if (upperType === 'SMALLSERIAL') {
|
||||
normalizedBaseType = 'SMALLINT';
|
||||
isSerialType = true;
|
||||
} else if (upperType === 'BIGSERIAL') {
|
||||
normalizedBaseType = 'BIGINT';
|
||||
isSerialType = true;
|
||||
} else if (upperType === 'INT') {
|
||||
// Use length to determine the actual int type
|
||||
if (typeLength === 2) {
|
||||
@@ -862,12 +868,17 @@ export async function fromPostgres(
|
||||
// Now handle parameters - but skip for integer types that shouldn't have them
|
||||
let finalDataType = normalizedBaseType;
|
||||
|
||||
// Don't add parameters to INTEGER types that come from int4, int8, etc.
|
||||
// Don't add parameters to INTEGER types that come from int4, int8, serial types, etc.
|
||||
const isNormalizedIntegerType =
|
||||
['INTEGER', 'BIGINT', 'SMALLINT'].includes(
|
||||
normalizedBaseType
|
||||
) &&
|
||||
(upperType === 'INT' || upperType === 'SERIAL');
|
||||
[
|
||||
'INT',
|
||||
'SERIAL',
|
||||
'SMALLSERIAL',
|
||||
'BIGSERIAL',
|
||||
].includes(upperType);
|
||||
|
||||
if (!isSerialType && !isNormalizedIntegerType) {
|
||||
// Include precision/scale/length in the type string if available
|
||||
|
||||
@@ -9,9 +9,8 @@ const routes: RouteObject[] = [
|
||||
...['', 'diagrams/:diagramId'].map((path) => ({
|
||||
path,
|
||||
async lazy() {
|
||||
const { EditorPage } = await import(
|
||||
'./pages/editor-page/editor-page'
|
||||
);
|
||||
const { EditorPage } =
|
||||
await import('./pages/editor-page/editor-page');
|
||||
|
||||
return {
|
||||
element: <EditorPage />,
|
||||
@@ -21,9 +20,8 @@ const routes: RouteObject[] = [
|
||||
{
|
||||
path: 'examples',
|
||||
async lazy() {
|
||||
const { ExamplesPage } = await import(
|
||||
'./pages/examples-page/examples-page'
|
||||
);
|
||||
const { ExamplesPage } =
|
||||
await import('./pages/examples-page/examples-page');
|
||||
return {
|
||||
element: <ExamplesPage />,
|
||||
};
|
||||
@@ -33,9 +31,8 @@ const routes: RouteObject[] = [
|
||||
id: 'templates',
|
||||
path: 'templates',
|
||||
async lazy() {
|
||||
const { TemplatesPage } = await import(
|
||||
'./pages/templates-page/templates-page'
|
||||
);
|
||||
const { TemplatesPage } =
|
||||
await import('./pages/templates-page/templates-page');
|
||||
return {
|
||||
element: <TemplatesPage />,
|
||||
};
|
||||
@@ -54,9 +51,8 @@ const routes: RouteObject[] = [
|
||||
id: 'templates_featured',
|
||||
path: 'templates/featured',
|
||||
async lazy() {
|
||||
const { TemplatesPage } = await import(
|
||||
'./pages/templates-page/templates-page'
|
||||
);
|
||||
const { TemplatesPage } =
|
||||
await import('./pages/templates-page/templates-page');
|
||||
return {
|
||||
element: <TemplatesPage />,
|
||||
};
|
||||
@@ -76,9 +72,8 @@ const routes: RouteObject[] = [
|
||||
id: 'templates_tags',
|
||||
path: 'templates/tags/:tag',
|
||||
async lazy() {
|
||||
const { TemplatesPage } = await import(
|
||||
'./pages/templates-page/templates-page'
|
||||
);
|
||||
const { TemplatesPage } =
|
||||
await import('./pages/templates-page/templates-page');
|
||||
return {
|
||||
element: <TemplatesPage />,
|
||||
};
|
||||
@@ -98,17 +93,15 @@ const routes: RouteObject[] = [
|
||||
id: 'templates_templateSlug',
|
||||
path: 'templates/:templateSlug',
|
||||
async lazy() {
|
||||
const { TemplatePage } = await import(
|
||||
'./pages/template-page/template-page'
|
||||
);
|
||||
const { TemplatePage } =
|
||||
await import('./pages/template-page/template-page');
|
||||
return {
|
||||
element: <TemplatePage />,
|
||||
};
|
||||
},
|
||||
loader: async ({ params }): Promise<TemplatePageLoaderData> => {
|
||||
const { templates } = await import(
|
||||
'./templates-data/templates-data'
|
||||
);
|
||||
const { templates } =
|
||||
await import('./templates-data/templates-data');
|
||||
return {
|
||||
template: templates.find(
|
||||
(template) => template.slug === params.templateSlug
|
||||
@@ -120,17 +113,15 @@ const routes: RouteObject[] = [
|
||||
id: 'templates_load',
|
||||
path: 'templates/clone/:templateSlug',
|
||||
async lazy() {
|
||||
const { CloneTemplatePage } = await import(
|
||||
'./pages/clone-template-page/clone-template-page'
|
||||
);
|
||||
const { CloneTemplatePage } =
|
||||
await import('./pages/clone-template-page/clone-template-page');
|
||||
return {
|
||||
element: <CloneTemplatePage />,
|
||||
};
|
||||
},
|
||||
loader: async ({ params }) => {
|
||||
const { templates } = await import(
|
||||
'./templates-data/templates-data'
|
||||
);
|
||||
const { templates } =
|
||||
await import('./templates-data/templates-data');
|
||||
return {
|
||||
template: templates.find(
|
||||
(template) => template.slug === params.templateSlug
|
||||
@@ -141,9 +132,8 @@ const routes: RouteObject[] = [
|
||||
{
|
||||
path: '*',
|
||||
async lazy() {
|
||||
const { NotFoundPage } = await import(
|
||||
'./pages/not-found-page/not-found-page'
|
||||
);
|
||||
const { NotFoundPage } =
|
||||
await import('./pages/not-found-page/not-found-page');
|
||||
return {
|
||||
element: <NotFoundPage />,
|
||||
};
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2020",
|
||||
"target": "ES2022",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user