Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<Tablename>UpsertArgs select field does not match type for db.<tablename>.upsert(item) #20243

Closed
dawnmist opened this issue Jul 16, 2023 · 6 comments · Fixed by #20285
Closed
Assignees
Labels
5.0.0 bug/2-confirmed Bug has been reproduced and confirmed. kind/bug A reported bug. team/client Issue for team Client. tech/typescript Issue for tech TypeScript. topic: client types Types in Prisma Client topic: prisma-client topic: upsert nested upsert
Milestone

Comments

@dawnmist
Copy link

dawnmist commented Jul 16, 2023

Bug description

On upgrading to Prisma version 5.0.0, the typescript types for the select field of a Prisma.<TableName>UpsertArgs datatype is no longer compatible with the actual upsert function for that table. This had previously been working in version 4.16.2-dev.2.

How to reproduce

  1. Use the provided prisma.schema to generate the prisma client
  2. Use the files quoted in the Prisma Information section below
  3. Run typescript's tsc command to type-check the files, or load them in an IDE and look at the errors identified.

Expected behavior

These datatypes should be compatible.

Prisma information

Prisma schema
generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["multiSchema", "postgresqlExtensions", "views"]
  engineType      = "binary"
}

datasource db {
  provider   = "postgresql"
  url        = env("DATABASE_URL")
  directUrl  = env("DIRECT_URL")
  extensions = [pg_graphql, pg_stat_statements, pgcrypto, pgjwt, pgsodium, pgtap, plpgsql, uuid_ossp(map: "uuid-ossp")]
  schemas    = ["app", "audit", "auth", "extensions", "realtime", "storage", "stripe"]
}

