Skip to content

Commit 15983e8

Browse files
authored
fix: fixed unsupported columns being filtered from output (#11)
1 parent 76bfb7a commit 15983e8

8 files changed

Lines changed: 1124 additions & 233 deletions

File tree

integration/generated/migration.sql

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@ CREATE TABLE "ArrayTypes" (
4040
"id" TEXT NOT NULL,
4141
"strings" TEXT[],
4242
"ints" INTEGER[],
43+
"floats" DOUBLE PRECISION[],
4344
"bools" BOOLEAN[],
45+
"dateTimes" TIMESTAMP(3)[],
46+
"bigInts" BIGINT[],
47+
"decimals" DECIMAL(65,30)[],
4448
"enums" "Role"[],
4549
"jsonArray" JSONB[],
4650

@@ -232,6 +236,58 @@ CREATE TABLE "NativeTypes" (
232236
CONSTRAINT "NativeTypes_pkey" PRIMARY KEY ("id")
233237
);
234238

239+
-- CreateTable
240+
CREATE TABLE "PostgresNativeTypes" (
241+
"id" UUID NOT NULL,
242+
"text" TEXT NOT NULL,
243+
"varchar255" VARCHAR(255) NOT NULL,
244+
"char10" CHAR(10) NOT NULL,
245+
"xml" XML NOT NULL,
246+
"inet" INET NOT NULL,
247+
"bit8" BIT(8) NOT NULL,
248+
"varbit" VARBIT NOT NULL,
249+
"integer" INTEGER NOT NULL,
250+
"smallint" SMALLINT NOT NULL,
251+
"oid" OID NOT NULL,
252+
"bigint" BIGINT NOT NULL,
253+
"doublePrecision" DOUBLE PRECISION NOT NULL,
254+
"real" REAL NOT NULL,
255+
"decimal102" DECIMAL(10,2) NOT NULL,
256+
"money" MONEY NOT NULL,
257+
"timestamp6" TIMESTAMP(6) NOT NULL,
258+
"timestamptz6" TIMESTAMPTZ(6) NOT NULL,
259+
"date" DATE NOT NULL,
260+
"time6" TIME(6) NOT NULL,
261+
"timetz6" TIMETZ(6) NOT NULL,
262+
"json" JSON NOT NULL,
263+
"jsonb" JSONB NOT NULL,
264+
"boolean" BOOLEAN NOT NULL,
265+
266+
CONSTRAINT "PostgresNativeTypes_pkey" PRIMARY KEY ("id")
267+
);
268+
269+
-- CreateTable
270+
CREATE TABLE "TimestampModel" (
271+
"id" TEXT NOT NULL,
272+
"name" TEXT NOT NULL,
273+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
274+
"updatedAt" TIMESTAMP(3) NOT NULL,
275+
276+
CONSTRAINT "TimestampModel_pkey" PRIMARY KEY ("id")
277+
);
278+
279+
-- CreateTable
280+
CREATE TABLE "DefaultFunctions" (
281+
"id" TEXT NOT NULL,
282+
"cuidField" TEXT NOT NULL,
283+
"nowField" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
284+
"intDefault" INTEGER NOT NULL DEFAULT 0,
285+
"boolDefault" BOOLEAN NOT NULL DEFAULT false,
286+
"strDefault" TEXT NOT NULL DEFAULT 'default',
287+
288+
CONSTRAINT "DefaultFunctions_pkey" PRIMARY KEY ("id")
289+
);
290+
235291
-- CreateTable
236292
CREATE TABLE "ExcludedModel" (
237293
"id" TEXT NOT NULL,

integration/generated/zero/schema.ts

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ export const scalarTypesTable = table('ScalarTypes')
2828
json: json(),
2929
bigInt: number(),
3030
decimal: number(),
31-
bytes: string(),
3231
})
3332
.primaryKey('id');
3433

@@ -48,7 +47,11 @@ export const arrayTypesTable = table('ArrayTypes')
4847
id: string(),
4948
strings: json<string[]>(),
5049
ints: json<number[]>(),
50+
floats: json<number[]>(),
5151
bools: json<boolean[]>(),
52+
dateTimes: json<number[]>(),
53+
bigInts: json<number[]>(),
54+
decimals: json<number[]>(),
5255
enums: json<Role[]>(),
5356
jsonArray: json<any[]>(),
5457
})
@@ -220,6 +223,55 @@ export const nativeTypesTable = table('NativeTypes')
220223
})
221224
.primaryKey('id');
222225

226+
export const postgresNativeTypesTable = table('PostgresNativeTypes')
227+
.columns({
228+
id: string(),
229+
text: string(),
230+
varchar255: string(),
231+
char10: string(),
232+
xml: string(),
233+
inet: string(),
234+
bit8: string(),
235+
varbit: string(),
236+
integer: number(),
237+
smallint: number(),
238+
oid: number(),
239+
bigint: number(),
240+
doublePrecision: number(),
241+
real: number(),
242+
decimal102: number(),
243+
money: number(),
244+
timestamp6: number(),
245+
timestamptz6: number(),
246+
date: number(),
247+
time6: number(),
248+
timetz6: number(),
249+
json: json(),
250+
jsonb: json(),
251+
boolean: boolean(),
252+
})
253+
.primaryKey('id');
254+
255+
export const timestampModelTable = table('TimestampModel')
256+
.columns({
257+
id: string(),
258+
name: string(),
259+
createdAt: number(),
260+
updatedAt: number(),
261+
})
262+
.primaryKey('id');
263+
264+
export const defaultFunctionsTable = table('DefaultFunctions')
265+
.columns({
266+
id: string(),
267+
cuidField: string(),
268+
nowField: number(),
269+
intDefault: number(),
270+
boolDefault: boolean(),
271+
strDefault: string(),
272+
})
273+
.primaryKey('id');
274+
223275
export const minimalModelTable = table('MinimalModel')
224276
.columns({
225277
id: string(),
@@ -512,6 +564,9 @@ export const schema = createSchema({
512564
memberTable,
513565
enumFieldsTable,
514566
nativeTypesTable,
567+
postgresNativeTypesTable,
568+
timestampModelTable,
569+
defaultFunctionsTable,
515570
minimalModelTable,
516571
reservedWordsTable,
517572
_articleToTagTable,

integration/schema.prisma

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ enum Status {
4040
/// - Boolean → boolean()
4141
/// - DateTime → number() (timestamp)
4242
/// - Json → json()
43-
/// - Bytes → string() (fallback)
43+
/// - Bytes → excluded (unsupported currently)
4444
model ScalarTypes {
4545
id String @id @default(uuid())
4646
str String
@@ -69,7 +69,11 @@ model ArrayTypes {
6969
id String @id @default(uuid())
7070
strings String[]
7171
ints Int[]
72+
floats Float[]
7273
bools Boolean[]
74+
dateTimes DateTime[]
75+
bigInts BigInt[]
76+
decimals Decimal[]
7377
enums Role[]
7478
jsonArray Json[]
7579
}
@@ -292,6 +296,60 @@ model NativeTypes {
292296
jsonb Json @db.JsonB
293297
}
294298

299+
/// TEST: Comprehensive PostgreSQL native types → all map to base Zero types
300+
model PostgresNativeTypes {
301+
id String @id @default(uuid()) @db.Uuid
302+
// String native types
303+
text String @db.Text
304+
varchar255 String @db.VarChar(255)
305+
char10 String @db.Char(10)
306+
xml String @db.Xml
307+
inet String @db.Inet
308+
bit8 String @db.Bit(8)
309+
varbit String @db.VarBit
310+
// Integer native types
311+
integer Int @db.Integer
312+
smallint Int @db.SmallInt
313+
oid Int @db.Oid
314+
// BigInt native types
315+
bigint BigInt @db.BigInt
316+
// Float native types
317+
doublePrecision Float @db.DoublePrecision
318+
real Float @db.Real
319+
// Decimal native types
320+
decimal102 Decimal @db.Decimal(10, 2)
321+
money Decimal @db.Money
322+
// DateTime native types
323+
timestamp6 DateTime @db.Timestamp(6)
324+
timestamptz6 DateTime @db.Timestamptz(6)
325+
date DateTime @db.Date
326+
time6 DateTime @db.Time(6)
327+
timetz6 DateTime @db.Timetz(6)
328+
// Json native types
329+
json Json @db.Json
330+
jsonb Json @db.JsonB
331+
// Boolean native type
332+
boolean Boolean @db.Boolean
333+
}
334+
335+
/// TEST: @updatedAt attribute → DateTime field auto-updated
336+
model TimestampModel {
337+
id String @id @default(uuid())
338+
name String
339+
createdAt DateTime @default(now())
340+
updatedAt DateTime @updatedAt
341+
}
342+
343+
/// TEST: Various @default functions
344+
model DefaultFunctions {
345+
id String @id @default(uuid()) // uuid() default
346+
cuidField String @default(cuid()) // cuid() default
347+
nowField DateTime @default(now()) // now() default
348+
intDefault Int @default(0) // static default
349+
boolDefault Boolean @default(false) // static default
350+
strDefault String @default("default") // static default
351+
}
352+
295353
// ============================================================================
296354
// EXCLUDED MODEL - Tests excludeTables config
297355
// ============================================================================

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "prisma-zero",
3-
"version": "0.1.1",
3+
"version": "0.1.2",
44
"description": "Generate Zero schemas from Prisma ORM schemas",
55
"type": "module",
66
"scripts": {

src/mappers/schema-mapper.ts

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
ZeroRelationship,
55
TransformedSchema,
66
Config,
7+
ZeroTypeMapping,
78
} from '../types';
89
import {mapPrismaTypeToZero} from './type-mapper';
910
import camelCase from 'camelcase';
@@ -107,6 +108,13 @@ function createImplicitManyToManyModel(
107108
const columnAType = mapPrismaTypeToZero(idFieldA);
108109
const columnBType = mapPrismaTypeToZero(idFieldB);
109110

111+
if (!columnAType || !columnBType) {
112+
const unsupportedModel = !columnAType ? modelA : modelB;
113+
throw new Error(
114+
`Implicit relation ${relationName ?? 'unknown'}: Model ${unsupportedModel.name} has an unsupported @id field.`,
115+
);
116+
}
117+
110118
return {
111119
tableName,
112120
originalTableName,
@@ -141,6 +149,20 @@ function mapRelationships(
141149
): Record<string, ZeroRelationship> {
142150
const relationships: Record<string, ZeroRelationship> = {};
143151

152+
const isSupportedField = (target: DMMF.Model, fieldName: string): boolean => {
153+
const field = target.fields.find(f => f.name === fieldName);
154+
if (!field) {
155+
return true;
156+
}
157+
return mapPrismaTypeToZero(field) !== null;
158+
};
159+
160+
const areFieldsSupported = (
161+
target: DMMF.Model,
162+
fieldNames: string[],
163+
): boolean =>
164+
fieldNames.every(fieldName => isSupportedField(target, fieldName));
165+
144166
model.fields
145167
.filter(field => field.relationName)
146168
.forEach(field => {
@@ -189,18 +211,31 @@ function mapRelationships(
189211
: true
190212
: model.name === modelA.name;
191213

214+
const sourceField = [model.fields.find(f => f.isId)?.name || 'id'];
215+
const destField = [isModelA ? 'A' : 'B'];
216+
const targetDestField = [
217+
targetModel.fields.find(f => f.isId)?.name || 'id',
218+
];
219+
220+
if (
221+
!areFieldsSupported(model, sourceField) ||
222+
!areFieldsSupported(targetModel, targetDestField)
223+
) {
224+
return;
225+
}
226+
192227
// Create a chained relationship through the join table
193228
relationships[field.name] = {
194229
type: 'many',
195230
chain: [
196231
{
197-
sourceField: [model.fields.find(f => f.isId)?.name || 'id'],
198-
destField: [isModelA ? 'A' : 'B'],
232+
sourceField,
233+
destField,
199234
destSchema: getZeroTableName(joinTableName),
200235
},
201236
{
202237
sourceField: [isModelA ? 'B' : 'A'],
203-
destField: [targetModel.fields.find(f => f.isId)?.name || 'id'],
238+
destField: targetDestField,
204239
destSchema: getZeroTableName(targetModel.name),
205240
},
206241
],
@@ -216,6 +251,13 @@ function mapRelationships(
216251
? ensureStringArray(backReference.relationFromFields)
217252
: [];
218253

254+
if (
255+
!areFieldsSupported(model, sourceFields) ||
256+
!areFieldsSupported(targetModel, destFields)
257+
) {
258+
return;
259+
}
260+
219261
relationships[field.name] = {
220262
sourceField: sourceFields,
221263
destField: destFields,
@@ -240,6 +282,13 @@ function mapRelationships(
240282
destFields = ensureStringArray(backReference.relationFromFields);
241283
}
242284

285+
if (
286+
!areFieldsSupported(model, sourceFields) ||
287+
!areFieldsSupported(targetModel, destFields)
288+
) {
289+
return;
290+
}
291+
243292
relationships[field.name] = {
244293
sourceField: sourceFields,
245294
destField: destFields,
@@ -257,12 +306,16 @@ function mapModel(
257306
dmmf: DMMF.Document,
258307
config: Config,
259308
): ZeroModel {
260-
const columns: Record<string, ReturnType<typeof mapPrismaTypeToZero>> = {};
309+
const columns: Record<string, ZeroTypeMapping> = {};
261310

262311
model.fields
263312
.filter(field => !field.relationName)
264313
.forEach(field => {
265-
columns[field.name] = mapPrismaTypeToZero(field);
314+
const mapping = mapPrismaTypeToZero(field);
315+
if (!mapping) {
316+
return;
317+
}
318+
columns[field.name] = mapping;
266319
});
267320

268321
const idField = model.fields.find(f => f.isId)?.name;
@@ -271,6 +324,20 @@ function mapModel(
271324
throw new Error(`No primary key found for ${model.name}`);
272325
}
273326

327+
const unsupportedPrimaryKeys = primaryKey.filter(fieldName => {
328+
const field = model.fields.find(f => f.name === fieldName);
329+
if (!field) {
330+
return false;
331+
}
332+
return mapPrismaTypeToZero(field) === null;
333+
});
334+
335+
if (unsupportedPrimaryKeys.length > 0) {
336+
throw new Error(
337+
`Primary key field(s) ${unsupportedPrimaryKeys.join(', ')} in ${model.name} are not supported by Zero.`,
338+
);
339+
}
340+
274341
// Use the Prisma model name (optionally camelCased) for the Zero table name.
275342
// If the Prisma model is mapped to a different DB table (@@map) or camelCase
276343
// changes the casing, capture the DB table name in originalTableName so we

0 commit comments

Comments
 (0)