Skip to content

Commit

Permalink
🔧 Implement convertToPostgresColumnType function for PostgreSQL type …
Browse files Browse the repository at this point in the history
…conversion and update parser to utilize it
  • Loading branch information
FunamaYukina committed Jan 27, 2025
1 parent 6d26866 commit 7085005
Showing 4 changed files with 106 additions and 30 deletions.
6 changes: 6 additions & 0 deletions .changeset/funny-squids-yawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@liam-hq/db-structure": patch
"@liam-hq/cli": patch
---

🔧 Implement convertToPostgresColumnType function for PostgreSQL type conversion and update parser to utilize it
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import type { DMMF } from '@prisma/generator-helper'

// ref: https://www.prisma.io/docs/orm/reference/prisma-schema-reference#model-field-scalar-types
export function convertToPostgresColumnType(
type: string,
nativeType: DMMF.Field['nativeType'],
defaultValue: DMMF.Field['default'] | null,
): string {
if (nativeType) {
const [nativeTypeName, nativeTypeArgs] = nativeType

// If the default value includes 'autoincrement()', return the appropriate serial type
if (
typeof defaultValue === 'string' &&
defaultValue.includes('autoincrement()')
) {
switch (nativeTypeName) {
case 'Int':
return 'serial'
case 'SmallInt':
return 'smallserial'
case 'BigInt':
return 'bigserial'
default:
return nativeTypeName.toLowerCase()
}
}

// If nativeType has arguments, format it as 'type(args)'
// For example, when `price Decimal @db.Decimal(10, 2)`, type should be Decimal(10, 2)
if (nativeTypeArgs.length > 0) {
return `${nativeTypeName.toLowerCase()}(${nativeTypeArgs.join(',')})`
}

// Special case for 'DoublePrecision' to return 'double precision' with a space
if (nativeTypeName === 'DoublePrecision') {
return 'double precision'
}
return nativeTypeName.toLowerCase()
}

// If nativeType is not provided, use the Prisma field type to determine the PostgreSQL column type
if (
typeof defaultValue === 'string' &&
defaultValue.includes('autoincrement()')
) {
switch (type) {
case 'Int':
return 'serial'
case 'BigInt':
return 'bigserial'
default:
return type.toLowerCase()
}
}

// Special case for 'uuid' default value
if (typeof defaultValue === 'string' && defaultValue.includes('uuid')) {
return 'uuid'
}

switch (type) {
case 'String':
return 'text'
case 'Boolean':
return 'boolean'
case 'Int':
return 'integer'
case 'BigInt':
return 'bigint'
case 'Float':
return 'double precision'
case 'DateTime':
return 'timestamp(3)'
case 'Json':
return 'jsonb'
case 'Decimal':
return 'decimal(65,30)'
case 'Bytes':
return 'bytea'
default:
return type
}
}
Original file line number Diff line number Diff line change
@@ -12,7 +12,8 @@ describe(_processor, () => {
columns: {
id: aColumn({
name: 'id',
type: 'integer',
type: 'serial',
default: 'autoincrement()',
notNull: true,
primary: true,
unique: false,
43 changes: 14 additions & 29 deletions frontend/packages/db-structure/src/parser/prisma/parser.ts
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@ import type {
Table,
} from '../../schema/index.js'
import type { ProcessResult, Processor } from '../types.js'
import { convertToPostgresColumnType } from './convertToPostgresColumnType.js'

// NOTE: Workaround for CommonJS module import issue with @prisma/internals
// CommonJS module can not support all module.exports as named exports
const { getDMMF } = pkg
@@ -25,7 +27,11 @@ async function parsePrismaSchema(schemaString: string): Promise<ProcessResult> {
const defaultValue = extractDefaultValue(field)
columns[field.name] = {
name: field.name,
type: convertToPostgresColumnType(field.type),
type: convertToPostgresColumnType(
field.type,
field.nativeType,
defaultValue,
),
default: defaultValue,
notNull: field.isRequired,
unique: field.isUnique,
@@ -132,8 +138,13 @@ function extractDefaultValue(field: DMMF.Field) {
const defaultValue = value === undefined ? null : value
// NOTE: For example, when `@default(autoincrement())` is specified, `defaultValue`
// becomes an object like `{"name":"autoincrement","args":[]}` (DMMF.FieldDefault).
// Currently, to maintain consistency with other parsers, only primitive types
// (DMMF.FieldDefaultScalar as `string | number | boolean`) are accepted.
// This function now supports both primitive types (DMMF.FieldDefaultScalar as `string | number | boolean`)
// and object types. For object types, it returns a string representation like `name(args)`.
if (typeof defaultValue === 'object' && defaultValue !== null) {
if ('name' in defaultValue && 'args' in defaultValue) {
return `${defaultValue.name}(${defaultValue.args})`
}
}
return typeof defaultValue === 'string' ||
typeof defaultValue === 'number' ||
typeof defaultValue === 'boolean'
@@ -157,30 +168,4 @@ function normalizeConstraintName(constraint: string): ForeignKeyConstraint {
}
}

// ref: https://www.prisma.io/docs/orm/reference/prisma-schema-reference#model-field-scalar-types
function convertToPostgresColumnType(type: string): string {
switch (type) {
case 'String':
return 'text'
case 'Boolean':
return 'boolean'
case 'Int':
return 'integer'
case 'BigInt':
return 'bigint'
case 'Float':
return 'double precision'
case 'DateTime':
return 'timestamp(3)'
case 'Json':
return 'jsonb'
case 'Decimal':
return 'decimal(65,30)'
case 'Bytes':
return 'bytea'
default:
return type
}
}

export const processor: Processor = (str) => parsePrismaSchema(str)

0 comments on commit 7085005

Please sign in to comment.