/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model RecordVersion {
  id          BigInt              @id @default(autoincrement())
  recordId    String?             @map("record_id") @db.Uuid
  oldRecordId String?             @map("old_record_id") @db.Uuid
  op          Operation
  ts          DateTime            @default(now()) @db.Timestamptz(6)
  tableOid    Int                 @map("table_oid") @db.Oid
  tableSchema Unsupported("name") @map("table_schema")
  tableName   Unsupported("name") @map("table_name")
  record      Json?
  oldRecord   Json?               @map("old_record")
  authUid     String?             @default(dbgenerated("auth.uid()")) @map("auth_uid") @db.Uuid
  authRole    String?             @default(dbgenerated("auth.role()")) @map("auth_role")

  @@index([tableOid], map: "record_version_table_oid")
  @@index([ts], map: "record_version_ts", type: Brin)
  @@map("record_version")
  @@schema("audit")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model AuditLogEntry {
  instanceId String?   @map("instance_id") @db.Uuid
  id         String    @id @db.Uuid
  payload    Json?     @db.Json
  createdAt  DateTime? @map("created_at") @db.Timestamptz(6)
  ipAddress  String    @default("") @map("ip_address") @db.VarChar(64)

  @@index([instanceId], map: "audit_logs_instance_id_idx")
  @@map("audit_log_entries")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model Identity {
  id           String
  userId       String    @map("user_id") @db.Uuid
  identityData Json      @map("identity_data")
  provider     String
  lastSignInAt DateTime? @map("last_sign_in_at") @db.Timestamptz(6)
  createdAt    DateTime? @map("created_at") @db.Timestamptz(6)
  updatedAt    DateTime? @map("updated_at") @db.Timestamptz(6)
  email        String?   @default(dbgenerated("lower((identity_data ->> 'email'::text))"))
  users        AuthUser  @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@id([provider, id])
  @@index([email])
  @@index([userId])
  @@map("identities")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model Instance {
  id            String    @id @db.Uuid
  uuid          String?   @db.Uuid
  rawBaseConfig String?   @map("raw_base_config")
  createdAt     DateTime? @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime? @map("updated_at") @db.Timestamptz(6)

  @@map("instances")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model MfaAmrClaim {
  sessionId            String   @map("session_id") @db.Uuid
  createdAt            DateTime @map("created_at") @db.Timestamptz(6)
  updatedAt            DateTime @map("updated_at") @db.Timestamptz(6)
  authenticationMethod String   @map("authentication_method")
  id                   String   @id(map: "amr_id_pk") @db.Uuid
  sessions             Session  @relation(fields: [sessionId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@unique([sessionId, authenticationMethod], map: "mfa_amr_claims_session_id_authentication_method_pkey")
  @@map("mfa_amr_claims")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model MfaChallenge {
  id         String    @id @db.Uuid
  factorId   String    @map("factor_id") @db.Uuid
  createdAt  DateTime  @map("created_at") @db.Timestamptz(6)
  verifiedAt DateTime? @map("verified_at") @db.Timestamptz(6)
  ipAddress  String    @map("ip_address") @db.Inet
  mfaFactor  MfaFactor @relation(fields: [factorId], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "mfa_challenges_auth_factor_id_fkey")

  @@map("mfa_challenges")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model MfaFactor {
  id           String         @id @db.Uuid
  userId       String         @map("user_id") @db.Uuid
  friendlyName String?        @map("friendly_name")
  factorType   FactorType     @map("factor_type")
  status       FactorStatus
  createdAt    DateTime       @map("created_at") @db.Timestamptz(6)
  updatedAt    DateTime       @map("updated_at") @db.Timestamptz(6)
  secret       String?
  mfaChallenge MfaChallenge[]
  user         AuthUser       @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([userId, createdAt], map: "factor_id_created_at_idx")
  @@map("mfa_factors")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model RefreshToken {
  instanceId String?   @map("instance_id") @db.Uuid
  id         BigInt    @id @default(autoincrement())
  token      String?   @unique(map: "refresh_tokens_token_unique") @db.VarChar(255)
  userId     String?   @map("user_id") @db.VarChar(255)
  revoked    Boolean?
  createdAt  DateTime? @map("created_at") @db.Timestamptz(6)
  updatedAt  DateTime? @map("updated_at") @db.Timestamptz(6)
  parent     String?   @db.VarChar(255)
  sessionId  String?   @map("session_id") @db.Uuid
  session    Session?  @relation(fields: [sessionId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([instanceId])
  @@index([instanceId, userId])
  @@index([parent])
  @@index([sessionId, revoked])
  @@map("refresh_tokens")
  @@schema("auth")
}

/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model SamlProvider {
  id               String      @id @db.Uuid
  ssoProviderId    String      @map("sso_provider_id") @db.Uuid
  entityId         String      @unique @map("entity_id")
  metadataXml      String      @map("metadata_xml")
  metadataUrl      String?     @map("metadata_url")
  attributeMapping Json?       @map("attribute_mapping")
  createdAt        DateTime?   @map("created_at") @db.Timestamptz(6)
  updatedAt        DateTime?   @map("updated_at") @db.Timestamptz(6)
  ssoProvider      SsoProvider @relation(fields: [ssoProviderId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([ssoProviderId])
  @@map("saml_providers")
  @@schema("auth")
}

/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model SamlRelayState {
  id            String      @id @db.Uuid
  ssoProviderId String      @map("sso_provider_id") @db.Uuid
  requestId     String      @map("request_id")
  forEmail      String?     @map("for_email")
  redirectTo    String?     @map("redirect_to")
  fromIpAddress String?     @map("from_ip_address") @db.Inet
  createdAt     DateTime?   @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime?   @map("updated_at") @db.Timestamptz(6)
  ssoProvider   SsoProvider @relation(fields: [ssoProviderId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([forEmail])
  @@index([ssoProviderId])
  @@map("saml_relay_states")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model AuthSchemaMigration {
  version String @id @db.VarChar(255)

  @@map("schema_migrations")
  @@schema("auth")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model Session {
  id            String         @id @db.Uuid
  userId        String         @map("user_id") @db.Uuid
  createdAt     DateTime?      @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime?      @map("updated_at") @db.Timestamptz(6)
  factorId      String?        @map("factor_id") @db.Uuid
  aal           AalLevel?
  notAfter      DateTime?      @map("not_after") @db.Timestamptz(6)
  mfaAmrClaims  MfaAmrClaim[]
  refreshTokens RefreshToken[]
  user          AuthUser       @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([userId])
  @@index([userId, createdAt], map: "user_id_created_at_idx")
  @@map("sessions")
  @@schema("auth")
}

/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info.
model SsoDomain {
  id            String      @id @db.Uuid
  ssoProviderId String      @map("sso_provider_id") @db.Uuid
  domain        String
  createdAt     DateTime?   @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime?   @map("updated_at") @db.Timestamptz(6)
  ssoProvider   SsoProvider @relation(fields: [ssoProviderId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([ssoProviderId])
  @@map("sso_domains")
  @@schema("auth")
}

/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info.
model SsoProvider {
  id              String           @id @db.Uuid
  resourceId      String?          @map("resource_id")
  createdAt       DateTime?        @map("created_at") @db.Timestamptz(6)
  updatedAt       DateTime?        @map("updated_at") @db.Timestamptz(6)
  samlProviders   SamlProvider[]
  samlRelayStates SamlRelayState[]
  ssoDomains      SsoDomain[]

  @@map("sso_providers")
  @@schema("auth")
}

/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info.
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info.
model AuthUser {
  instanceId               String?         @map("instance_id") @db.Uuid
  id                       String          @id @db.Uuid
  aud                      String?         @db.VarChar(255)
  role                     String?         @db.VarChar(255)
  email                    String?         @db.VarChar(255)
  encryptedPassword        String?         @map("encrypted_password") @db.VarChar(255)
  emailConfirmedAt         DateTime?       @map("email_confirmed_at") @db.Timestamptz(6)
  invitedAt                DateTime?       @map("invited_at") @db.Timestamptz(6)
  confirmationToken        String?         @map("confirmation_token") @db.VarChar(255)
  confirmationSentAt       DateTime?       @map("confirmation_sent_at") @db.Timestamptz(6)
  recoveryToken            String?         @map("recovery_token") @db.VarChar(255)
  recoverySentAt           DateTime?       @map("recovery_sent_at") @db.Timestamptz(6)
  emailChangeTokenNew      String?         @map("email_change_token_new") @db.VarChar(255)
  emailChange              String?         @map("email_change") @db.VarChar(255)
  emailChangeSentAt        DateTime?       @map("email_change_sent_at") @db.Timestamptz(6)
  lastSignInAt             DateTime?       @map("last_sign_in_at") @db.Timestamptz(6)
  rawAppMetaData           Json?           @map("raw_app_meta_data")
  rawUserMetaData          Json?           @map("raw_user_meta_data")
  isSuperAdmin             Boolean?        @map("is_super_admin")
  createdAt                DateTime?       @map("created_at") @db.Timestamptz(6)
  updatedAt                DateTime?       @map("updated_at") @db.Timestamptz(6)
  phone                    String?         @unique
  phoneConfirmedAt         DateTime?       @map("phone_confirmed_at") @db.Timestamptz(6)
  phoneChange              String?         @default("") @map("phone_change")
  phoneChangeToken         String?         @default("") @map("phone_change_token") @db.VarChar(255)
  phoneChangeSentAt        DateTime?       @map("phone_change_sent_at") @db.Timestamptz(6)
  confirmed_at             DateTime?       @default(dbgenerated("LEAST(email_confirmed_at, phone_confirmed_at)")) @db.Timestamptz(6)
  emailChangeTokenCurrent  String?         @default("") @map("email_change_token_current") @db.VarChar(255)
  emailChangeConfirmStatus Int?            @default(0) @map("email_change_confirm_status") @db.SmallInt
  bannedUntil              DateTime?       @map("banned_until") @db.Timestamptz(6)
  reauthenticationToken    String?         @default("") @map("reauthentication_token") @db.VarChar(255)
  reauthenticationSentAt   DateTime?       @map("reauthentication_sent_at") @db.Timestamptz(6)
  isSsoUser                Boolean         @default(false) @map("is_sso_user")
  deletedAt                DateTime?       @map("deleted_at") @db.Timestamptz(6)
  userProfile              UserProfile?
  identities               Identity[]
  mfaFactors               MfaFactor[]
  sessions                 Session[]
  buckets                  StorageBucket[]

  @@index([instanceId])
  @@map("users")
  @@schema("auth")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model StripeCustomer {
  id                  String               @id
  createdAt           DateTime             @default(now()) @map("created_at") @db.Timestamptz(6)
  customerName        String               @map("customer_name")
  description         String?
  delinquent          Boolean              @default(false)
  email               String?
  invoicePrefix       String?              @map("invoice_prefix")
  userProfile         UserProfile?
  stripeSubscriptions StripeSubscription[]

  @@map("stripe_customers")
  @@schema("stripe")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model StripeProduct {
  id                      String                   @id
  productName             String                   @map("product_name")
  description             String?
  createdAt               DateTime                 @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt               DateTime                 @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  defaultPrice            String?                  @map("default_price")
  active                  Boolean                  @default(true)
  taxCode                 String?                  @map("tax_code")
  stripePrices            StripePrice[]
  stripeSubscriptionItems StripeSubscriptionItem[]

  @@map("stripe_products")
  @@schema("stripe")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model StripePrice {
  id                      String                   @id
  stripeProductId         String?                  @map("stripe_product_id")
  createdAt               DateTime                 @default(now()) @map("created_at") @db.Timestamptz(6)
  active                  Boolean                  @default(true)
  priceType               StripePriceType          @map("price_type")
  unitAmount              Int?                     @map("unit_amount")
  currency                CurrencyCode             @default(aud)
  priceBillingScheme      StripePriceBillingScheme @default(per_unit) @map("price_billing_scheme")
  recurringInterval       IntervalPeriod?          @map("recurring_interval")
  recurringCount          Int?                     @map("recurring_count")
  tiersMode               StripePriceTiersMode?    @map("tiers_mode")
  tiersFlatAmount         Int?                     @map("tiers_flat_amount")
  tiersUnitAmount         Int?                     @map("tiers_unit_amount")
  taxBehavior             StripeTaxBehavior        @default(exclusive) @map("tax_behavior")
  stripeProduct           StripeProduct?           @relation(fields: [stripeProductId], references: [id], onDelete: Cascade)
  stripeSubscriptionItems StripeSubscriptionItem[]

  @@map("stripe_prices")
  @@schema("stripe")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model StripeSubscription {
  id                       String                      @id
  stripeCustomerId         String?                     @map("stripe_customer_id")
  createdAt                DateTime                    @default(now()) @map("created_at") @db.Timestamptz(6)
  startDate                DateTime                    @default(now()) @map("start_date") @db.Timestamptz(6)
  endedAt                  DateTime?                   @map("ended_at") @db.Timestamptz(6)
  cancelAtPeriodEnd        Boolean                     @default(false) @map("cancel_at_period_end")
  cancelAt                 DateTime?                   @map("cancel_at") @db.Timestamptz(6)
  currentPeriodStartDate   DateTime?                   @map("current_period_start_date") @db.Timestamptz(6)
  currentPeriodEndDate     DateTime?                   @map("current_period_end_date") @db.Timestamptz(6)
  trialStartDate           DateTime?                   @map("trial_start_date") @db.Timestamptz(6)
  trialEndDate             DateTime?                   @map("trial_end_date") @db.Timestamptz(6)
  trialEndBehavior         StripeTrialEndBehavior?     @map("trial_end_behavior")
  status                   StripeSubscriptionStatus
  cancellationFeedback     StripeCancellationFeedback? @map("cancellation_feedback")
  cancellationReason       StripeCancellationReason?   @map("cancellation_reason")
  cancellationComment      String?                     @map("cancellation_comment")
  pauseCollectionBehaviour StripePauseBehavior?        @map("pause_collection_behaviour")
  pauseCollectionResumesAt DateTime?                   @map("pause_collection_resumes_at") @db.Timestamptz(6)
  daysUntilDue             Int?                        @map("days_until_due") @db.SmallInt
  collectionMethod         StripeCollectionMethod?     @map("collection_method")
  stripeSubscriptionItems  StripeSubscriptionItem[]
  stripeCustomer           StripeCustomer?             @relation(fields: [stripeCustomerId], references: [id], onDelete: Cascade)

  @@map("stripe_subscriptions")
  @@schema("stripe")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model StripeSubscriptionItem {
  id                   String             @id
  stripeSubscriptionId String             @map("stripe_subscription_id")
  stripePriceId        String             @map("stripe_price_id")
  stripeProductId      String?            @map("stripe_product_id")
  quantity             Int                @db.SmallInt
  stripePrice          StripePrice        @relation(fields: [stripePriceId], references: [id], onDelete: Cascade)
  stripeProduct        StripeProduct?     @relation(fields: [stripeProductId], references: [id], onDelete: Cascade)
  stripeSubscription   StripeSubscription @relation(fields: [stripeSubscriptionId], references: [id], onDelete: Cascade)

  @@map("stripe_subscription_items")
  @@schema("stripe")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model Country {
  id        Int        @id @default(autoincrement())
  name      String     @unique
  iso2      String     @unique
  iso3      String?
  localName String?    @map("local_name")
  continent Continent?
  addresses Address[]

  @@map("countries")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model SupportCategory {
  code           Int             @id @db.SmallInt
  name           String          @unique
  portalName     String          @unique @map("portal_name")
  description    String
  budgets        Budget[]
  claims         Claim[]
  futureSupports FutureSupport[]

  @@map("support_categories")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model UserProfile {
  id                 String              @id @db.Uuid
  createdAt          DateTime            @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt          DateTime            @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  email              String              @unique
  displayName        String?             @map("display_name")
  timezone           String?             @default("Australia/Melbourne")
  consentToEmail     Boolean?            @default(false) @map("consent_to_email")
  stripeCustomerId   String?             @unique @map("stripe_customer_id")
  participantInvites ParticipantInvite[]
  Participant        Participant[]
  authUser           AuthUser            @relation(fields: [id], references: [id], onDelete: Cascade, onUpdate: NoAction)
  stripeCustomer     StripeCustomer?     @relation(fields: [stripeCustomerId], references: [id])
  updatedByUserRoles UserRole[]          @relation("user_roles_updated_byTouser_profiles")
  userRoles          UserRole[]          @relation("user_roles_user_idTouser_profiles")

  @@index([stripeCustomerId])
  @@map("user_profiles")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model Participant {
  id                 String              @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  ownerId            String              @map("owner_id") @db.Uuid
  displayName        String              @map("display_name")
  ndis_number        String
  createdAt          DateTime            @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt          DateTime            @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  claimsToLineItems  ClaimToLineItem[]
  addresses          Address[]
  bankDetails        BankDetail[]
  budgets            Budget[]
  claims             Claim[]
  companies          Company[]
  documents          Document[]
  futureSupports     FutureSupport[]
  invoices           Invoice[]
  lineItems          LineItem[]
  participantInvites ParticipantInvite[]
  owner              UserProfile         @relation(fields: [ownerId], references: [id], onDelete: Cascade, onUpdate: NoAction)
  phoneNumbers       PhoneNumber[]
  plans              Plan[]
  userRoles          UserRole[]

  @@map("participants")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model PhoneNumber {
  id            String      @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  participantId String      @map("participant_id") @db.Uuid
  companyId     String?     @map("company_id") @db.Uuid
  number        String
  phone_type    PhoneType
  otherType     String?     @map("other_type")
  createdAt     DateTime    @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime    @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  company       Company?    @relation(fields: [companyId], references: [id], onDelete: Cascade, onUpdate: NoAction)
  participant   Participant @relation(fields: [participantId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([companyId])
  @@index([participantId])
  @@map("phone_numbers")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model Plan {
  id            String      @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  participantId String      @map("participant_id") @db.Uuid
  startDate     DateTime    @map("start_date") @db.Date
  endDate       DateTime    @map("end_date") @db.Date
  createdAt     DateTime    @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime    @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  budgets       Budget[]
  documents     Document[]
  participant   Participant @relation(fields: [participantId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([participantId])
  @@map("plans")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model Address {
  id            String       @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  participantId String       @map("participant_id") @db.Uuid
  companyId     String?      @map("company_id") @db.Uuid
  street1       String
  street2       String?
  suburb        String?
  postcode      String?
  countryCode   String       @default("AU") @map("country_code")
  addressType   AddressType? @map("address_type")
  createdAt     DateTime     @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime     @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  company       Company?     @relation(fields: [companyId], references: [id], onDelete: Cascade, onUpdate: NoAction)
  country       Country      @relation(fields: [countryCode], references: [iso2])
  participants  Participant  @relation(fields: [participantId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([companyId])
  @@index([participantId])
  @@map("addresses")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model BankDetail {
  id            String      @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  participantId String      @map("participant_id") @db.Uuid
  companyId     String?     @unique @map("company_id") @db.Uuid
  bank          String
  accountName   String      @map("account_name")
  bsb           String
  accountNumber String      @map("account_number")
  createdAt     DateTime    @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime    @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  company       Company?    @relation(fields: [companyId], references: [id], onDelete: Cascade)
  participant   Participant @relation(fields: [participantId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([participantId])
  @@map("bank_details")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model Company {
  id                 String          @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  participantId      String          @map("participant_id") @db.Uuid
  company_name       String
  abn                String
  email              String?
  url                String?
  paymentTermsPeriod IntervalPeriod? @map("payment_terms_interval")
  paymentTermsCount  Int?            @map("payment_terms_count")
  contactPerson      String?         @map("contact_person")
  createdAt          DateTime        @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt          DateTime        @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  terminatedAt       DateTime?       @map("terminated_at") @db.Timestamptz(6)
  notes              String?
  addresses          Address[]
  bankDetails        BankDetail?
  participants       Participant     @relation(fields: [participantId], references: [id], onDelete: Cascade, onUpdate: NoAction)
  documents          Document[]
  future_supports    FutureSupport[]
  invoices           Invoice[]
  line_items         LineItem[]
  phone_numbers      PhoneNumber[]

  @@unique([participantId, abn])
  @@unique([participantId, company_name])
  @@index([participantId])
  @@index([company_name], map: "companies_name_idx")
  @@map("companies")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model Budget {
  id                 String              @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  participantId      String              @map("participant_id") @db.Uuid
  planId             String              @map("plan_id") @db.Uuid
  budget_value       Int
  supportCategory    Int                 @map("support_category") @db.SmallInt
  initialSpent       Int                 @default(0) @map("initial_spent")
  managementType     FundsManagementType @default(Self_Managed) @map("management_type")
  isStated           Boolean             @default(false) @map("is_stated")
  quoteRequired      Boolean             @default(false) @map("quote_required")
  createdAt          DateTime            @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt          DateTime            @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  participant        Participant         @relation(fields: [participantId], references: [id], onDelete: Cascade, onUpdate: NoAction)
  plan               Plan                @relation(fields: [planId], references: [id], onDelete: Cascade, onUpdate: NoAction)
  support_categories SupportCategory     @relation(fields: [supportCategory], references: [code], onUpdate: NoAction)
  lineItems          LineItem[]

  @@index([participantId])
  @@index([participantId, planId])
  @@index([participantId, supportCategory])
  @@map("budgets")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model Claim {
  prn             BigInt            @id
  participantId   String            @map("participant_id") @db.Uuid
  title           String
  claimType       ClaimType         @default(Claim) @map("claim_type")
  claim_date      DateTime          @default(now()) @db.Date
  supportCategory Int               @map("support_category") @db.SmallInt
  claim_value     Int
  notes           String?
  createdAt       DateTime          @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt       DateTime          @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  previousPrn     BigInt?           @unique @map("previous_prn")
  lineItems       ClaimToLineItem[]
  participant     Participant       @relation(fields: [participantId], references: [id], onDelete: Cascade, onUpdate: NoAction)
  previousClaim   Claim?            @relation("replacement_claim", fields: [previousPrn], references: [prn], onDelete: Restrict)
  nextClaim       Claim?            @relation("replacement_claim")
  category        SupportCategory   @relation(fields: [supportCategory], references: [code], onDelete: NoAction, onUpdate: NoAction)

  @@index([participantId])
  @@index([supportCategory, claim_date], map: "claims_support_category_idx")
  @@map("claims")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model Document {
  documentObjectId String         @id @map("document_object_id") @db.Uuid
  documentType     DocumentType   @map("document_type")
  participantId    String         @map("participant_id") @db.Uuid
  companyId        String?        @map("company_id") @db.Uuid
  planId           String?        @map("plan_id") @db.Uuid
  invoiceId        String?        @map("invoice_id") @db.Uuid
  createdAt        DateTime       @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt        DateTime       @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  company          Company?       @relation(fields: [companyId], references: [id], onDelete: Cascade)
  document         DocumentObject @relation(fields: [documentObjectId], references: [id], onDelete: Cascade)
  invoice          Invoice?       @relation(fields: [invoiceId], references: [id], onDelete: Cascade)
  participant      Participant    @relation(fields: [participantId], references: [id], onDelete: Cascade)
  plan             Plan?          @relation(fields: [planId], references: [id], onDelete: Cascade)

  @@index([participantId])
  @@map("documents")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model FutureSupport {
  id                   String          @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  participantId        String          @map("participant_id") @db.Uuid
  companyId            String          @map("company_id") @db.Uuid
  title                String
  createdAt            DateTime        @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt            DateTime        @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  status               LineItemStatus  @default(Planned)
  supportCategory      Int             @map("support_category") @db.SmallInt
  quantity             Int             @default(1)
  pricePer             Int             @map("price_per")
  total                Int
  startDate            DateTime        @map("start_date") @db.Date
  endDate              DateTime        @map("end_date") @db.Date
  startTime            DateTime?       @map("start_time") @db.Time(0)
  endTime              DateTime?       @map("end_time") @db.Time(0)
  repeatIntervalPeriod IntervalPeriod? @map("repeat_interval_period")
  repeatIntervalCount  Int?            @map("repeat_interval_count")
  daysOfWeek           String          @map("days_of_week") @db.Bit(7)
  notes                String?
  company              Company         @relation(fields: [companyId], references: [id])
  participant          Participant     @relation(fields: [participantId], references: [id], onDelete: Cascade, onUpdate: NoAction)
  category             SupportCategory @relation(fields: [supportCategory], references: [code], onDelete: NoAction, onUpdate: NoAction)
  lineItems            LineItem[]

  @@index([companyId])
  @@index([participantId])
  @@index([supportCategory, endDate, startDate])
  @@map("future_supports")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model Invoice {
  id               String        @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  participantId    String        @map("participant_id") @db.Uuid
  companyId        String        @map("company_id") @db.Uuid
  invoiceId        String        @map("invoice_id")
  title            String
  invoiceDate      DateTime      @map("invoice_date") @db.Date
  receivedDate     DateTime?     @map("received_date") @db.Date
  dueDate          DateTime      @map("due_date") @db.Date
  paidDate         DateTime?     @map("paid_date") @db.Date
  total            Int
  supportStartDate DateTime      @map("support_start_date") @db.Date
  supportEndDate   DateTime      @map("support_end_date") @db.Date
  invoiceType      InvoiceStatus @map("invoice_type")
  notes            String?
  createdAt        DateTime      @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt        DateTime      @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  documents        Document[]
  company          Company       @relation(fields: [companyId], references: [id])
  participant      Participant   @relation(fields: [participantId], references: [id], onDelete: Cascade, onUpdate: NoAction)
  lineItems        LineItem[]

  @@unique([participantId, companyId, invoiceId])
  @@index([companyId])
  @@index([participantId])
  @@map("invoices")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model LineItem {
  id              String            @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  participantId   String            @map("participant_id") @db.Uuid
  companyId       String            @map("company_id") @db.Uuid
  budgetId        String            @map("budget_id") @db.Uuid
  invoiceId       String?           @map("invoice_id") @db.Uuid
  futureSupportId String?           @map("future_support_id") @db.Uuid
  status          LineItemStatus
  quantity        Int               @default(1)
  pricePer        Int               @map("price_per")
  total           Int
  startDate       DateTime          @map("start_date") @db.Date
  endDate         DateTime          @map("end_date") @db.Date
  startTime       DateTime?         @map("start_time") @db.Time(0)
  endTime         DateTime?         @map("end_time") @db.Time(0)
  notes           String?
  createdAt       DateTime          @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt       DateTime          @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  claims          ClaimToLineItem[]
  budget          Budget            @relation(fields: [budgetId], references: [id])
  company         Company           @relation(fields: [companyId], references: [id])
  futureSupport   FutureSupport?    @relation(fields: [futureSupportId], references: [id], onDelete: Cascade)
  invoice         Invoice?          @relation(fields: [invoiceId], references: [id], onUpdate: NoAction)
  participant     Participant       @relation(fields: [participantId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([budgetId])
  @@index([companyId])
  @@index([participantId])
  @@index([startDate, endDate])
  @@map("line_items")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model ClaimToLineItem {
  prn           BigInt
  lineItemId    String      @map("line_item_id") @db.Uuid
  participantId String      @map("participant_id") @db.Uuid
  lineItem      LineItem    @relation(fields: [lineItemId], references: [id], onDelete: Cascade)
  participant   Participant @relation(fields: [participantId], references: [id], onDelete: Cascade, onUpdate: NoAction)
  claim         Claim       @relation(fields: [prn], references: [prn], onDelete: Cascade)

  @@id([prn, lineItemId])
  @@index([participantId])
  @@map("_claims_to_line_items")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model StorageBucket {
  id                String           @id
  name              String           @unique(map: "bname")
  owner             String?          @db.Uuid
  createdAt         DateTime?        @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt         DateTime?        @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  public            Boolean?         @default(false)
  avifAutodetection Boolean?         @default(false) @map("avif_autodetection")
  fileSizeLimit     BigInt?          @map("file_size_limit")
  allowedMimeTypes  String[]         @map("allowed_mime_types")
  user              AuthUser?        @relation(fields: [owner], references: [id], onDelete: NoAction, onUpdate: NoAction)
  documents         DocumentObject[]

  @@map("buckets")
  @@schema("storage")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model StorageMigration {
  id         Int       @id
  name       String    @unique @db.VarChar(100)
  hash       String    @db.VarChar(40)
  executedAt DateTime? @default(now()) @map("executed_at") @db.Timestamp(6)

  @@map("migrations")
  @@schema("storage")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model DocumentObject {
  id             String         @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
  bucketId       String?        @map("bucket_id")
  name           String?
  owner          String?        @db.Uuid
  createdAt      DateTime?      @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt      DateTime?      @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
  lastAccessedAt DateTime?      @default(now()) @map("last_accessed_at") @db.Timestamptz(6)
  metadata       Json?
  pathTokens     String[]       @default(dbgenerated("string_to_array(name, '/'::text)")) @map("path_tokens")
  version        String?
  document       Document?
  bucket         StorageBucket? @relation(fields: [bucketId], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "objects_bucketId_fkey")

  @@unique([bucketId, name], map: "bucketid_objname")
  @@index([name], map: "name_prefix_search")
  @@map("objects")
  @@schema("storage")
}

/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
model FlowState {
  id                   String              @id @db.Uuid
  userId               String?             @map("user_id") @db.Uuid
  authCode             String              @map("auth_code")
  codeChallengeMethod  CodeChallengeMethod @map("code_challenge_method")
  codeChallenge        String              @map("code_challenge")
  providerType         String              @map("provider_type")
  providerAccessToken  String?             @map("provider_access_token")
  providerRefreshToken String?             @map("provider_refresh_token")
  createdAt            DateTime?           @map("created_at") @db.Timestamptz(6)
  updatedAt            DateTime?           @map("updated_at") @db.Timestamptz(6)
  authenticationMethod String              @map("authentication_method")

  @@index([authCode], map: "idx_auth_code")
  @@index([userId, authenticationMethod], map: "idx_user_id_auth_method")
  @@map("flow_state")
  @@schema("auth")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model ParticipantInvite {
  id            String      @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  participantId String      @map("participant_id") @db.Uuid
  accessLevel   AccessLevel @default(Read) @map("access_level")
  createdAt     DateTime    @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime    @default(now()) @map("updated_at") @db.Timestamptz(6)
  email         String
  invitedBy     String      @map("invited_by") @db.Uuid
  userProfiles  UserProfile @relation(fields: [invitedBy], references: [id], onDelete: Cascade, onUpdate: NoAction)
  participants  Participant @relation(fields: [participantId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@index([participantId])
  @@map("participant_invites")
  @@schema("app")
}

/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model UserRole {
  userId        String       @map("user_id") @db.Uuid
  participantId String       @map("participant_id") @db.Uuid
  accessLevel   AccessLevel  @map("access_level")
  createdAt     DateTime     @default(now()) @map("created_at") @db.Timestamptz(6)
  updatedAt     DateTime     @default(now()) @map("updated_at") @db.Timestamptz(6)
  updatedBy     String?      @map("updated_by") @db.Uuid
  participants  Participant  @relation(fields: [participantId], references: [id], onDelete: Cascade, onUpdate: NoAction)
  updatedByUser UserProfile? @relation("user_roles_updated_byTouser_profiles", fields: [updatedBy], references: [id], onUpdate: NoAction)
  user          UserProfile  @relation("user_roles_user_idTouser_profiles", fields: [userId], references: [id], onDelete: Cascade, onUpdate: NoAction)

  @@id([userId, participantId])
  @@index([participantId])
  @@map("user_roles")
  @@schema("app")
}

/// The underlying view does not contain a valid unique identifier and can therefore currently not be handled by the Prisma Client.
view pg_stat_statements_info {
  dealloc     BigInt?
  stats_reset DateTime? @db.Timestamptz(6)

  @@ignore
  @@schema("extensions")
}

/// The underlying view does not contain a valid unique identifier and can therefore currently not be handled by the Prisma Client.
view pg_stat_statements {
  userid                 Int?     @db.Oid
  dbid                   Int?     @db.Oid
  toplevel               Boolean?
  queryid                BigInt?
  query                  String?
  plans                  BigInt?
  total_plan_time        Float?
  min_plan_time          Float?
  max_plan_time          Float?
  mean_plan_time         Float?
  stddev_plan_time       Float?
  calls                  BigInt?
  total_exec_time        Float?
  min_exec_time          Float?
  max_exec_time          Float?
  mean_exec_time         Float?
  stddev_exec_time       Float?
  rows                   BigInt?
  shared_blks_hit        BigInt?
  shared_blks_read       BigInt?
  shared_blks_dirtied    BigInt?
  shared_blks_written    BigInt?
  local_blks_hit         BigInt?
  local_blks_read        BigInt?
  local_blks_dirtied     BigInt?
  local_blks_written     BigInt?
  temp_blks_read         BigInt?
  temp_blks_written      BigInt?
  blk_read_time          Float?
  blk_write_time         Float?
  temp_blk_read_time     Float?
  temp_blk_write_time    Float?
  wal_records            BigInt?
  wal_fpi                BigInt?
  wal_bytes              Decimal? @db.Decimal
  jit_functions          BigInt?
  jit_generation_time    Float?
  jit_inlining_count     BigInt?
  jit_inlining_time      Float?
  jit_optimization_count BigInt?
  jit_optimization_time  Float?
  jit_emission_count     BigInt?
  jit_emission_time      Float?

  @@ignore
  @@schema("extensions")
}

/// The underlying view does not contain a valid unique identifier and can therefore currently not be handled by the Prisma Client.
view tap_funky {
  oid         Int?                 @db.Oid
  schema      Unsupported("name")?
  name        Unsupported("name")?
  owner       Unsupported("name")?
  args        String?
  returns     String?
  langoid     Int?                 @db.Oid
  is_strict   Boolean?
  kind        String?              @db.Char
  is_definer  Boolean?
  returns_set Boolean?
  volatility  String?              @db.Char(1)
  is_visible  Boolean?

  @@ignore
  @@schema("extensions")
}

/// The underlying view does not contain a valid unique identifier and can therefore currently not be handled by the Prisma Client.
view pg_all_foreign_keys {
  fk_schema_name     Unsupported("name")?
  fk_table_name      Unsupported("name")?
  fk_constraint_name Unsupported("name")?
  fk_table_oid       Int?                   @db.Oid
  fk_columns         Unsupported("_name")[]
  pk_schema_name     Unsupported("name")?
  pk_table_name      Unsupported("name")?
  pk_constraint_name Unsupported("name")?
  pk_table_oid       Int?                   @db.Oid
  pk_index_name      Unsupported("name")?
  pk_columns         Unsupported("_name")[]
  match_type         String?
  on_delete          String?
  on_update          String?
  is_deferrable      Boolean?
  is_deferred        Boolean?

  @@ignore
  @@schema("extensions")
}

enum Operation {
  INSERT
  UPDATE
  DELETE
  TRUNCATE

  @@map("operation")
  @@schema("audit")
}

enum AalLevel {
  aal1
  aal2
  aal3

  @@map("aal_level")
  @@schema("auth")
}

enum FactorStatus {
  unverified
  verified

  @@map("factor_status")
  @@schema("auth")
}

enum FactorType {
  totp
  webauthn

  @@map("factor_type")
  @@schema("auth")
}

enum CodeChallengeMethod {
  s256
  plain

  @@map("code_challenge_method")
  @@schema("auth")
}

enum StripeCancellationFeedback {
  too_expensive
  missing_features
  switched_service
  unused
  customer_service
  too_complex
  low_quality
  other

  @@map("stripe_cancellation_feedback")
  @@schema("stripe")
}

enum StripeCancellationReason {
  cancellation_requested
  payment_disputed
  payment_failed

  @@map("stripe_cancellation_reason")
  @@schema("stripe")
}

enum StripeCollectionMethod {
  charge_automatically
  send_invoice

  @@map("stripe_collection_method")
  @@schema("stripe")
}

enum StripePauseBehavior {
  keep_as_draft
  mark_uncollectible
  void

  @@map("stripe_pause_behavior")
  @@schema("stripe")
}

enum StripePriceBillingScheme {
  per_unit
  tiered

  @@map("stripe_price_billing_scheme")
  @@schema("stripe")
}

enum StripePriceTiersMode {
  graduated
  volume

  @@map("stripe_price_tiers_mode")
  @@schema("stripe")
}

enum StripePriceType {
  recurring
  one_time

  @@map("stripe_price_type")
  @@schema("stripe")
}

enum StripeSubscriptionStatus {
  trialing
  active
  past_due
  canceled
  unpaid
  incomplete
  incomplete_expired
  paused

  @@map("stripe_subscription_status")
  @@schema("stripe")
}

enum StripeTaxBehavior {
  exclusive
  inclusive
  unspecified

  @@map("stripe_tax_behavior")
  @@schema("stripe")
}

enum StripeTrialEndBehavior {
  cancel
  pause
  create_invoice

  @@map("stripe_trial_end_behavior")
  @@schema("stripe")
}

enum FundsManagementType {
  Agency_Managed
  Plan_Managed
  Self_Managed

  @@map("funds_management_type")
  @@schema("app")
}

enum IntervalPeriod {
  day
  week
  month
  year

  @@map("interval_period")
  @@schema("app")
}

enum InvoiceStatus {
  Receipt
  Quote
  Invoice
  Refund

  @@map("invoice_status")
  @@schema("app")
}

enum LineItemStatus {
  Planned
  Quoted
  Actual
  Cancellation
  Refund

  @@map("line_item_status")
  @@schema("app")
}

enum PhoneType {
  Mobile
  Home
  Office
  Head_Office
  Support
  Billing
  After_Hours
  Complaints
  Other

  @@map("phone_type")
  @@schema("app")
}

enum DocumentType {
  Plan
  Company
  Invoice
  Participant

  @@map("document_type")
  @@schema("app")
}

enum AddressType {
  Postal
  Billing
  Office
  Head_Office
  Other

  @@map("address_type")
  @@schema("app")
}

enum ClaimType {
  Claim
  Cancellation

  @@map("claim_type")
  @@schema("app")
}

enum Continent {
  Africa
  Antarctica
  Asia
  Europe
  Oceania
  North_America
  South_America

  @@map("continent")
  @@schema("app")
}

enum CurrencyCode {
  usd
  aed
  afn
  all
  amd
  ang
  aoa
  ars
  aud
  awg
  azn
  bam
  bbd
  bdt
  bgn
  bif
  bmd
  bnd
  bob
  brl
  bsd
  bwp
  byn
  bzd
  cad
  cdf
  chf
  clp
  cny
  cop
  crc
  cve
  czk
  djf
  dkk
  dop
  dzd
  egp
  etb
  eur
  fjd
  fkp
  gbp
  gel
  gip
  gmd
  gnf
  gtq
  gyd
  hkd
  hnl
  htg
  huf
  idr
  ils
  inr
  isk
  jmd
  jpy
  kes
  kgs
  khr
  kmf
  krw
  kyd
  kzt
  lak
  lbp
  lkr
  lrd
  lsl
  mad
  mdl
  mga
  mkd
  mmk
  mnt
  mop
  mro
  mur
  mvr
  mwk
  mxn
  myr
  mzn
  nad
  ngn
  nio
  nok
  npr
  nzd
  pab
  pen
  pgk
  php
  pkr
  pln
  pyg
  qar
  ron
  rsd
  rub
  rwf
  sar
  sbd
  scr
  sek
  sgd
  shp
  sle
  sll
  sos
  srd
  std
  szl
  thb
  tjs
  top
  try
  ttd
  twd
  tzs
  uah
  ugx
  uyu
  uzs
  vnd
  vuv
  wst
  xaf
  xcd
  xof
  xpf
  yer
  zar
  zmw

  @@map("currency_code")
  @@schema("app")
}

enum AccessLevel {
  Read
  Write
  Admin

  @@map("access_level")
  @@schema("app")
}

tsconfig.json:

{
  "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["server/index.ts", ".react-email/**/*"],
  "compilerOptions": {
    "lib": ["DOM", "DOM.Iterable", "ES2022"],
    "isolatedModules": true,
    "esModuleInterop": true,
    "jsx": "react-jsx",
    "module": "NodeNext",
    "moduleResolution": "nodenext",
    "resolveJsonModule": true,
    "target": "ES2022",
    "strict": true,
    "noImplicitAny": true,
    "noErrorTruncation": true,
    "allowJs": true,
    "forceConsistentCasingInFileNames": true,
    "baseUrl": ".",
    "paths": {
      "~/*": ["./app/*"],
      "~server/*": ["./server/*"],
      "~shared/*": ["./shared/*"],
      "~tests/*": ["./tests/*"]
    },
    "skipLibCheck": true,
    "allowImportingTsExtensions": true,
    // Remix takes care of building everything in `remix build`.
    "noEmit": true
  }
}

~/utils/singleton.server.ts:

declare global {
  // eslint-disable-next-line no-var
  var __singletons: Record<string, unknown> | undefined;
}

export function singleton<Value>(name: string, value: () => Value): Value {
  global.__singletons ??= {};
  global.__singletons[name] ??= value();

  return global.__singletons[name] as Value;
}

~/integrations/prisma/index.server.ts:

import { Prisma, PrismaClient } from '@prisma/client';
import { singleton } from '~/utils/singleton.server.ts';

// this is needed because in development we don't want to restart
// the server with every change, but we want to make sure we don't
// create a new connection to the DB with every change either.
// in production, we'll have a single connection to the DB.
const db = singleton('prisma', () => new PrismaClient());
db.$connect().catch((e) => {
  console.error(e);
  throw e;
});

const supabaseRowLevelSecurityExtension = (claims: string) =>
  Prisma.defineExtension((client) =>
    client.$extends({
      name: 'supabaseRowLevelSecurity',
      query: {
        $allModels: {
          async $allOperations({ args, query }) {
            try {
              // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
              const [, , result] = await client.$transaction([
                client.$executeRaw`SET ROLE authenticated;`,
                client.$executeRaw`SELECT set_config('request.jwt.claims', (${claims})::text, TRUE);`,
                query(args),
              ]);
              // console.log(JSON.stringify(result));

              // eslint-disable-next-line @typescript-eslint/no-unsafe-return
              return result;
            } catch (e) {
              console.log(JSON.stringify(e));
              throw e;
            }
          },
        },
      },
    })
  );

export const getDbClient = (claims: string) => db.$extends(supabaseRowLevelSecurityExtension(claims));

export type PrismaTransactionClient = Omit<
  ReturnType<typeof getDbClient>,
  '$extends' | '$transaction' | '$disconnect' | '$connect' | '$on' | '$use'
>;

~/models/stripe/stripePrice.ts:

import type { Prisma } from '@prisma/client';
import type { PrismaTransactionClient } from '~/integrations/prisma/index.server.ts';

export type StripePriceCreate = Prisma.StripePriceUncheckedCreateInput;
export type StripePriceUpdate = Prisma.StripePriceUncheckedUpdateInput;
export type StripePriceUpsert = Omit<Prisma.StripePriceUpsertArgs, 'create' | 'update'> & {
  create: StripePriceCreate;
  update: StripePriceUpdate;
};

export const onStripePriceUpdated = (db: PrismaTransactionClient, price: StripePriceUpsert) =>
  db.stripePrice.upsert(price);

Error in onStripePriceUpdated:

app/models/stripe/stripePrice/index.server.ts:15:25 - error TS2345: Argument of type 'StripePriceUpsert' is not assignable to parameter of type '{ select?: Exact<StripePriceSelect<DefaultArgs> | null | undefined, StripePriceSelect<Args & { result: {}; model: {}; query: {}; client: {}; }> | null | undefined>; include?: Exact<StripePriceInclude<DefaultArgs> | null | undefined, StripePriceInclude<Args & { result: {}; model: {}; query: {}; client: {}; }> | null | undefined>; where: Exact<StripePriceWhereUniqueInput, StripePriceWhereUniqueInput>; create: Exact<StripePriceUncheckedCreateInput, (Without<StripePriceCreateInput, StripePriceUncheckedCreateInput> & StripePriceUncheckedCreateInput) | (Without<StripePriceUncheckedCreateInput, StripePriceCreateInput> & StripePriceCreateInput)>; update: Exact<StripePriceUncheckedUpdateInput, (Without<StripePriceUpdateInput, StripePriceUncheckedUpdateInput> & StripePriceUncheckedUpdateInput) | (Without<StripePriceUncheckedUpdateInput, StripePriceUpdateInput> & StripePriceUpdateInput)>; }'.
  Types of property 'select' are incompatible.
    Type 'StripePriceSelect<DefaultArgs> | null | undefined' is not assignable to type 'Exact<StripePriceSelect<DefaultArgs> | null | undefined, StripePriceSelect<Args & { result: {}; model: {}; query: {}; client: {}; }> | null | undefined>'.
      Type 'StripePriceSelect<DefaultArgs>' is not assignable to type 'Exact<StripePriceSelect<DefaultArgs> | null | undefined, StripePriceSelect<Args & { result: {}; model: {}; query: {}; client: {}; }> | null | undefined>'.
        Types of property 'id' are incompatible.
          Type 'boolean | undefined' is not assignable to type 'undefined'.
            Type 'false' is not assignable to type 'undefined'.

12   db.stripePrice.upsert(price);
                           ~~~~~

~/models/stripe/stripeProduct.ts:

import type { Prisma } from '@prisma/client';
import type { PrismaTransactionClient } from '~/integrations/prisma/index.server.ts';

type ExcludeFields = 'stripePrices';
export type StripeProductCreate = Omit<Prisma.StripeProductUncheckedCreateInput, ExcludeFields>;
export type StripeProductUpdate = Omit<Prisma.StripeProductUncheckedUpdateInput, ExcludeFields>;
export type StripeProductUpsert = Omit<Prisma.StripeProductUpsertArgs, 'create' | 'update'> & {
  create: StripeProductCreate;
  update: StripeProductUpdate;
};

export const onStripeProductUpdated = (db: PrismaTransactionClient, product: StripeProductUpsert) =>
  db.stripeProduct.upsert(product);

Errors in onStripeProductUpdated:

app/models/stripe/stripeProduct/index.server.ts:13:3 - error TS2321: Excessive stack depth comparing types 'StripeProductSelect<DefaultArgs>' and 'StripeProductSelect<Args & { result: {}; model: {}; query: {}; client: {}; }>'.

13   db.stripeProduct.upsert(product);
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

app/models/stripe/stripeProduct/index.server.ts:13:27 - error TS2345: Argument of type 'StripeProductUpsert' is not assignable to parameter of type '{ select?: Exact<StripeProductSelect<DefaultArgs> | null | undefined, StripeProductSelect<Args & { result: {}; model: {}; query: {}; client: {}; }> | null | undefined>; include?: Exact<StripeProductInclude<DefaultArgs> | null | undefined, StripeProductInclude<Args & { result: {}; model: {}; query: {}; client: {}; }> | null | undefined>; where: Exact<StripeProductWhereUniqueInput, StripeProductWhereUniqueInput>; create: Exact<StripeProductCreate, (Without<StripeProductCreateInput, StripeProductUncheckedCreateInput> & StripeProductUncheckedCreateInput) | (Without<StripeProductUncheckedCreateInput, StripeProductCreateInput> & StripeProductCreateInput)>; update: Exact<StripeProductUpdate, (Without<StripeProductUpdateInput, StripeProductUncheckedUpdateInput> & StripeProductUncheckedUpdateInput) | (Without<StripeProductUncheckedUpdateInput, StripeProductUpdateInput> & StripeProductUpdateInput)>; }'.
  Types of property 'select' are incompatible.
    Type 'StripeProductSelect<DefaultArgs> | null | undefined' is not assignable to type 'Exact<StripeProductSelect<DefaultArgs> | null | undefined, StripeProductSelect<Args & { result: {}; model: {}; query: {}; client: {}; }> | null | undefined>'.
      Type 'StripeProductSelect<DefaultArgs>' is not assignable to type 'Exact<StripeProductSelect<DefaultArgs> | null | undefined, StripeProductSelect<Args & { result: {}; model: {}; query: {}; client: {}; }> | null | undefined>'.
        Types of property 'id' are incompatible.
          Type 'boolean | undefined' is not assignable to type 'undefined'.
            Type 'false' is not assignable to type 'undefined'.

13   db.stripeProduct.upsert(product);
                             ~~~~~~~

Environment & setup

  • OS: Debian 'Bullseye', 5.10.0-22-amd64 #1 SMP Debian 5.10.178-3 (2023-04-22) x86_64 GNU/Linux
  • Database: PostgreSQL
  • Node.js version: 18.12.1

Prisma Version

prisma                  : 5.0.0
@prisma/client          : 5.0.0
Current platform        : debian-openssl-1.1.x
Query Engine (Node-API) : libquery-engine 6b0aef69b7cdfc787f822ecd7cdc76d5f1991584 (at node_modules/.store/@prisma-engines-npm-5.0.0-9f601585ff/node_modules/@prisma/engines/libquery_engine-debian-openssl-1.1.x.so.node)
Schema Engine           : schema-engine-cli 6b0aef69b7cdfc787f822ecd7cdc76d5f1991584 (at node_modules/.store/@prisma-engines-npm-5.0.0-9f601585ff/node_modules/@prisma/engines/schema-engine-debian-openssl-1.1.x)
Schema Wasm             : @prisma/prisma-schema-wasm 4.17.0-26.6b0aef69b7cdfc787f822ecd7cdc76d5f1991584
Default Engines Hash    : 6b0aef69b7cdfc787f822ecd7cdc76d5f1991584
Studio                  : 0.487.0
Preview Features        : multiSchema, postgresqlExtensions, views
@dawnmist dawnmist added the kind/bug A reported bug. label Jul 16, 2023
@janpio janpio added the 5.0.0 label Jul 17, 2023
@Jolg42 Jolg42 added topic: prisma-client topic: client types Types in Prisma Client team/client Issue for team Client. topic: upsert nested upsert bug/1-unconfirmed Bug should have enough information for reproduction, but confirmation has not happened yet. tech/typescript Issue for tech TypeScript. labels Jul 17, 2023
@SevInf
Copy link
Contributor

SevInf commented Jul 18, 2023

Can confirm the issue in 4.16.2, 5.0.0 and dev. Simpler reproduction:

Schema:

model User {
  id String @id
}

Code:

const prisma = new PrismaClient().$extends({})
declare const args: Prisma.UserUpsertArgs
prisma.user.upsert(args)

As original issue mentions, it works fine on 4.16.2-dev.2, but is broken since 4.16.2-dev.3. #19942 is the PR that was merged between dev.2 and dev.3.

@SevInf SevInf added bug/2-confirmed Bug has been reproduced and confirmed. and removed bug/1-unconfirmed Bug should have enough information for reproduction, but confirmation has not happened yet. labels Jul 18, 2023
@millsp millsp self-assigned this Jul 19, 2023
@Jolg42 Jolg42 added this to the 5.1.0 milestone Jul 19, 2023
@janpio
Copy link
Member

janpio commented Jul 26, 2023

Thanks @dawnmist for reporting this, it will be fixed in 5.1 that will be released next Tuesday.

@dawnmist
Copy link
Author

Sorry to take so long to test the update.

I can confirm that the Types of property 'select' are incompatible errors have been fixed, but I'm still getting the "Excessive stack depth comparing types" error in the onStripeProductUpdated function in this example.

@millsp
Copy link
Member

millsp commented Aug 28, 2023

Hey @dawnmist sorry that we didn't resolve that. I actually tried to look into this a bit today and found that it would be very hard to make this work as it was before, here's why. Your extended client will expect its own types to be valid, and in your example you are passing it the original client's types. TypeScript knows the two types have chances of being unrelated, so it will go ahead at traverse your input type and match it against the expected one. The problem is that these are recursive types, and the comparison will never actually stop, so the answer whether they are assignable to one another cannot be found.

Now you might ask why did this work before and stopped working now? We had some type level performance issues in client extensions, so we had to refactor the code otherwise we could have impacted many people (even ones not using extensions). To achieve that, we had to change some inner workings, and inputs were one of them.

If your pattern is common enough, we may want to document it, as we do have the solution. As I pointed out, TypeScript knows when to compare the types. So the solution is to use the types from your extended client directly, we have a tool called Prisma.Args to do this:

export type StripePriceCreate = Prisma.StripePriceUncheckedCreateInput;
export type StripePriceUpdate = Prisma.StripePriceUncheckedUpdateInput;
export type StripePriceUpsert = Omit<Prisma.Args<PrismaTransactionClient['stripePrice'], 'upsert'>, 'create' | 'update'> & {
  create: StripePriceCreate;
  update: StripePriceUpdate;
};

Does this help?

@dawnmist
Copy link
Author

@millsp Thank you! That does work, and your help is greatly appreciated. 🥇

@millsp
Copy link
Member

millsp commented Aug 30, 2023

Amazing! Always happy to help :) and thank you for the high quality bug reports 💯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
5.0.0 bug/2-confirmed Bug has been reproduced and confirmed. kind/bug A reported bug. team/client Issue for team Client. tech/typescript Issue for tech TypeScript. topic: client types Types in Prisma Client topic: prisma-client topic: upsert nested upsert
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants