diff --git a/cli/packages/prisma-cli-core/src/commands/delete/index.ts b/cli/packages/prisma-cli-core/src/commands/delete/index.ts index 31dbd2974e..b21d37e14d 100644 --- a/cli/packages/prisma-cli-core/src/commands/delete/index.ts +++ b/cli/packages/prisma-cli-core/src/commands/delete/index.ts @@ -15,6 +15,10 @@ export default class Delete extends Command { description: 'Path to .env file to inject env vars', char: 'e', }), + ['project']: flags.string({ + description: 'Path to Prisma definition file', + char: 'p', + }), } async run() { const { force } = this.flags diff --git a/cli/packages/prisma-cli-core/src/commands/deploy/deploy.ts b/cli/packages/prisma-cli-core/src/commands/deploy/deploy.ts index e05ac3755f..3c854a866f 100644 --- a/cli/packages/prisma-cli-core/src/commands/deploy/deploy.ts +++ b/cli/packages/prisma-cli-core/src/commands/deploy/deploy.ts @@ -58,6 +58,10 @@ ${chalk.gray( description: 'Path to .env file to inject env vars', char: 'e', }), + ['project']: flags.string({ + description: 'Path to Prisma definition file', + char: 'p', + }), } private deploying: boolean = false private showedHooks: boolean = false diff --git a/cli/packages/prisma-cli-core/src/commands/export/index.ts b/cli/packages/prisma-cli-core/src/commands/export/index.ts index a894bfa5ba..180c2cc15b 100644 --- a/cli/packages/prisma-cli-core/src/commands/export/index.ts +++ b/cli/packages/prisma-cli-core/src/commands/export/index.ts @@ -16,6 +16,10 @@ export default class Export extends Command { description: 'Path to .env file to inject env vars', char: 'e', }), + ['project']: flags.string({ + description: 'Path to Prisma definition file', + char: 'p', + }), } async run() { let exportPath = diff --git a/cli/packages/prisma-cli-core/src/commands/generate/generate.ts b/cli/packages/prisma-cli-core/src/commands/generate/generate.ts index 8211ff9fd2..3e884c9530 100644 --- a/cli/packages/prisma-cli-core/src/commands/generate/generate.ts +++ b/cli/packages/prisma-cli-core/src/commands/generate/generate.ts @@ -27,6 +27,10 @@ export default class GenereateCommand extends Command { description: 'Path to .env file to inject env vars', char: 'e', }), + ['project']: flags.string({ + description: 'Path to Prisma definition file', + char: 'p', + }), ['endpoint']: flags.boolean({ description: 'Use a specific endpoint for schema generation or pick endpoint from prisma.yml', @@ -99,11 +103,16 @@ export default class GenereateCommand extends Command { const resolvedOutput = output.startsWith('/') ? output : path.join(this.config.definitionDir, output) - - fs.mkdirpSync(resolvedOutput) - + if (generator === 'graphql-schema') { + if (!resolvedOutput.endsWith('.graphql')) { + throw new Error(`Error: ${chalk.bold('output')} for generator ${chalk.bold('graphql-schema')} should be a ${chalk.green(chalk.bold('.graphql'))}-file. Please change the ${chalk.bold('output')} property for this generator in ${chalk.green(chalk.bold('prisma.yml'))}`) + } + + fs.mkdirpSync(path.resolve(resolvedOutput, '../')) await this.generateSchema(resolvedOutput, schemaString) + } else { + fs.mkdirpSync(resolvedOutput) } const isMongo = @@ -158,7 +167,7 @@ export default class GenereateCommand extends Command { } async generateSchema(output: string, schemaString: string) { - fs.writeFileSync(path.join(output, 'prisma.graphql'), schemaString) + fs.writeFileSync(output, schemaString) this.out.log(`Saving Prisma GraphQL schema (SDL) at ${output}`) } diff --git a/cli/packages/prisma-cli-core/src/commands/import/index.ts b/cli/packages/prisma-cli-core/src/commands/import/index.ts index efc6315059..3585a21a2d 100644 --- a/cli/packages/prisma-cli-core/src/commands/import/index.ts +++ b/cli/packages/prisma-cli-core/src/commands/import/index.ts @@ -15,6 +15,10 @@ export default class Import extends Command { description: 'Path to .env file to inject env vars', char: 'e', }), + ['project']: flags.string({ + description: 'Path to Prisma definition file', + char: 'p', + }), } async run() { const { data } = this.flags diff --git a/cli/packages/prisma-cli-core/src/commands/info/index.ts b/cli/packages/prisma-cli-core/src/commands/info/index.ts index e7c17f6e8b..7751ecae67 100644 --- a/cli/packages/prisma-cli-core/src/commands/info/index.ts +++ b/cli/packages/prisma-cli-core/src/commands/info/index.ts @@ -39,6 +39,10 @@ export default class InfoCommand extends Command { description: 'Path to .env file to inject env vars', char: 'e', }), + ['project']: flags.string({ + description: 'Path to Prisma definition file', + char: 'p', + }), } async run() { const { json, secret } = this.flags diff --git a/cli/packages/prisma-cli-core/src/commands/introspect/introspect.ts b/cli/packages/prisma-cli-core/src/commands/introspect/introspect.ts index 73e9367761..bb75d9ca7a 100644 --- a/cli/packages/prisma-cli-core/src/commands/introspect/introspect.ts +++ b/cli/packages/prisma-cli-core/src/commands/introspect/introspect.ts @@ -16,6 +16,8 @@ import { ConnectorData, getConnectorWithDatabase, IntermediateConnectorData, + populateMongoDatabase, + sanitizeMongoUri, } from './util' export default class IntrospectCommand extends Command { @@ -26,6 +28,16 @@ export default class IntrospectCommand extends Command { char: 'i', description: 'Interactive mode', }), + + ['env-file']: flags.string({ + description: 'Path to .env file to inject env vars', + char: 'e', + }), + ['project']: flags.string({ + description: 'Path to Prisma definition file', + char: 'p', + }), + /** * Postgres Params */ @@ -164,7 +176,7 @@ ${chalk.bold( const introspection = await connector.introspect(databaseName) const sdl = existingDatamodel ? await introspection.getNormalizedDatamodel(existingDatamodel) - : await introspection.getDatamodel() + : await introspection.getNormalizedDatamodel() const renderer = DefaultRenderer.create( introspection.databaseType, @@ -244,7 +256,12 @@ ${chalk.bold( async getConnector(): Promise { const hasExecuteRaw = await this.hasExecuteRaw() - const credentials = await this.getCredentials(hasExecuteRaw) + let credentials = this.getCredentialsByFlags() + let interactive = false + if (!credentials) { + credentials = await this.getCredentialsInteractively(hasExecuteRaw) + interactive = true + } if (credentials) { const { connector, @@ -255,6 +272,7 @@ ${chalk.bold( disconnect, databaseType: credentials.type, databaseName: credentials.schema, + interactive, } } @@ -282,12 +300,11 @@ ${chalk.bold( disconnect, databaseType: client.databaseType, databaseName, + interactive: false, } } - async getCredentials( - hasExecuteRaw: boolean, - ): Promise { + getCredentialsByFlags(): DatabaseCredentials | null { const requiredPostgresFlags = ['pg-host', 'pg-user', 'pg-password', 'pg-db'] const requiredMysqlFlags = ['mysql-host', 'mysql-user', 'mysql-password'] @@ -321,7 +338,7 @@ ${chalk.bold( if (mysqlFlags.length >= requiredMysqlFlags.length) { return { - host: flags['myqsl-host'], + host: flags['mysql-host'], port: parseInt(flags['mysql-port'], 10), user: flags['mysql-user'], password: flags['mysql-password'], @@ -335,7 +352,7 @@ ${chalk.bold( host: flags['pg-host'], user: flags['pg-user'], password: flags['pg-password'], - database: flags['pg-database'], + database: flags['pg-db'], port: parseInt(flags['pg-port'], 10), schema: flags['pg-schema'], // this is optional and can be undefined type: DatabaseType.postgres, @@ -343,14 +360,23 @@ ${chalk.bold( } if (flags['mongo-uri']) { + const uri = flags['mongo-uri'] + const database = flags['mongo-db'] // this is optional and can be undefined + const credentials = populateMongoDatabase({ uri, database }) return { - uri: flags['mongo-uri'], - schema: flags['mongo-db'], // this is optional and can be undefined + uri: sanitizeMongoUri(credentials.uri), + schema: credentials.database, type: DatabaseType.mongo, } } - if (flags.interactive || !hasExecuteRaw) { + return null + } + + async getCredentialsInteractively( + hasExecuteRaw: boolean, + ): Promise { + if (this.flags.interactive || !hasExecuteRaw) { const endpointDialog = new EndpointDialog({ out: this.out, client: this.client, @@ -359,11 +385,12 @@ ${chalk.bold( definition: this.definition, shouldAskForGenerator: false, }) - return await endpointDialog.getDatabase(true) + return endpointDialog.getDatabase(true) } return null } + handleMissingArgs( requiredArgs: string[], providedArgs: string[], diff --git a/cli/packages/prisma-cli-core/src/commands/introspect/util.test.ts b/cli/packages/prisma-cli-core/src/commands/introspect/util.test.ts new file mode 100644 index 0000000000..d8cad01993 --- /dev/null +++ b/cli/packages/prisma-cli-core/src/commands/introspect/util.test.ts @@ -0,0 +1,73 @@ +import { sanitizeMongoUri, populateMongoDatabase } from './util' + +test('sanitizeMongoUri', () => { + expect(sanitizeMongoUri('mongodb://localhost')).toBe( + 'mongodb://localhost/admin', + ) + expect(sanitizeMongoUri('mongodb://localhost/')).toBe( + 'mongodb://localhost/admin', + ) + expect(sanitizeMongoUri('mongodb://localhost:27017')).toBe( + 'mongodb://localhost:27017/admin', + ) + expect(sanitizeMongoUri('mongodb://localhost:27017/')).toBe( + 'mongodb://localhost:27017/admin', + ) + expect(sanitizeMongoUri('mongodb://localhost:27017/prisma')).toBe( + 'mongodb://localhost:27017/prisma', + ) + expect( + sanitizeMongoUri( + 'mongodb+srv://prisma:asdas9djasdpassword@cluster100.mongodb.net/test?retryWrites=true', + ), + ).toBe( + 'mongodb+srv://prisma:asdas9djasdpassword@cluster100.mongodb.net/test?retryWrites=true', + ) +}) + +test('populateMongoDatabase', () => { + expect(populateMongoDatabase({ uri: 'mongodb://localhost:27017/prisma' })) + .toMatchInlineSnapshot(` +Object { + "database": "prisma", + "uri": "mongodb://localhost:27017/prisma", +} +`) + expect( + populateMongoDatabase({ + uri: 'mongodb://localhost:27017/prisma', + database: 'another-db', + }), + ).toMatchInlineSnapshot(` +Object { + "database": "another-db", + "uri": "mongodb://localhost:27017/prisma", +} +`) + expect( + populateMongoDatabase({ + uri: 'mongodb://localhost:27017/prisma?authSource=admin', + }), + ).toMatchInlineSnapshot(` +Object { + "database": "prisma", + "uri": "mongodb://localhost:27017/prisma?authSource=admin", +} +`) + expect( + populateMongoDatabase({ + uri: 'mongodb://localhost:27017/', + database: 'database', + }), + ).toMatchInlineSnapshot(` +Object { + "database": "database", + "uri": "mongodb://localhost:27017/", +} +`) + expect(() => + populateMongoDatabase({ + uri: 'mongodb://localhost:27017/', + }), + ).toThrow() +}) diff --git a/cli/packages/prisma-cli-core/src/commands/introspect/util.ts b/cli/packages/prisma-cli-core/src/commands/introspect/util.ts index 9d20e16bdb..d9760df430 100644 --- a/cli/packages/prisma-cli-core/src/commands/introspect/util.ts +++ b/cli/packages/prisma-cli-core/src/commands/introspect/util.ts @@ -7,6 +7,7 @@ import { omit } from 'lodash' import { Connectors } from 'prisma-db-introspection' import { DatabaseType } from 'prisma-datamodel' import { IConnector } from 'prisma-db-introspection/dist/common/connector' +import { URL } from 'url' function replaceLocalDockerHost(credentials: DatabaseCredentials) { if (credentials.host) { @@ -53,6 +54,7 @@ export interface ConnectorData extends ConnectorAndDisconnect { export interface IntermediateConnectorData extends ConnectorAndDisconnect { databaseType: DatabaseType databaseName?: string + interactive: boolean } export async function getConnectedConnectorFromCredentials( @@ -88,7 +90,13 @@ export async function getConnectorWithDatabase( connectorData: IntermediateConnectorData, endpointDialog: EndpointDialog, ): Promise { - const { connector, disconnect, databaseType, ...result } = connectorData + const { + connector, + disconnect, + databaseType, + interactive, + ...result + } = connectorData let { databaseName } = result let schemas: string[] @@ -98,9 +106,14 @@ export async function getConnectorWithDatabase( throw new Error(`Could not connect to database. ${e.message}`) } + if (!databaseName && !interactive) { + throw new Error(`Please provide a database name`) + } + if (databaseName && !schemas.includes(databaseName)) { const schemaWord = databaseType === DatabaseType.postgres ? 'schema' : 'database' + throw new Error( `The provided ${schemaWord} "${databaseName}" does not exist. The following are available: ${schemas.join( ', ', @@ -171,3 +184,40 @@ function getConnectedMongoClient( ) }) } + +export function sanitizeMongoUri(mongoUri: string) { + const url = new URL(mongoUri) + if (url.pathname === '/' || url.pathname.length === 0) { + url.pathname = 'admin' + } + + return url.toString() +} + +export function populateMongoDatabase({ + uri, + database, +}: { + uri: string + database?: string +}): { uri: string; database: string } { + const url = new URL(uri) + if ((url.pathname === '/' || url.pathname.length === 0) && !database) { + throw new Error( + `Please provide a Mongo database in your connection string.\nRead more here https://docs.mongodb.com/manual/reference/connection-string/`, + ) + } + + if (!database) { + database = url.pathname.slice(1) + } + + return { + uri, + database, + } +} + +export function hasAuthSource(uri: string): boolean { + return new URL(uri).searchParams.has('authSource') +} diff --git a/cli/packages/prisma-cli-core/src/commands/playground/index.ts b/cli/packages/prisma-cli-core/src/commands/playground/index.ts index ee598a616d..cd75e4d117 100644 --- a/cli/packages/prisma-cli-core/src/commands/playground/index.ts +++ b/cli/packages/prisma-cli-core/src/commands/playground/index.ts @@ -37,6 +37,10 @@ export default class Playground extends Command { description: 'Path to .env file to inject env vars', char: 'e', }), + ['project']: flags.string({ + description: 'Path to Prisma definition file', + char: 'p', + }), 'server-only': flags.boolean({ char: 's', description: 'Run only the server', diff --git a/cli/packages/prisma-cli-core/src/commands/reset/reset.ts b/cli/packages/prisma-cli-core/src/commands/reset/reset.ts index dde5c57eb1..6e65c06c9b 100644 --- a/cli/packages/prisma-cli-core/src/commands/reset/reset.ts +++ b/cli/packages/prisma-cli-core/src/commands/reset/reset.ts @@ -21,6 +21,10 @@ export default class Reset extends Command { description: 'Path to .env file to inject env vars', char: 'e', }), + ['project']: flags.string({ + description: 'Path to Prisma definition file', + char: 'p', + }), } async run() { const { force } = this.flags diff --git a/cli/packages/prisma-cli-core/src/commands/seed/seed.ts b/cli/packages/prisma-cli-core/src/commands/seed/seed.ts index 7372a461f0..bc6980530d 100644 --- a/cli/packages/prisma-cli-core/src/commands/seed/seed.ts +++ b/cli/packages/prisma-cli-core/src/commands/seed/seed.ts @@ -18,6 +18,10 @@ export default class Seed extends Command { description: 'Path to .env file to inject env vars', char: 'e', }), + ['project']: flags.string({ + description: 'Path to Prisma definition file', + char: 'p', + }), } async run() { const { reset } = this.flags diff --git a/cli/packages/prisma-cli-core/src/commands/token/cluster-token.ts b/cli/packages/prisma-cli-core/src/commands/token/cluster-token.ts index 1d9f67efe8..7cf6e53da0 100644 --- a/cli/packages/prisma-cli-core/src/commands/token/cluster-token.ts +++ b/cli/packages/prisma-cli-core/src/commands/token/cluster-token.ts @@ -10,6 +10,14 @@ export default class ClusterToken extends Command { char: 'c', description: 'Copy token to clipboard', }), + ['env-file']: flags.string({ + description: 'Path to .env file to inject env vars', + char: 'e', + }), + ['project']: flags.string({ + description: 'Path to Prisma definition file', + char: 'p', + }), } async run() { const { copy } = this.flags diff --git a/cli/packages/prisma-cli-core/src/commands/token/token.ts b/cli/packages/prisma-cli-core/src/commands/token/token.ts index f0a4e044bc..cc84e4f154 100644 --- a/cli/packages/prisma-cli-core/src/commands/token/token.ts +++ b/cli/packages/prisma-cli-core/src/commands/token/token.ts @@ -13,6 +13,10 @@ export default class Token extends Command { description: 'Path to .env file to inject env vars', char: 'e', }), + ['project']: flags.string({ + description: 'Path to Prisma definition file', + char: 'p', + }), } async run() { const { copy } = this.flags diff --git a/cli/packages/prisma-cli-core/src/utils/EndpointDialog.ts b/cli/packages/prisma-cli-core/src/utils/EndpointDialog.ts index 159290a700..0a2190a53c 100644 --- a/cli/packages/prisma-cli-core/src/utils/EndpointDialog.ts +++ b/cli/packages/prisma-cli-core/src/utils/EndpointDialog.ts @@ -18,6 +18,9 @@ import { DatabaseType, DefaultRenderer } from 'prisma-datamodel' import { getConnectedConnectorFromCredentials, getConnectorWithDatabase, + sanitizeMongoUri, + hasAuthSource, + populateMongoDatabase, } from '../commands/introspect/util' export interface GetEndpointParams { @@ -349,6 +352,7 @@ export class EndpointDialog { { ...intermediateConnectorData, databaseType: credentials.type, + interactive: true, }, this, ) @@ -389,8 +393,10 @@ export class EndpointDialog { `Introspecting database ${chalk.bold(databaseName)}`, ) const introspection = await connector.introspect(databaseName) - const isdl = await introspection.getDatamodel() + + const isdl = await introspection.getNormalizedDatamodel() const renderer = DefaultRenderer.create(databaseType, this.prototype) + datamodel = renderer.render(isdl) const tableName = databaseType === DatabaseType.mongo ? 'Mongo collections' : 'tables' @@ -587,13 +593,11 @@ export class EndpointDialog { message: 'Enter MongoDB connection string', key: 'uri', }) - const alreadyData = - introspection || (await this.askForExistingDataMongo()) - if (alreadyData) { - credentials.database = await this.ask({ - message: `Enter name of existing database`, - key: 'database', - }) + credentials.uri = sanitizeMongoUri(credentials.uri) + + if (hasAuthSource(credentials.uri)) { + const { database } = populateMongoDatabase({ uri: credentials.uri }) + credentials.schema = database } } diff --git a/cli/packages/prisma-datamodel/README.md b/cli/packages/prisma-datamodel/README.md index c84f3c8da0..366037a5b5 100644 --- a/cli/packages/prisma-datamodel/README.md +++ b/cli/packages/prisma-datamodel/README.md @@ -1,3 +1,45 @@ # prisma-datamodel -Prisma Datamodel package +The Prisma Datamodel package forms the foundation of all datamodel related tasks in the CLI. + +### Components + +* Data structures to represent datamodels in memory: `ISDL`, `IGQLType`, `IGQLField`. These data structures are documented inline. The data structures might be self referencing, and all operations in this library guarantee to keep the references valid. +* Constants for known primitive types: `TypeIdentifier`, `TypeIdentifiers` +* Classes to parse data models from strings into the internal format: `Parser`, with the factory class`DefaultParser` +* Classes to render data models to strings, from the internal format: `Renderer`, with the factory class `Default Renderer` +* Auxiliary functions: `cloneSchema` to safely clone an `ISDL`structure, `toposort`to sort a datamodel in topological order. + +### Different Database Types + +When creating a parser or renderer, a flag that indicates the database type has to be passed. The internal representation is guaranteed to be consistent between different databases. It is possible to parse a mongo schema and render a postgres schema without any transformations in between. + +### Datamodel V1 vs. V1.1 + +The parser is capable of parsing both datamodel formats, and even models with mixed directives from both standards. For rendering, a flag can be passed which indicates the datamodel format to follow. + +### Modifying a Model + +The types `ISDL`, `IGQLType` and `IGQLField` are designed to allow convenient analysis and transformation. Most notably, they may contain circular references (for representing related types and indexes). Therefor, these types are **mutable**, and care has to be taken when modifying them, for example by cloning them using `cloneSchema`. + +When adding or removing a type, it is important to also update all referring fields or indexes, otherwise other transformations or the rendering process might break. + +### Usage + +Basic example: + +```typescript +const parser = DefaultParser.create(DatabaseType.mongo) +const model = parser.parse(datamodelAsString) + +// Do something with the model +for(const type of model.types) { + console.log(`${type.name} has ${type.fields.length} fields and ${type.indices.length} indexes`) +} + +const enableDatamodel1_1 = true +const renderer = DefaultRenderer.create(DatabaseType.postgres, enableDatamodel1_1) + +const renderedAsString = renderer.render(model) +``` + diff --git a/cli/packages/prisma-datamodel/src/datamodel/renderer/renderer.ts b/cli/packages/prisma-datamodel/src/datamodel/renderer/renderer.ts index 91aae4d19d..05ff5af23c 100644 --- a/cli/packages/prisma-datamodel/src/datamodel/renderer/renderer.ts +++ b/cli/packages/prisma-datamodel/src/datamodel/renderer/renderer.ts @@ -58,12 +58,19 @@ export default abstract class Renderer { } } + protected getValidIndices(type: IGQLType) { + return type.indices.filter( + index => !index.fields.some(f => f.comments.some(c => c.isError)), + ) + } + // TODO: Cleanup index rendering. protected createIndexDirectives( type: IGQLType, typeDirectives: IDirectiveInfo[], ) { - if (type.indices.length > 0) { + const validIndices = this.getValidIndices(type) + if (validIndices.length > 0) { const indexDescriptions: string[] = [] for (const index of type.indices) { indexDescriptions.push(this.createIndexDirective(index)) diff --git a/cli/packages/prisma-datamodel/src/datamodel/scalar.ts b/cli/packages/prisma-datamodel/src/datamodel/scalar.ts index 3d90eb3df0..e6e2d82542 100644 --- a/cli/packages/prisma-datamodel/src/datamodel/scalar.ts +++ b/cli/packages/prisma-datamodel/src/datamodel/scalar.ts @@ -20,3 +20,19 @@ export abstract class TypeIdentifiers { public static uuid: TypeIdentifier = 'UUID' public static json: TypeIdentifier = 'Json' } + +export const TypeIdentifierTable = { + String: true, + Int: true, + Float: true, + Boolean: true, + Long: true, + DateTime: true, + ID: true, + UUID: true, + Json: true, +} + +export function isTypeIdentifier(str: string) { + return TypeIdentifierTable[str] || false +} diff --git a/cli/packages/prisma-datamodel/src/index.ts b/cli/packages/prisma-datamodel/src/index.ts index e35a7ede46..fd50f2c6be 100644 --- a/cli/packages/prisma-datamodel/src/index.ts +++ b/cli/packages/prisma-datamodel/src/index.ts @@ -30,3 +30,4 @@ export { capitalize, camelCase, plural, dedent } from './util/util' export { toposort } from './util/sort' export { TypeIdentifier, TypeIdentifiers } from './datamodel/scalar' export { SdlExpect } from './test-helpers' +export { isTypeIdentifier } from './datamodel/scalar' diff --git a/cli/packages/prisma-db-introspection/README.md b/cli/packages/prisma-db-introspection/README.md index efa72a2215..650329d8ff 100644 --- a/cli/packages/prisma-db-introspection/README.md +++ b/cli/packages/prisma-db-introspection/README.md @@ -1,51 +1,103 @@ -## Shorthand +# prisma-db-introspection -``` -const renderedSdl = await connector.introspect(schema).renderToDatamodelString() -``` +This module is capable of generating a prisma datamodel for a relational or document databases. Please refer to the `prisma-datamodel` doc for more information on the `ISDL` datamodel structure. -## Creating a connector +## Convenient Shorthand usage +Iintrospection and rendering in one step): + +```typescript +const renderedSdl = await connector.introspect(schema).renderToNormalizdDatamodelString() ``` + +Creating a connector: + +```typescript const connector = Connectors.create(DatabaseType.mysql, client) const connector = Connectors.create(DatabaseType.postgres, client) const connector = Connectors.create(DatabaseType.mongo, client) ``` -The database client has to be connected and disconnected by the caller. +The database client has to be connected and disconnected by the caller. Please refer to the connector implementation to see the required client library. -## Introspecting +## Detailed Usage Introspect the database: -``` +```typescript const introspection = await connector.introspect(schema) ``` -Then, create an ISDL structure from the introspection: +The introspection result caches all database related information and is database specific. -``` -const sdl: ISDL = await introspection.getDatamodel() +Create an `ISDL` structure from the introspection: + +```typescript +const sdl: ISDL = await introspection.getNormalizedDatamodel() ``` or with an existing reference model: -``` +```typescript const sdl: ISDL = await introspection.getNormalizedDatamodel(referenceModel) ``` -## Rendering introspection results - -With prototype features enabled (V2) +it is also possible to get an unnormalized datamodel, basically a raw introspection result from the database. The unnormalized model is most likely not a valid datamodel. +```typescript +const sdl: ISDL = await introspection.getDatamodel() // Don't use unless you know what you're doing ``` -const renderer = Renderers.create(introspection.databaseType, prototype) + +Rendering can be done using `prisma-datamodel`. + +With prototype features enabled (V1.1): + +```typescript +const renderer = Renderers.create(introspection.databaseType, true) const renderedSdl = renderer.render(sdl) ``` -Without prototype featurs, simply use the shorthand: +Without prototype features, simply use the shorthand: +```typescript +const renderer = Renderers.create(introspection.databaseType) +const renderedSdl = renderer.render(sdl) ``` +Which is equivalent to, given the database type for rendering and introspection are the same: + +```typescript const renderedSdl = introspection.renderToDatamodelString() ``` -or with an existing reference model: -``` + +Or with an existing reference model: + +```typescript const renderedSdl = introspection.renderToNormalizedDatamodelString(referenceModel) -``` \ No newline at end of file +``` + +### Document Database Introspection Internals + +Document database introspection works by randomly sampling objects from each collection, inferring a schema for each object and merging these object schemas together. This is done in the class `ModelSampler`. + +For each field which might be a reference to another collection, we attempt to look up objects in other collections by their primary key. If we find enough referred objects, we mark the field as relation. This is done in `RelationResolver`, and can be a somewhat expensive operation. + +This approach should work for all forms of document DBs. The `MongoConnector` class can be used as a reference implementation. + +### Relational Database Introspection Internals + +Relational introspection works by querying the database schema using SQL, and then bringing everything together. + +Relations are resolved via their corresponding FK constraints, IDs are resolved via their corresponding PK constraints. + +Here, `MysqlConnector` , `MySqlIntrospectionResult` , `PostgresConnector` and `PostgresIntrospectionResult` can serve as a reference. + +There is a common base class, `RelationalConnector` which attempts to unify certain queries using the `information_schema` table, which should be standardized. This approach had limited success in practice. + +### Normalization Pipeline + +The exact normalization pipeline can be found in the `DefaultNormalizer` factory class. In the most complex case, introspecting a relational database with an existing base model, the following steps are applied: + +1. Copy Enum definitions from the existing base model. +2. Remove all relation names which are not needed, unless they are explicitly given by the base model. +3. Normalize all type and field names. If the type or field is present in the base model, copy the name and directives, as they might be known to prisma, but not to the database. +4. Re-order all models according to the base model. For new types and enums, order alphabetically. +5. Hide all reserved fields, like `updatedAt`, `createdAt`, `id`, unless they are present in the base model. +6. Adjust the cardinality of relations which use a join table according to the base model, since we cannot guess them from the database. +7. Remove all back relations for inine relations, except they are given in the datamodel. This is especially important for self-relations, which would otherwise generate duplicated fields. \ No newline at end of file diff --git a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/ambiguous-relation.test.ts.snap b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/ambiguous-relation.test.ts.snap index e2d88168d7..ec9203a436 100644 --- a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/ambiguous-relation.test.ts.snap +++ b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/ambiguous-relation.test.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Introspector test schema - 2504 1`] = ` -"type Direct_message @pgTable(name: \\"direct_messages\\") { +"type DirectMessage @pgTable(name: \\"direct_messages\\") { id: Int! @unique receiver_id: User sender_id: User @@ -10,14 +10,14 @@ exports[`Introspector test schema - 2504 1`] = ` type User @pgTable(name: \\"users\\") { id: Int! @unique - direct_messages: [Direct_message] - direct_messages: [Direct_message] + direct_messages: [DirectMessage] + direct_messages: [DirectMessage] name: String! }" `; exports[`Introspector test schema - 2504 2`] = ` -"type Direct_message @db(name: \\"direct_messages\\") { +"type DirectMessage @db(name: \\"direct_messages\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"direct_messages_id_seq\\", initialValue: 1, allocationSize: 1) receiver_id: User sender_id: User @@ -26,8 +26,8 @@ exports[`Introspector test schema - 2504 2`] = ` type User @db(name: \\"users\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"users_id_seq\\", initialValue: 1, allocationSize: 1) - direct_messages: [Direct_message] - direct_messages: [Direct_message] + direct_messages: [DirectMessage] + direct_messages: [DirectMessage] name: String! }" `; diff --git a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/cms.ts.snap b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/cms.ts.snap index f43574ea12..3f18673b22 100644 --- a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/cms.ts.snap +++ b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/cms.ts.snap @@ -15,15 +15,15 @@ exports[`Introspector CMS/sequences 1`] = ` address_street_02: String address_zip_code: String entity_type: String - provider_performance: [Provider_performance] + provider_performance: [ProviderPerformance] provider_type: String - providers_individuals: [Providers_individual] - providers_organizations: [Providers_organization] - service_provider_performance: [Service_provider_performance] - service_provider_performance_summary: [Service_provider_performance_summary] + providers_individuals: [ProvidersIndividual] + providers_organizations: [ProvidersOrganization] + service_provider_performance: [ServiceProviderPerformance] + service_provider_performance_summary: [ServiceProviderPerformanceSummary] } -type Provider_performance @indexes(value: [ +type ProviderPerformance @indexes(value: [ {name: \\"provider_performance_avg_mcare_allowed_amt_index\\", fields: [\\"avg_mcare_allowed_amt\\"]}, {name: \\"provider_performance_avg_mcare_pay_amt_index\\", fields: [\\"avg_mcare_pay_amt\\"]}, {name: \\"provider_performance_avg_mcare_standardized_amt_index\\", fields: [\\"avg_mcare_standardized_amt\\"]}, @@ -49,7 +49,7 @@ type Provider_performance @indexes(value: [ place_of_service: String! } -type Providers_individual @indexes(value: [ +type ProvidersIndividual @indexes(value: [ {name: \\"providers_individuals_gender_index\\", fields: [\\"gender\\"]} ]) @pgTable(name: \\"providers_individuals\\") { id: Provider! @pgColumn(name: \\"npi\\") @unique @@ -60,7 +60,7 @@ type Providers_individual @indexes(value: [ name_middle: String } -type Providers_organization @pgTable(name: \\"providers_organizations\\") { +type ProvidersOrganization @pgTable(name: \\"providers_organizations\\") { id: Provider! @pgColumn(name: \\"npi\\") @unique name: String } @@ -71,12 +71,12 @@ type Service @indexes(value: [ id: ID! @pgColumn(name: \\"hcpcs_code\\") @unique hcpcs_description: String hcpcs_drug_indicator: String - provider_performance: [Provider_performance] - service_performance: [Service_performance] - service_provider_performance: [Service_provider_performance] + provider_performance: [ProviderPerformance] + service_performance: [ServicePerformance] + service_provider_performance: [ServiceProviderPerformance] } -type Service_performance @indexes(value: [ +type ServicePerformance @indexes(value: [ {name: \\"service_performance_entity_type_index\\", fields: [\\"entity_type\\"]}, {name: \\"service_performance_hcpcs_code_entity_type_pk\\", fields: [\\"hcpcs_code\\", \\"entity_type\\"], unique: true}, {name: \\"service_performance_rank_avg_avg_mcare_allowed_amt_in\\", fields: [\\"rank_avg_avg_mcare_allowed_amt\\"]}, @@ -160,7 +160,7 @@ type Service_performance @indexes(value: [ var_avg_submitted_charge_amt: Float } -type Service_provider_performance @indexes(value: [ +type ServiceProviderPerformance @indexes(value: [ {name: \\"service_provider_performance_entity_type_index\\", fields: [\\"entity_type\\"]}, {name: \\"service_provider_performance_hcpcs_code_npi_place_of_service_pk\\", fields: [\\"hcpcs_code\\", \\"npi\\", \\"place_of_service\\"], unique: true}, {name: \\"service_provider_performance_rank_avg_mcare_allowed_amou\\", fields: [\\"rank_avg_mcare_allowed_amt\\"]}, @@ -202,7 +202,7 @@ type Service_provider_performance @indexes(value: [ var_avg_mcare_submitted_charge_pay_amt: Float } -type Service_provider_performance_summary @indexes(value: [ +type ServiceProviderPerformanceSummary @indexes(value: [ {name: \\"prvdr_smry_rank_est_ttl_mcare_pay_amt_by_ttl_hcpc\\", fields: [\\"rank_est_ttl_mcare_pay_amt_by_ttl_hcpcs_code\\"]}, {name: \\"prvdr_smry_rank_est_ttl_mcare_pay_amt_by_ttl_numb\\", fields: [\\"rank_est_ttl_mcare_pay_amt_by_ttl_n_of_servi\\"]}, {name: \\"prvdr_smry_rank_est_ttl_mcare_pay_amt_index\\", fields: [\\"rank_est_ttl_mcare_pay_amt\\"]}, @@ -227,17 +227,17 @@ type Service_provider_performance_summary @indexes(value: [ rank_ttl_hcpcs_code: Int rank_ttl_n_of_svcs: Int rank_var_est_ttl_mcare_submitted_charge_pay_amoun: Int - summary_type: Service_provider_performance_summary_type! + summary_type: ServiceProviderPerformanceSummaryType! ttl_hcpcs_code: String ttl_n_of_svcs: Int var_est_ttl_mcare_submitted_charge_pay_amt: Float } -type Service_provider_performance_summary_type @pgTable(name: \\"service_provider_performance_summary_type\\") { +type ServiceProviderPerformanceSummaryType @pgTable(name: \\"service_provider_performance_summary_type\\") { id: Int! @unique description: String! group_membership: Boolean! @default(value: true) - service_provider_performance_summary: [Service_provider_performance_summary] + service_provider_performance_summary: [ServiceProviderPerformanceSummary] slug: String @unique }" `; @@ -257,15 +257,15 @@ exports[`Introspector CMS/sequences 2`] = ` address_street_02: String address_zip_code: String entity_type: String - provider_performance: [Provider_performance] + provider_performance: [ProviderPerformance] provider_type: String - providers_individuals: [Providers_individual] - providers_organizations: [Providers_organization] - service_provider_performance: [Service_provider_performance] - service_provider_performance_summary: [Service_provider_performance_summary] + providers_individuals: [ProvidersIndividual] + providers_organizations: [ProvidersOrganization] + service_provider_performance: [ServiceProviderPerformance] + service_provider_performance_summary: [ServiceProviderPerformanceSummary] } -type Provider_performance @db(name: \\"provider_performance\\") @indexes(value: [ +type ProviderPerformance @db(name: \\"provider_performance\\") @indexes(value: [ {name: \\"provider_performance_avg_mcare_allowed_amt_index\\", fields: [\\"avg_mcare_allowed_amt\\"]}, {name: \\"provider_performance_avg_mcare_pay_amt_index\\", fields: [\\"avg_mcare_pay_amt\\"]}, {name: \\"provider_performance_avg_mcare_standardized_amt_index\\", fields: [\\"avg_mcare_standardized_amt\\"]}, @@ -291,7 +291,7 @@ type Provider_performance @db(name: \\"provider_performance\\") @indexes(value: place_of_service: String! } -type Providers_individual @db(name: \\"providers_individuals\\") @indexes(value: [ +type ProvidersIndividual @db(name: \\"providers_individuals\\") @indexes(value: [ {name: \\"providers_individuals_gender_index\\", fields: [\\"gender\\"]} ]) { npi: Provider! @id(strategy: NONE) @@ -302,7 +302,7 @@ type Providers_individual @db(name: \\"providers_individuals\\") @indexes(value: name_middle: String } -type Providers_organization @db(name: \\"providers_organizations\\") { +type ProvidersOrganization @db(name: \\"providers_organizations\\") { npi: Provider! @id(strategy: NONE) name: String } @@ -313,12 +313,12 @@ type Service @db(name: \\"services\\") @indexes(value: [ hcpcs_code: ID! @id(strategy: NONE) hcpcs_description: String hcpcs_drug_indicator: String - provider_performance: [Provider_performance] - service_performance: [Service_performance] - service_provider_performance: [Service_provider_performance] + provider_performance: [ProviderPerformance] + service_performance: [ServicePerformance] + service_provider_performance: [ServiceProviderPerformance] } -type Service_performance @db(name: \\"service_performance\\") @indexes(value: [ +type ServicePerformance @db(name: \\"service_performance\\") @indexes(value: [ {name: \\"service_performance_entity_type_index\\", fields: [\\"entity_type\\"]}, {name: \\"service_performance_hcpcs_code_entity_type_pk\\", fields: [\\"hcpcs_code\\", \\"entity_type\\"], unique: true}, {name: \\"service_performance_rank_avg_avg_mcare_allowed_amt_in\\", fields: [\\"rank_avg_avg_mcare_allowed_amt\\"]}, @@ -402,7 +402,7 @@ type Service_performance @db(name: \\"service_performance\\") @indexes(value: [ var_avg_submitted_charge_amt: Float } -type Service_provider_performance @db(name: \\"service_provider_performance\\") @indexes(value: [ +type ServiceProviderPerformance @db(name: \\"service_provider_performance\\") @indexes(value: [ {name: \\"service_provider_performance_entity_type_index\\", fields: [\\"entity_type\\"]}, {name: \\"service_provider_performance_hcpcs_code_npi_place_of_service_pk\\", fields: [\\"hcpcs_code\\", \\"npi\\", \\"place_of_service\\"], unique: true}, {name: \\"service_provider_performance_rank_avg_mcare_allowed_amou\\", fields: [\\"rank_avg_mcare_allowed_amt\\"]}, @@ -444,7 +444,7 @@ type Service_provider_performance @db(name: \\"service_provider_performance\\") var_avg_mcare_submitted_charge_pay_amt: Float } -type Service_provider_performance_summary @db(name: \\"service_provider_performance_summary\\") @indexes(value: [ +type ServiceProviderPerformanceSummary @db(name: \\"service_provider_performance_summary\\") @indexes(value: [ {name: \\"prvdr_smry_rank_est_ttl_mcare_pay_amt_by_ttl_hcpc\\", fields: [\\"rank_est_ttl_mcare_pay_amt_by_ttl_hcpcs_code\\"]}, {name: \\"prvdr_smry_rank_est_ttl_mcare_pay_amt_by_ttl_numb\\", fields: [\\"rank_est_ttl_mcare_pay_amt_by_ttl_n_of_servi\\"]}, {name: \\"prvdr_smry_rank_est_ttl_mcare_pay_amt_index\\", fields: [\\"rank_est_ttl_mcare_pay_amt\\"]}, @@ -469,17 +469,17 @@ type Service_provider_performance_summary @db(name: \\"service_provider_performa rank_ttl_hcpcs_code: Int rank_ttl_n_of_svcs: Int rank_var_est_ttl_mcare_submitted_charge_pay_amoun: Int - summary_type: Service_provider_performance_summary_type! + summary_type: ServiceProviderPerformanceSummaryType! ttl_hcpcs_code: String ttl_n_of_svcs: Int var_est_ttl_mcare_submitted_charge_pay_amt: Float } -type Service_provider_performance_summary_type @db(name: \\"service_provider_performance_summary_type\\") { +type ServiceProviderPerformanceSummaryType @db(name: \\"service_provider_performance_summary_type\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"service_provider_performance_summary_type_id_seq1\\", initialValue: 1, allocationSize: 1) description: String! group_membership: Boolean! @default(value: true) - service_provider_performance_summary: [Service_provider_performance_summary] + service_provider_performance_summary: [ServiceProviderPerformanceSummary] slug: String @unique }" `; diff --git a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/discourse.ts.snap b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/discourse.ts.snap index e9992f86f2..13a8b379f4 100644 --- a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/discourse.ts.snap +++ b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/discourse.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Introspector discourse 1`] = ` -"type Api_key @indexes(value: [ +"type ApiKey @indexes(value: [ {name: \\"index_api_keys_on_key\\", fields: [\\"key\\"]} ]) @pgTable(name: \\"api_keys\\") { id: Int! @unique @@ -15,7 +15,7 @@ exports[`Introspector discourse 1`] = ` user_id: Int @unique } -type Application_request @indexes(value: [ +type ApplicationRequest @indexes(value: [ {name: \\"index_application_requests_on_date_and_req_type\\", fields: [\\"date\\", \\"req_type\\"], unique: true} ]) @pgTable(name: \\"application_requests\\") { id: Int! @unique @@ -24,7 +24,7 @@ type Application_request @indexes(value: [ req_type: Int! } -type Ar_internal_metadatum @pgTable(name: \\"ar_internal_metadata\\") { +type ArInternalMetadatum @pgTable(name: \\"ar_internal_metadata\\") { id: ID! @pgColumn(name: \\"key\\") @unique created_at: DateTime! updated_at: DateTime! @@ -57,7 +57,7 @@ type Badge @indexes(value: [ updated_at: DateTime! } -type Badge_grouping @pgTable(name: \\"badge_groupings\\") { +type BadgeGrouping @pgTable(name: \\"badge_groupings\\") { id: Int! @unique created_at: DateTime! description: String @@ -66,14 +66,14 @@ type Badge_grouping @pgTable(name: \\"badge_groupings\\") { updated_at: DateTime! } -type Badge_type @pgTable(name: \\"badge_types\\") { +type BadgeType @pgTable(name: \\"badge_types\\") { id: Int! @unique created_at: DateTime! name: String! @unique updated_at: DateTime! } -type Categories_web_hook @indexes(value: [ +type CategoriesWebHook @indexes(value: [ {name: \\"index_categories_web_hooks_on_web_hook_id_and_category_id\\", fields: [\\"web_hook_id\\", \\"category_id\\"], unique: true} ]) @pgTable(name: \\"categories_web_hooks\\") { category_id: Int! @@ -133,7 +133,7 @@ type Category @indexes(value: [ user_id: Int! } -type Category_custom_field @indexes(value: [ +type CategoryCustomField @indexes(value: [ {name: \\"index_category_custom_fields_on_category_id_and_name\\", fields: [\\"category_id\\", \\"name\\"]} ]) @pgTable(name: \\"category_custom_fields\\") { id: Int! @unique @@ -144,7 +144,7 @@ type Category_custom_field @indexes(value: [ value: String } -type Category_featured_topic @indexes(value: [ +type CategoryFeaturedTopic @indexes(value: [ {name: \\"cat_featured_threads\\", fields: [\\"category_id\\", \\"topic_id\\"], unique: true}, {name: \\"index_category_featured_topics_on_category_id_and_rank\\", fields: [\\"category_id\\", \\"rank\\"]} ]) @pgTable(name: \\"category_featured_topics\\") { @@ -156,7 +156,7 @@ type Category_featured_topic @indexes(value: [ updated_at: DateTime! } -type Category_group @pgTable(name: \\"category_groups\\") { +type CategoryGroup @pgTable(name: \\"category_groups\\") { id: Int! @unique category_id: Int! created_at: DateTime! @@ -165,9 +165,7 @@ type Category_group @pgTable(name: \\"category_groups\\") { updated_at: DateTime! } -type Category_search_datum @indexes(value: [ - {name: \\"idx_search_category\\", fields: [\\"search_data\\"]} -]) @pgTable(name: \\"category_search_data\\") { +type CategorySearchDatum @pgTable(name: \\"category_search_data\\") { id: Int! @pgColumn(name: \\"category_id\\") @unique locale: String raw_data: String @@ -176,7 +174,7 @@ type Category_search_datum @indexes(value: [ version: Int @default(value: 0) } -type Category_tag @indexes(value: [ +type CategoryTag @indexes(value: [ {name: \\"idx_category_tags_ix1\\", fields: [\\"category_id\\", \\"tag_id\\"], unique: true}, {name: \\"idx_category_tags_ix2\\", fields: [\\"category_id\\", \\"tag_id\\"], unique: true} ]) @pgTable(name: \\"category_tags\\") { @@ -187,7 +185,7 @@ type Category_tag @indexes(value: [ updated_at: DateTime } -type Category_tag_group @indexes(value: [ +type CategoryTagGroup @indexes(value: [ {name: \\"idx_category_tag_groups_ix1\\", fields: [\\"category_id\\", \\"tag_group_id\\"], unique: true} ]) @pgTable(name: \\"category_tag_groups\\") { id: Int! @unique @@ -197,7 +195,7 @@ type Category_tag_group @indexes(value: [ updated_at: DateTime } -type Category_tag_stat @indexes(value: [ +type CategoryTagStat @indexes(value: [ {name: \\"index_category_tag_stats_on_category_id\\", fields: [\\"category_id\\"]}, {name: \\"index_category_tag_stats_on_category_id_and_tag_id\\", fields: [\\"category_id\\", \\"tag_id\\"], unique: true}, {name: \\"index_category_tag_stats_on_category_id_and_topic_count\\", fields: [\\"category_id\\", \\"topic_count\\"]}, @@ -209,7 +207,7 @@ type Category_tag_stat @indexes(value: [ topic_count: Int! @default(value: 0) } -type Category_user @indexes(value: [ +type CategoryUser @indexes(value: [ {name: \\"idx_category_users_u1\\", fields: [\\"category_id\\", \\"user_id\\", \\"notification_level\\"], unique: true}, {name: \\"idx_category_users_u2\\", fields: [\\"category_id\\", \\"user_id\\", \\"notification_level\\"], unique: true} ]) @pgTable(name: \\"category_users\\") { @@ -219,7 +217,7 @@ type Category_user @indexes(value: [ user_id: Int! } -type Child_theme @indexes(value: [ +type ChildTheme @indexes(value: [ {name: \\"index_child_themes_on_child_theme_id_and_parent_theme_id\\", fields: [\\"parent_theme_id\\", \\"child_theme_id\\"], unique: true}, {name: \\"index_child_themes_on_parent_theme_id_and_child_theme_id\\", fields: [\\"parent_theme_id\\", \\"child_theme_id\\"], unique: true} ]) @pgTable(name: \\"child_themes\\") { @@ -230,7 +228,7 @@ type Child_theme @indexes(value: [ updated_at: DateTime } -type Color_scheme @pgTable(name: \\"color_schemes\\") { +type ColorScheme @pgTable(name: \\"color_schemes\\") { id: Int! @unique base_scheme_id: String created_at: DateTime! @@ -241,7 +239,7 @@ type Color_scheme @pgTable(name: \\"color_schemes\\") { via_wizard: Boolean! @default(value: false) } -type Color_scheme_color @indexes(value: [ +type ColorSchemeColor @indexes(value: [ {name: \\"index_color_scheme_colors_on_color_scheme_id\\", fields: [\\"color_scheme_id\\"]} ]) @pgTable(name: \\"color_scheme_colors\\") { id: Int! @unique @@ -252,7 +250,7 @@ type Color_scheme_color @indexes(value: [ updated_at: DateTime! } -type Custom_emoji @pgTable(name: \\"custom_emojis\\") { +type CustomEmoji @pgTable(name: \\"custom_emojis\\") { id: Int! @unique created_at: DateTime! name: String! @unique @@ -265,7 +263,7 @@ type Developer @pgTable(name: \\"developers\\") { user_id: Int! } -type Directory_item @indexes(value: [ +type DirectoryItem @indexes(value: [ {name: \\"index_directory_items_on_days_visited\\", fields: [\\"days_visited\\"]}, {name: \\"index_directory_items_on_likes_given\\", fields: [\\"likes_given\\"]}, {name: \\"index_directory_items_on_likes_received\\", fields: [\\"likes_received\\"]}, @@ -302,7 +300,7 @@ type Draft @indexes(value: [ user_id: Int! } -type Draft_sequence @indexes(value: [ +type DraftSequence @indexes(value: [ {name: \\"index_draft_sequences_on_user_id_and_draft_key\\", fields: [\\"user_id\\", \\"draft_key\\"], unique: true} ]) @pgTable(name: \\"draft_sequences\\") { id: Int! @unique @@ -311,7 +309,7 @@ type Draft_sequence @indexes(value: [ user_id: Int! } -type Email_change_request @indexes(value: [ +type EmailChangeRequest @indexes(value: [ {name: \\"index_email_change_requests_on_user_id\\", fields: [\\"user_id\\"]} ]) @pgTable(name: \\"email_change_requests\\") { id: Int! @unique @@ -325,7 +323,7 @@ type Email_change_request @indexes(value: [ user_id: Int! } -type Email_log @indexes(value: [ +type EmailLog @indexes(value: [ {name: \\"index_email_logs_on_bounced\\", fields: [\\"bounced\\"]}, {name: \\"index_email_logs_on_created_at\\", fields: [\\"created_at\\"]}, {name: \\"index_email_logs_on_message_id\\", fields: [\\"message_id\\"]}, @@ -344,7 +342,7 @@ type Email_log @indexes(value: [ user_id: Int } -type Email_token @indexes(value: [ +type EmailToken @indexes(value: [ {name: \\"index_email_tokens_on_user_id\\", fields: [\\"user_id\\"]} ]) @pgTable(name: \\"email_tokens\\") { id: Int! @unique @@ -357,7 +355,7 @@ type Email_token @indexes(value: [ user_id: Int! } -type Embeddable_host @pgTable(name: \\"embeddable_hosts\\") { +type EmbeddableHost @pgTable(name: \\"embeddable_hosts\\") { id: Int! @unique category_id: Int! class_name: String @@ -367,7 +365,7 @@ type Embeddable_host @pgTable(name: \\"embeddable_hosts\\") { updated_at: DateTime } -type Facebook_user_info @pgTable(name: \\"facebook_user_infos\\") { +type FacebookUserInfo @pgTable(name: \\"facebook_user_infos\\") { id: Int! @unique about_me: String avatar_url: String @@ -386,7 +384,7 @@ type Facebook_user_info @pgTable(name: \\"facebook_user_infos\\") { website: String } -type Github_user_info @pgTable(name: \\"github_user_infos\\") { +type GithubUserInfo @pgTable(name: \\"github_user_infos\\") { id: Int! @unique created_at: DateTime! github_user_id: Int! @unique @@ -395,7 +393,7 @@ type Github_user_info @pgTable(name: \\"github_user_infos\\") { user_id: Int! @unique } -type Given_daily_like @indexes(value: [ +type GivenDailyLike @indexes(value: [ {name: \\"index_given_daily_likes_on_limit_reached_and_user_id\\", fields: [\\"user_id\\", \\"limit_reached\\"]}, {name: \\"index_given_daily_likes_on_user_id_and_given_date\\", fields: [\\"user_id\\", \\"given_date\\"], unique: true} ]) @pgTable(name: \\"given_daily_likes\\") { @@ -405,7 +403,7 @@ type Given_daily_like @indexes(value: [ user_id: Int! } -type Google_user_info @pgTable(name: \\"google_user_infos\\") { +type GoogleUserInfo @pgTable(name: \\"google_user_infos\\") { id: Int! @unique created_at: DateTime! email: String @@ -451,7 +449,7 @@ type Group @pgTable(name: \\"groups\\") { visibility_level: Int! @default(value: 0) } -type Group_archived_message @indexes(value: [ +type GroupArchivedMessage @indexes(value: [ {name: \\"index_group_archived_messages_on_group_id_and_topic_id\\", fields: [\\"group_id\\", \\"topic_id\\"], unique: true} ]) @pgTable(name: \\"group_archived_messages\\") { id: Int! @unique @@ -461,7 +459,7 @@ type Group_archived_message @indexes(value: [ updated_at: DateTime } -type Group_custom_field @indexes(value: [ +type GroupCustomField @indexes(value: [ {name: \\"index_group_custom_fields_on_group_id_and_name\\", fields: [\\"group_id\\", \\"name\\"]} ]) @pgTable(name: \\"group_custom_fields\\") { id: Int! @unique @@ -472,7 +470,7 @@ type Group_custom_field @indexes(value: [ value: String } -type Group_history @indexes(value: [ +type GroupHistory @indexes(value: [ {name: \\"index_group_histories_on_acting_user_id\\", fields: [\\"acting_user_id\\"]}, {name: \\"index_group_histories_on_action\\", fields: [\\"action\\"]}, {name: \\"index_group_histories_on_group_id\\", fields: [\\"group_id\\"]}, @@ -490,7 +488,7 @@ type Group_history @indexes(value: [ updated_at: DateTime! } -type Group_mention @indexes(value: [ +type GroupMention @indexes(value: [ {name: \\"index_group_mentions_on_group_id_and_post_id\\", fields: [\\"post_id\\", \\"group_id\\"], unique: true}, {name: \\"index_group_mentions_on_post_id_and_group_id\\", fields: [\\"post_id\\", \\"group_id\\"], unique: true} ]) @pgTable(name: \\"group_mentions\\") { @@ -501,7 +499,7 @@ type Group_mention @indexes(value: [ updated_at: DateTime } -type Group_user @indexes(value: [ +type GroupUser @indexes(value: [ {name: \\"index_group_users_on_group_id_and_user_id\\", fields: [\\"group_id\\", \\"user_id\\"], unique: true}, {name: \\"index_group_users_on_user_id_and_group_id\\", fields: [\\"group_id\\", \\"user_id\\"], unique: true} ]) @pgTable(name: \\"group_users\\") { @@ -514,14 +512,14 @@ type Group_user @indexes(value: [ user_id: Int! } -type Groups_web_hook @indexes(value: [ +type GroupsWebHook @indexes(value: [ {name: \\"index_groups_web_hooks_on_web_hook_id_and_group_id\\", fields: [\\"web_hook_id\\", \\"group_id\\"], unique: true} ]) @pgTable(name: \\"groups_web_hooks\\") { group_id: Int! web_hook_id: Int! } -type Incoming_domain @indexes(value: [ +type IncomingDomain @indexes(value: [ {name: \\"index_incoming_domains_on_name_and_https_and_port\\", fields: [\\"name\\", \\"https\\", \\"port\\"], unique: true} ]) @pgTable(name: \\"incoming_domains\\") { id: Int! @unique @@ -530,7 +528,7 @@ type Incoming_domain @indexes(value: [ port: Int! } -type Incoming_email @indexes(value: [ +type IncomingEmail @indexes(value: [ {name: \\"index_incoming_emails_on_created_at\\", fields: [\\"created_at\\"]}, {name: \\"index_incoming_emails_on_error\\", fields: [\\"error\\"]}, {name: \\"index_incoming_emails_on_message_id\\", fields: [\\"message_id\\"]}, @@ -554,7 +552,7 @@ type Incoming_email @indexes(value: [ user_id: Int } -type Incoming_link @indexes(value: [ +type IncomingLink @indexes(value: [ {name: \\"index_incoming_links_on_created_at_and_user_id\\", fields: [\\"created_at\\", \\"user_id\\"]}, {name: \\"index_incoming_links_on_post_id\\", fields: [\\"post_id\\"]} ]) @pgTable(name: \\"incoming_links\\") { @@ -568,7 +566,7 @@ type Incoming_link @indexes(value: [ user_id: Int } -type Incoming_referer @indexes(value: [ +type IncomingReferer @indexes(value: [ {name: \\"index_incoming_referers_on_path_and_incoming_domain_id\\", fields: [\\"path\\", \\"incoming_domain_id\\"], unique: true} ]) @pgTable(name: \\"incoming_referers\\") { id: Int! @unique @@ -576,7 +574,7 @@ type Incoming_referer @indexes(value: [ path: String! } -type Instagram_user_info @pgTable(name: \\"instagram_user_infos\\") { +type InstagramUserInfo @pgTable(name: \\"instagram_user_infos\\") { id: Int! @unique created_at: DateTime! instagram_user_id: Int @@ -603,7 +601,7 @@ type Invite @indexes(value: [ user_id: Int } -type Invited_group @pgTable(name: \\"invited_groups\\") { +type InvitedGroup @pgTable(name: \\"invited_groups\\") { id: Int! @unique created_at: DateTime! group_id: Int @@ -611,7 +609,7 @@ type Invited_group @pgTable(name: \\"invited_groups\\") { updated_at: DateTime! } -type Javascript_cach @indexes(value: [ +type JavascriptCach @indexes(value: [ {name: \\"index_javascript_caches_on_digest\\", fields: [\\"digest\\"]}, {name: \\"index_javascript_caches_on_theme_field_id\\", fields: [\\"theme_field_id\\"]} ]) @pgTable(name: \\"javascript_caches\\") { @@ -623,7 +621,7 @@ type Javascript_cach @indexes(value: [ updated_at: DateTime! } -type Message_bus @indexes(value: [ +type MessageBus @indexes(value: [ {name: \\"index_message_bus_on_created_at\\", fields: [\\"created_at\\"]} ]) @pgTable(name: \\"message_bus\\") { id: Int! @unique @@ -633,7 +631,7 @@ type Message_bus @indexes(value: [ name: String } -type Muted_user @indexes(value: [ +type MutedUser @indexes(value: [ {name: \\"index_muted_users_on_muted_user_id_and_user_id\\", fields: [\\"user_id\\", \\"muted_user_id\\"], unique: true}, {name: \\"index_muted_users_on_user_id_and_muted_user_id\\", fields: [\\"user_id\\", \\"muted_user_id\\"], unique: true} ]) @pgTable(name: \\"muted_users\\") { @@ -663,7 +661,7 @@ type Notification @indexes(value: [ user_id: Int! } -type Oauth2_user_info @indexes(value: [ +type Oauth2UserInfo @indexes(value: [ {name: \\"index_oauth2_user_infos_on_uid_and_provider\\", fields: [\\"uid\\", \\"provider\\"], unique: true} ]) @pgTable(name: \\"oauth2_user_infos\\") { id: Int! @unique @@ -676,7 +674,7 @@ type Oauth2_user_info @indexes(value: [ user_id: Int! } -type Onceoff_log @indexes(value: [ +type OnceoffLog @indexes(value: [ {name: \\"index_onceoff_logs_on_job_name\\", fields: [\\"job_name\\"]} ]) @pgTable(name: \\"onceoff_logs\\") { id: Int! @unique @@ -685,7 +683,7 @@ type Onceoff_log @indexes(value: [ updated_at: DateTime! } -type Optimized_image @indexes(value: [ +type OptimizedImage @indexes(value: [ {name: \\"index_optimized_images_on_upload_id\\", fields: [\\"upload_id\\"]}, {name: \\"index_optimized_images_on_upload_id_and_width_and_height\\", fields: [\\"width\\", \\"height\\", \\"upload_id\\"], unique: true} ]) @pgTable(name: \\"optimized_images\\") { @@ -710,7 +708,7 @@ type Permalink @pgTable(name: \\"permalinks\\") { url: String! @unique } -type Plugin_store_row @indexes(value: [ +type PluginStoreRow @indexes(value: [ {name: \\"index_plugin_store_rows_on_plugin_name_and_key\\", fields: [\\"plugin_name\\", \\"key\\"], unique: true} ]) @pgTable(name: \\"plugin_store_rows\\") { id: Int! @unique @@ -788,7 +786,7 @@ type Post @indexes(value: [ word_count: Int } -type Post_action @indexes(value: [ +type PostAction @indexes(value: [ {name: \\"idx_unique_actions\\", fields: [\\"post_id\\", \\"user_id\\", \\"post_action_type_id\\", \\"targets_topic\\"], unique: true}, {name: \\"idx_unique_flags\\", fields: [\\"post_id\\", \\"user_id\\", \\"targets_topic\\"], unique: true}, {name: \\"index_post_actions_on_post_id\\", fields: [\\"post_id\\"]}, @@ -813,7 +811,7 @@ type Post_action @indexes(value: [ user_id: Int! } -type Post_action_type @pgTable(name: \\"post_action_types\\") { +type PostActionType @pgTable(name: \\"post_action_types\\") { id: Int! @unique created_at: DateTime! icon: String @@ -823,7 +821,7 @@ type Post_action_type @pgTable(name: \\"post_action_types\\") { updated_at: DateTime! } -type Post_custom_field @indexes(value: [ +type PostCustomField @indexes(value: [ {name: \\"idx_post_custom_fields_akismet\\", fields: [\\"post_id\\"]}, {name: \\"index_post_custom_fields_on_name_and_value\\", fields: [\\"name\\"]}, {name: \\"index_post_custom_fields_on_post_id_and_name\\", fields: [\\"post_id\\", \\"name\\"]} @@ -836,7 +834,7 @@ type Post_custom_field @indexes(value: [ value: String } -type Post_detail @indexes(value: [ +type PostDetail @indexes(value: [ {name: \\"index_post_details_on_post_id_and_key\\", fields: [\\"post_id\\", \\"key\\"], unique: true} ]) @pgTable(name: \\"post_details\\") { id: Int! @unique @@ -848,7 +846,7 @@ type Post_detail @indexes(value: [ value: String } -type Post_reply @indexes(value: [ +type PostReply @indexes(value: [ {name: \\"index_post_replies_on_post_id_and_reply_id\\", fields: [\\"post_id\\", \\"reply_id\\"], unique: true} ]) @pgTable(name: \\"post_replies\\") { created_at: DateTime! @@ -857,7 +855,7 @@ type Post_reply @indexes(value: [ updated_at: DateTime! } -type Post_reply_key @indexes(value: [ +type PostReplyKey @indexes(value: [ {name: \\"index_post_reply_keys_on_user_id_and_post_id\\", fields: [\\"user_id\\", \\"post_id\\"], unique: true} ]) @pgTable(name: \\"post_reply_keys\\") { id: Int! @unique @@ -868,7 +866,7 @@ type Post_reply_key @indexes(value: [ user_id: Int! } -type Post_revision @indexes(value: [ +type PostRevision @indexes(value: [ {name: \\"index_post_revisions_on_post_id\\", fields: [\\"post_id\\"]}, {name: \\"index_post_revisions_on_post_id_and_number\\", fields: [\\"post_id\\", \\"number\\"]} ]) @pgTable(name: \\"post_revisions\\") { @@ -882,9 +880,7 @@ type Post_revision @indexes(value: [ user_id: Int } -type Post_search_datum @indexes(value: [ - {name: \\"idx_search_post\\", fields: [\\"search_data\\"]} -]) @pgTable(name: \\"post_search_data\\") { +type PostSearchDatum @pgTable(name: \\"post_search_data\\") { id: Int! @pgColumn(name: \\"post_id\\") @unique locale: String raw_data: String @@ -893,7 +889,7 @@ type Post_search_datum @indexes(value: [ version: Int @default(value: 0) } -type Post_stat @indexes(value: [ +type PostStat @indexes(value: [ {name: \\"index_post_stats_on_post_id\\", fields: [\\"post_id\\"]} ]) @pgTable(name: \\"post_stats\\") { id: Int! @unique @@ -905,7 +901,7 @@ type Post_stat @indexes(value: [ updated_at: DateTime } -type Post_timing @indexes(value: [ +type PostTiming @indexes(value: [ {name: \\"index_post_timings_on_user_id\\", fields: [\\"user_id\\"]}, {name: \\"post_timings_summary\\", fields: [\\"topic_id\\", \\"post_number\\"]}, {name: \\"post_timings_unique\\", fields: [\\"topic_id\\", \\"post_number\\", \\"user_id\\"], unique: true} @@ -916,7 +912,7 @@ type Post_timing @indexes(value: [ user_id: Int! } -type Post_upload @indexes(value: [ +type PostUpload @indexes(value: [ {name: \\"idx_unique_post_uploads\\", fields: [\\"post_id\\", \\"upload_id\\"], unique: true}, {name: \\"index_post_uploads_on_post_id\\", fields: [\\"post_id\\"]}, {name: \\"index_post_uploads_on_upload_id\\", fields: [\\"upload_id\\"]} @@ -926,7 +922,7 @@ type Post_upload @indexes(value: [ upload_id: Int! } -type Push_subscription @pgTable(name: \\"push_subscriptions\\") { +type PushSubscription @pgTable(name: \\"push_subscriptions\\") { id: Int! @unique created_at: DateTime! data: String! @@ -934,7 +930,7 @@ type Push_subscription @pgTable(name: \\"push_subscriptions\\") { user_id: Int! } -type Queued_post @indexes(value: [ +type QueuedPost @indexes(value: [ {name: \\"by_queue_status\\", fields: [\\"queue\\", \\"state\\", \\"created_at\\"]}, {name: \\"by_queue_status_topic\\", fields: [\\"queue\\", \\"state\\", \\"topic_id\\", \\"created_at\\"]} ]) @pgTable(name: \\"queued_posts\\") { @@ -953,7 +949,7 @@ type Queued_post @indexes(value: [ user_id: Int! } -type Quoted_post @indexes(value: [ +type QuotedPost @indexes(value: [ {name: \\"index_quoted_posts_on_post_id_and_quoted_post_id\\", fields: [\\"post_id\\", \\"quoted_post_id\\"], unique: true}, {name: \\"index_quoted_posts_on_quoted_post_id_and_post_id\\", fields: [\\"post_id\\", \\"quoted_post_id\\"], unique: true} ]) @pgTable(name: \\"quoted_posts\\") { @@ -964,7 +960,7 @@ type Quoted_post @indexes(value: [ updated_at: DateTime! } -type Remote_theme @pgTable(name: \\"remote_themes\\") { +type RemoteTheme @pgTable(name: \\"remote_themes\\") { id: Int! @unique about_url: String branch: String @@ -980,7 +976,7 @@ type Remote_theme @pgTable(name: \\"remote_themes\\") { updated_at: DateTime } -type Scheduler_stat @pgTable(name: \\"scheduler_stats\\") { +type SchedulerStat @pgTable(name: \\"scheduler_stats\\") { id: Int! @unique duration_ms: Int error: String @@ -993,11 +989,11 @@ type Scheduler_stat @pgTable(name: \\"scheduler_stats\\") { success: Boolean } -type Schema_migration @pgTable(name: \\"schema_migrations\\") { +type SchemaMigration @pgTable(name: \\"schema_migrations\\") { id: ID! @pgColumn(name: \\"version\\") @unique } -type Schema_migration_detail @indexes(value: [ +type SchemaMigrationDetail @indexes(value: [ {name: \\"index_schema_migration_details_on_version\\", fields: [\\"version\\"]} ]) @pgTable(name: \\"schema_migration_details\\") { id: Int! @unique @@ -1011,7 +1007,7 @@ type Schema_migration_detail @indexes(value: [ version: String! } -type Screened_email @indexes(value: [ +type ScreenedEmail @indexes(value: [ {name: \\"index_screened_emails_on_last_match_at\\", fields: [\\"last_match_at\\"]} ]) @pgTable(name: \\"screened_emails\\") { id: Int! @unique @@ -1025,7 +1021,7 @@ type Screened_email @indexes(value: [ updated_at: DateTime! } -type Screened_ip_address @indexes(value: [ +type ScreenedIpAddress @indexes(value: [ {name: \\"index_screened_ip_addresses_on_last_match_at\\", fields: [\\"last_match_at\\"]} ]) @pgTable(name: \\"screened_ip_addresses\\") { id: Int! @unique @@ -1038,7 +1034,7 @@ type Screened_ip_address @indexes(value: [ updated_at: DateTime! } -type Screened_url @indexes(value: [ +type ScreenedUrl @indexes(value: [ {name: \\"index_screened_urls_on_last_match_at\\", fields: [\\"last_match_at\\"]} ]) @pgTable(name: \\"screened_urls\\") { id: Int! @unique @@ -1053,7 +1049,7 @@ type Screened_url @indexes(value: [ url: String! @unique } -type Search_log @pgTable(name: \\"search_logs\\") { +type SearchLog @pgTable(name: \\"search_logs\\") { id: Int! @unique created_at: DateTime! # Type inet is not supported @@ -1065,7 +1061,7 @@ type Search_log @pgTable(name: \\"search_logs\\") { user_id: Int } -type Shared_draft @indexes(value: [ +type SharedDraft @indexes(value: [ {name: \\"index_shared_drafts_on_category_id\\", fields: [\\"category_id\\"]} ]) @pgTable(name: \\"shared_drafts\\") { id: Int! @unique @@ -1075,7 +1071,7 @@ type Shared_draft @indexes(value: [ updated_at: DateTime! } -type Single_sign_on_record @pgTable(name: \\"single_sign_on_records\\") { +type SingleSignOnRecord @pgTable(name: \\"single_sign_on_records\\") { id: Int! @unique created_at: DateTime! external_avatar_url: String @@ -1090,7 +1086,7 @@ type Single_sign_on_record @pgTable(name: \\"single_sign_on_records\\") { user_id: Int! } -type Site_setting @pgTable(name: \\"site_settings\\") { +type SiteSetting @pgTable(name: \\"site_settings\\") { id: Int! @unique created_at: DateTime! data_type: Int! @@ -1099,7 +1095,7 @@ type Site_setting @pgTable(name: \\"site_settings\\") { value: String } -type Skipped_email_log @indexes(value: [ +type SkippedEmailLog @indexes(value: [ {name: \\"index_skipped_email_logs_on_created_at\\", fields: [\\"created_at\\"]}, {name: \\"index_skipped_email_logs_on_post_id\\", fields: [\\"post_id\\"]}, {name: \\"index_skipped_email_logs_on_reason_type\\", fields: [\\"reason_type\\"]}, @@ -1116,7 +1112,7 @@ type Skipped_email_log @indexes(value: [ user_id: Int } -type Stylesheet_cache @indexes(value: [ +type StylesheetCache @indexes(value: [ {name: \\"index_stylesheet_cache_on_target_and_digest\\", fields: [\\"target\\", \\"digest\\"], unique: true} ]) @pgTable(name: \\"stylesheet_cache\\") { id: Int! @unique @@ -1138,7 +1134,7 @@ type Tag @pgTable(name: \\"tags\\") { updated_at: DateTime } -type Tag_group @pgTable(name: \\"tag_groups\\") { +type TagGroup @pgTable(name: \\"tag_groups\\") { id: Int! @unique created_at: DateTime name: String! @@ -1147,7 +1143,7 @@ type Tag_group @pgTable(name: \\"tag_groups\\") { updated_at: DateTime } -type Tag_group_membership @indexes(value: [ +type TagGroupMembership @indexes(value: [ {name: \\"index_tag_group_memberships_on_tag_group_id_and_tag_id\\", fields: [\\"tag_id\\", \\"tag_group_id\\"], unique: true} ]) @pgTable(name: \\"tag_group_memberships\\") { id: Int! @unique @@ -1157,7 +1153,7 @@ type Tag_group_membership @indexes(value: [ updated_at: DateTime } -type Tag_group_permission @indexes(value: [ +type TagGroupPermission @indexes(value: [ {name: \\"index_tag_group_permissions_on_group_id\\", fields: [\\"group_id\\"]}, {name: \\"index_tag_group_permissions_on_tag_group_id\\", fields: [\\"tag_group_id\\"]} ]) @pgTable(name: \\"tag_group_permissions\\") { @@ -1169,9 +1165,7 @@ type Tag_group_permission @indexes(value: [ updated_at: DateTime! } -type Tag_search_datum @indexes(value: [ - {name: \\"idx_search_tag\\", fields: [\\"search_data\\"]} -]) @pgTable(name: \\"tag_search_data\\") { +type TagSearchDatum @pgTable(name: \\"tag_search_data\\") { id: Int! @pgColumn(name: \\"tag_id\\") @unique locale: String raw_data: String @@ -1180,7 +1174,7 @@ type Tag_search_datum @indexes(value: [ version: Int @default(value: 0) } -type Tag_user @indexes(value: [ +type TagUser @indexes(value: [ {name: \\"idx_tag_users_ix1\\", fields: [\\"tag_id\\", \\"user_id\\", \\"notification_level\\"], unique: true}, {name: \\"idx_tag_users_ix2\\", fields: [\\"tag_id\\", \\"user_id\\", \\"notification_level\\"], unique: true} ]) @pgTable(name: \\"tag_users\\") { @@ -1206,7 +1200,7 @@ type Theme @pgTable(name: \\"themes\\") { user_selectable: Boolean! @default(value: false) } -type Theme_field @indexes(value: [ +type ThemeField @indexes(value: [ {name: \\"theme_field_unique_index\\", fields: [\\"theme_id\\", \\"target_id\\", \\"name\\", \\"type_id\\"], unique: true} ]) @pgTable(name: \\"theme_fields\\") { id: Int! @unique @@ -1223,7 +1217,7 @@ type Theme_field @indexes(value: [ value_baked: String } -type Theme_setting @pgTable(name: \\"theme_settings\\") { +type ThemeSetting @pgTable(name: \\"theme_settings\\") { id: Int! @unique created_at: DateTime! data_type: Int! @@ -1233,7 +1227,7 @@ type Theme_setting @pgTable(name: \\"theme_settings\\") { value: String } -type Top_topic @indexes(value: [ +type TopTopic @indexes(value: [ {name: \\"index_top_topics_on_all_score\\", fields: [\\"all_score\\"]}, {name: \\"index_top_topics_on_daily_likes_count\\", fields: [\\"daily_likes_count\\"]}, {name: \\"index_top_topics_on_daily_op_likes_count\\", fields: [\\"daily_op_likes_count\\"]}, @@ -1346,7 +1340,7 @@ type Topic @indexes(value: [ word_count: Int } -type Topic_allowed_group @indexes(value: [ +type TopicAllowedGroup @indexes(value: [ {name: \\"index_topic_allowed_groups_on_group_id_and_topic_id\\", fields: [\\"group_id\\", \\"topic_id\\"], unique: true}, {name: \\"index_topic_allowed_groups_on_topic_id_and_group_id\\", fields: [\\"group_id\\", \\"topic_id\\"], unique: true} ]) @pgTable(name: \\"topic_allowed_groups\\") { @@ -1355,7 +1349,7 @@ type Topic_allowed_group @indexes(value: [ topic_id: Int! } -type Topic_allowed_user @indexes(value: [ +type TopicAllowedUser @indexes(value: [ {name: \\"index_topic_allowed_users_on_topic_id_and_user_id\\", fields: [\\"user_id\\", \\"topic_id\\"], unique: true}, {name: \\"index_topic_allowed_users_on_user_id_and_topic_id\\", fields: [\\"user_id\\", \\"topic_id\\"], unique: true} ]) @pgTable(name: \\"topic_allowed_users\\") { @@ -1366,7 +1360,7 @@ type Topic_allowed_user @indexes(value: [ user_id: Int! } -type Topic_custom_field @indexes(value: [ +type TopicCustomField @indexes(value: [ {name: \\"index_topic_custom_fields_on_topic_id_and_name\\", fields: [\\"topic_id\\", \\"name\\"]}, {name: \\"topic_custom_fields_value_key_idx\\", fields: [\\"name\\", \\"value\\"]} ]) @pgTable(name: \\"topic_custom_fields\\") { @@ -1378,7 +1372,7 @@ type Topic_custom_field @indexes(value: [ value: String } -type Topic_embed @pgTable(name: \\"topic_embeds\\") { +type TopicEmbed @pgTable(name: \\"topic_embeds\\") { id: Int! @unique content_sha1: String created_at: DateTime! @@ -1390,7 +1384,7 @@ type Topic_embed @pgTable(name: \\"topic_embeds\\") { updated_at: DateTime! } -type Topic_invite @indexes(value: [ +type TopicInvite @indexes(value: [ {name: \\"index_topic_invites_on_invite_id\\", fields: [\\"invite_id\\"]}, {name: \\"index_topic_invites_on_topic_id_and_invite_id\\", fields: [\\"topic_id\\", \\"invite_id\\"], unique: true} ]) @pgTable(name: \\"topic_invites\\") { @@ -1401,7 +1395,7 @@ type Topic_invite @indexes(value: [ updated_at: DateTime! } -type Topic_link @indexes(value: [ +type TopicLink @indexes(value: [ {name: \\"index_topic_links_on_extension\\", fields: [\\"extension\\"]}, {name: \\"index_topic_links_on_link_post_id_and_reflection\\", fields: [\\"reflection\\", \\"link_post_id\\"]}, {name: \\"index_topic_links_on_post_id\\", fields: [\\"post_id\\"]}, @@ -1427,7 +1421,7 @@ type Topic_link @indexes(value: [ user_id: Int! } -type Topic_link_click @indexes(value: [ +type TopicLinkClick @indexes(value: [ {name: \\"by_link\\", fields: [\\"topic_link_id\\"]} ]) @pgTable(name: \\"topic_link_clicks\\") { id: Int! @unique @@ -1439,9 +1433,7 @@ type Topic_link_click @indexes(value: [ user_id: Int } -type Topic_search_datum @indexes(value: [ - {name: \\"idx_search_topic\\", fields: [\\"search_data\\"]} -]) @pgTable(name: \\"topic_search_data\\") { +type TopicSearchDatum @pgTable(name: \\"topic_search_data\\") { id: Int! @pgColumn(name: \\"topic_id\\") @unique locale: String! raw_data: String @@ -1450,7 +1442,7 @@ type Topic_search_datum @indexes(value: [ version: Int @default(value: 0) } -type Topic_tag @indexes(value: [ +type TopicTag @indexes(value: [ {name: \\"index_topic_tags_on_topic_id_and_tag_id\\", fields: [\\"topic_id\\", \\"tag_id\\"], unique: true} ]) @pgTable(name: \\"topic_tags\\") { id: Int! @unique @@ -1460,7 +1452,7 @@ type Topic_tag @indexes(value: [ updated_at: DateTime } -type Topic_timer @indexes(value: [ +type TopicTimer @indexes(value: [ {name: \\"index_topic_timers_on_user_id\\", fields: [\\"user_id\\"]} ]) @pgTable(name: \\"topic_timers\\") { id: Int! @unique @@ -1477,7 +1469,7 @@ type Topic_timer @indexes(value: [ user_id: Int! } -type Topic_user @indexes(value: [ +type TopicUser @indexes(value: [ {name: \\"index_topic_users_on_topic_id_and_user_id\\", fields: [\\"user_id\\", \\"topic_id\\"], unique: true}, {name: \\"index_topic_users_on_user_id_and_topic_id\\", fields: [\\"user_id\\", \\"topic_id\\"], unique: true} ]) @pgTable(name: \\"topic_users\\") { @@ -1499,7 +1491,7 @@ type Topic_user @indexes(value: [ user_id: Int! } -type Topic_view @indexes(value: [ +type TopicView @indexes(value: [ {name: \\"index_topic_views_on_topic_id_and_viewed_at\\", fields: [\\"topic_id\\", \\"viewed_at\\"]}, {name: \\"index_topic_views_on_user_id_and_viewed_at\\", fields: [\\"viewed_at\\", \\"user_id\\"]}, {name: \\"index_topic_views_on_viewed_at_and_topic_id\\", fields: [\\"topic_id\\", \\"viewed_at\\"]}, @@ -1512,7 +1504,7 @@ type Topic_view @indexes(value: [ viewed_at: DateTime! } -type Translation_override @indexes(value: [ +type TranslationOverride @indexes(value: [ {name: \\"index_translation_overrides_on_locale_and_translation_key\\", fields: [\\"locale\\", \\"translation_key\\"], unique: true} ]) @pgTable(name: \\"translation_overrides\\") { id: Int! @unique @@ -1524,7 +1516,7 @@ type Translation_override @indexes(value: [ value: String! } -type Twitter_user_info @pgTable(name: \\"twitter_user_infos\\") { +type TwitterUserInfo @pgTable(name: \\"twitter_user_infos\\") { id: Int! @unique created_at: DateTime! email: String @@ -1534,7 +1526,7 @@ type Twitter_user_info @pgTable(name: \\"twitter_user_infos\\") { user_id: Int! @unique } -type Unsubscribe_key @indexes(value: [ +type UnsubscribeKey @indexes(value: [ {name: \\"index_unsubscribe_keys_on_created_at\\", fields: [\\"created_at\\"]} ]) @pgTable(name: \\"unsubscribe_keys\\") { id: ID! @pgColumn(name: \\"key\\") @unique @@ -1615,7 +1607,7 @@ type User @indexes(value: [ views: Int! @default(value: 0) } -type User_action @indexes(value: [ +type UserAction @indexes(value: [ {name: \\"idx_unique_rows\\", fields: [\\"action_type\\", \\"user_id\\", \\"target_topic_id\\", \\"target_post_id\\", \\"acting_user_id\\"], unique: true}, {name: \\"idx_user_actions_speed_up_user_all\\", fields: [\\"action_type\\", \\"user_id\\", \\"created_at\\"]}, {name: \\"index_user_actions_on_acting_user_id\\", fields: [\\"acting_user_id\\"]}, @@ -1634,7 +1626,7 @@ type User_action @indexes(value: [ user_id: Int! } -type User_api_key @indexes(value: [ +type UserApiKey @indexes(value: [ {name: \\"index_user_api_keys_on_user_id\\", fields: [\\"user_id\\"]} ]) @pgTable(name: \\"user_api_keys\\") { id: Int! @unique @@ -1650,7 +1642,7 @@ type User_api_key @indexes(value: [ user_id: Int! } -type User_archived_message @indexes(value: [ +type UserArchivedMessage @indexes(value: [ {name: \\"index_user_archived_messages_on_user_id_and_topic_id\\", fields: [\\"user_id\\", \\"topic_id\\"], unique: true} ]) @pgTable(name: \\"user_archived_messages\\") { id: Int! @unique @@ -1660,7 +1652,7 @@ type User_archived_message @indexes(value: [ user_id: Int! } -type User_auth_token @pgTable(name: \\"user_auth_tokens\\") { +type UserAuthToken @pgTable(name: \\"user_auth_tokens\\") { id: Int! @unique auth_token: String! @unique auth_token_seen: Boolean! @default(value: false) @@ -1675,7 +1667,7 @@ type User_auth_token @pgTable(name: \\"user_auth_tokens\\") { user_id: Int! } -type User_auth_token_log @pgTable(name: \\"user_auth_token_logs\\") { +type UserAuthTokenLog @pgTable(name: \\"user_auth_token_logs\\") { id: Int! @unique action: String! auth_token: String @@ -1688,7 +1680,7 @@ type User_auth_token_log @pgTable(name: \\"user_auth_token_logs\\") { user_id: Int } -type User_avatar @indexes(value: [ +type UserAvatar @indexes(value: [ {name: \\"index_user_avatars_on_custom_upload_id\\", fields: [\\"custom_upload_id\\"]}, {name: \\"index_user_avatars_on_gravatar_upload_id\\", fields: [\\"gravatar_upload_id\\"]}, {name: \\"index_user_avatars_on_user_id\\", fields: [\\"user_id\\"]} @@ -1702,7 +1694,7 @@ type User_avatar @indexes(value: [ user_id: Int! } -type User_badge @indexes(value: [ +type UserBadge @indexes(value: [ {name: \\"index_user_badges_on_badge_id_and_user_id\\", fields: [\\"badge_id\\", \\"user_id\\"]}, {name: \\"index_user_badges_on_badge_id_and_user_id_and_post_id\\", fields: [\\"badge_id\\", \\"user_id\\", \\"post_id\\"], unique: true}, {name: \\"index_user_badges_on_badge_id_and_user_id_and_seq\\", fields: [\\"badge_id\\", \\"user_id\\", \\"seq\\"], unique: true}, @@ -1718,7 +1710,7 @@ type User_badge @indexes(value: [ user_id: Int! } -type User_custom_field @indexes(value: [ +type UserCustomField @indexes(value: [ {name: \\"index_user_custom_fields_on_user_id_and_name\\", fields: [\\"user_id\\", \\"name\\"]} ]) @pgTable(name: \\"user_custom_fields\\") { id: Int! @unique @@ -1729,7 +1721,7 @@ type User_custom_field @indexes(value: [ value: String } -type User_email @indexes(value: [ +type UserEmail @indexes(value: [ {name: \\"index_user_emails_on_user_id\\", fields: [\\"user_id\\"]}, {name: \\"index_user_emails_on_user_id_and_primary\\", fields: [\\"user_id\\", \\"primary\\"], unique: true} ]) @pgTable(name: \\"user_emails\\") { @@ -1741,7 +1733,7 @@ type User_email @indexes(value: [ user_id: Int! } -type User_export @pgTable(name: \\"user_exports\\") { +type UserExport @pgTable(name: \\"user_exports\\") { id: Int! @unique created_at: DateTime file_name: String! @@ -1750,7 +1742,7 @@ type User_export @pgTable(name: \\"user_exports\\") { user_id: Int! } -type User_field @pgTable(name: \\"user_fields\\") { +type UserField @pgTable(name: \\"user_fields\\") { id: Int! @unique created_at: DateTime description: String! @@ -1766,7 +1758,7 @@ type User_field @pgTable(name: \\"user_fields\\") { updated_at: DateTime } -type User_field_option @pgTable(name: \\"user_field_options\\") { +type UserFieldOption @pgTable(name: \\"user_field_options\\") { id: Int! @unique created_at: DateTime updated_at: DateTime @@ -1774,7 +1766,7 @@ type User_field_option @pgTable(name: \\"user_field_options\\") { value: String! } -type User_history @indexes(value: [ +type UserHistory @indexes(value: [ {name: \\"index_user_histories_on_acting_user_id_and_action_and_id\\", fields: [\\"id\\", \\"action\\", \\"acting_user_id\\"]}, {name: \\"index_user_histories_on_action_and_id\\", fields: [\\"id\\", \\"action\\"]}, {name: \\"index_user_histories_on_category_id\\", fields: [\\"category_id\\"]}, @@ -1801,7 +1793,7 @@ type User_history @indexes(value: [ updated_at: DateTime! } -type User_open_id @indexes(value: [ +type UserOpenId @indexes(value: [ {name: \\"index_user_open_ids_on_url\\", fields: [\\"url\\"]} ]) @pgTable(name: \\"user_open_ids\\") { id: Int! @unique @@ -1813,7 +1805,7 @@ type User_open_id @indexes(value: [ user_id: Int! } -type User_option @pgTable(name: \\"user_options\\") { +type UserOption @pgTable(name: \\"user_options\\") { id: Int! @pgColumn(name: \\"user_id\\") @unique allow_private_messages: Boolean! @default(value: true) auto_track_topics_after_msecs: Int @@ -1842,7 +1834,7 @@ type User_option @pgTable(name: \\"user_options\\") { theme_key_seq: Int! @default(value: 0) } -type User_profile @indexes(value: [ +type UserProfile @indexes(value: [ {name: \\"index_user_profiles_on_bio_cooked_version\\", fields: [\\"bio_cooked_version\\"]}, {name: \\"index_user_profiles_on_card_background\\", fields: [\\"card_background\\"]}, {name: \\"index_user_profiles_on_profile_background\\", fields: [\\"profile_background\\"]} @@ -1860,7 +1852,7 @@ type User_profile @indexes(value: [ website: String } -type User_profile_view @indexes(value: [ +type UserProfileView @indexes(value: [ {name: \\"index_user_profile_views_on_user_id\\", fields: [\\"user_id\\"]}, {name: \\"index_user_profile_views_on_user_profile_id\\", fields: [\\"user_profile_id\\"]}, {name: \\"unique_profile_view_user_or_ip\\", fields: [\\"user_profile_id\\", \\"viewed_at\\", \\"ip_address\\", \\"user_id\\"], unique: true} @@ -1873,9 +1865,7 @@ type User_profile_view @indexes(value: [ viewed_at: DateTime! } -type User_search_datum @indexes(value: [ - {name: \\"idx_search_user\\", fields: [\\"search_data\\"]} -]) @pgTable(name: \\"user_search_data\\") { +type UserSearchDatum @pgTable(name: \\"user_search_data\\") { id: Int! @pgColumn(name: \\"user_id\\") @unique locale: String raw_data: String @@ -1884,7 +1874,7 @@ type User_search_datum @indexes(value: [ version: Int @default(value: 0) } -type User_second_factor @indexes(value: [ +type UserSecondFactor @indexes(value: [ {name: \\"index_user_second_factors_on_user_id\\", fields: [\\"user_id\\"]} ]) @pgTable(name: \\"user_second_factors\\") { id: Int! @unique @@ -1897,7 +1887,7 @@ type User_second_factor @indexes(value: [ user_id: Int! } -type User_stat @pgTable(name: \\"user_stats\\") { +type UserStat @pgTable(name: \\"user_stats\\") { id: Int! @pgColumn(name: \\"user_id\\") @unique bounce_score: Float! @default(value: 0) days_visited: Int! @default(value: 0) @@ -1915,7 +1905,7 @@ type User_stat @pgTable(name: \\"user_stats\\") { topics_entered: Int! @default(value: 0) } -type User_upload @indexes(value: [ +type UserUpload @indexes(value: [ {name: \\"index_user_uploads_on_upload_id_and_user_id\\", fields: [\\"upload_id\\", \\"user_id\\"], unique: true} ]) @pgTable(name: \\"user_uploads\\") { id: Int! @unique @@ -1924,7 +1914,7 @@ type User_upload @indexes(value: [ user_id: Int! } -type User_visit @indexes(value: [ +type UserVisit @indexes(value: [ {name: \\"index_user_visits_on_user_id_and_visited_at\\", fields: [\\"user_id\\", \\"visited_at\\"], unique: true}, {name: \\"index_user_visits_on_user_id_and_visited_at_and_time_read\\", fields: [\\"user_id\\", \\"visited_at\\", \\"time_read\\"]}, {name: \\"index_user_visits_on_visited_at_and_mobile\\", fields: [\\"visited_at\\", \\"mobile\\"]} @@ -1937,7 +1927,7 @@ type User_visit @indexes(value: [ visited_at: DateTime! } -type User_warning @indexes(value: [ +type UserWarning @indexes(value: [ {name: \\"index_user_warnings_on_user_id\\", fields: [\\"user_id\\"]} ]) @pgTable(name: \\"user_warnings\\") { id: Int! @unique @@ -1948,7 +1938,7 @@ type User_warning @indexes(value: [ user_id: Int! } -type Watched_word @indexes(value: [ +type WatchedWord @indexes(value: [ {name: \\"index_watched_words_on_action_and_word\\", fields: [\\"word\\", \\"action\\"], unique: true} ]) @pgTable(name: \\"watched_words\\") { id: Int! @unique @@ -1958,7 +1948,7 @@ type Watched_word @indexes(value: [ word: String! } -type Web_crawler_request @indexes(value: [ +type WebCrawlerRequest @indexes(value: [ {name: \\"index_web_crawler_requests_on_date_and_user_agent\\", fields: [\\"date\\", \\"user_agent\\"], unique: true} ]) @pgTable(name: \\"web_crawler_requests\\") { id: Int! @unique @@ -1967,7 +1957,7 @@ type Web_crawler_request @indexes(value: [ user_agent: String! } -type Web_hook @pgTable(name: \\"web_hooks\\") { +type WebHook @pgTable(name: \\"web_hooks\\") { id: Int! @unique active: Boolean! @default(value: false) content_type: Int! @default(value: 1) @@ -1981,7 +1971,7 @@ type Web_hook @pgTable(name: \\"web_hooks\\") { wildcard_web_hook: Boolean! @default(value: false) } -type Web_hook_event @indexes(value: [ +type WebHookEvent @indexes(value: [ {name: \\"index_web_hook_events_on_web_hook_id\\", fields: [\\"web_hook_id\\"]} ]) @pgTable(name: \\"web_hook_events\\") { id: Int! @unique @@ -1996,12 +1986,12 @@ type Web_hook_event @indexes(value: [ web_hook_id: Int! } -type Web_hook_event_type @pgTable(name: \\"web_hook_event_types\\") { +type WebHookEventType @pgTable(name: \\"web_hook_event_types\\") { id: Int! @unique name: String! } -type Web_hook_event_types_hook @indexes(value: [ +type WebHookEventTypesHook @indexes(value: [ {name: \\"idx_web_hook_event_types_hooks_on_ids\\", fields: [\\"web_hook_id\\", \\"web_hook_event_type_id\\"], unique: true} ]) @pgTable(name: \\"web_hook_event_types_hooks\\") { web_hook_event_type_id: Int! @@ -2010,7 +2000,7 @@ type Web_hook_event_types_hook @indexes(value: [ `; exports[`Introspector discourse 2`] = ` -"type Api_key @db(name: \\"api_keys\\") @indexes(value: [ +"type ApiKey @db(name: \\"api_keys\\") @indexes(value: [ {name: \\"index_api_keys_on_key\\", fields: [\\"key\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"api_keys_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2024,7 +2014,7 @@ exports[`Introspector discourse 2`] = ` user_id: Int @unique } -type Application_request @db(name: \\"application_requests\\") @indexes(value: [ +type ApplicationRequest @db(name: \\"application_requests\\") @indexes(value: [ {name: \\"index_application_requests_on_date_and_req_type\\", fields: [\\"date\\", \\"req_type\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"application_requests_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2033,7 +2023,7 @@ type Application_request @db(name: \\"application_requests\\") @indexes(value: [ req_type: Int! } -type Ar_internal_metadatum @db(name: \\"ar_internal_metadata\\") { +type ArInternalMetadatum @db(name: \\"ar_internal_metadata\\") { key: ID! @id(strategy: NONE) created_at: DateTime! updated_at: DateTime! @@ -2066,7 +2056,7 @@ type Badge @db(name: \\"badges\\") @indexes(value: [ updated_at: DateTime! } -type Badge_grouping @db(name: \\"badge_groupings\\") { +type BadgeGrouping @db(name: \\"badge_groupings\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"badge_groupings_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! description: String @@ -2075,14 +2065,14 @@ type Badge_grouping @db(name: \\"badge_groupings\\") { updated_at: DateTime! } -type Badge_type @db(name: \\"badge_types\\") { +type BadgeType @db(name: \\"badge_types\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"badge_types_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! name: String! @unique updated_at: DateTime! } -type Categories_web_hook @db(name: \\"categories_web_hooks\\") @indexes(value: [ +type CategoriesWebHook @db(name: \\"categories_web_hooks\\") @indexes(value: [ {name: \\"index_categories_web_hooks_on_web_hook_id_and_category_id\\", fields: [\\"web_hook_id\\", \\"category_id\\"], unique: true} ]) { category_id: Int! @@ -2142,7 +2132,7 @@ type Category @db(name: \\"categories\\") @indexes(value: [ user_id: Int! } -type Category_custom_field @db(name: \\"category_custom_fields\\") @indexes(value: [ +type CategoryCustomField @db(name: \\"category_custom_fields\\") @indexes(value: [ {name: \\"index_category_custom_fields_on_category_id_and_name\\", fields: [\\"category_id\\", \\"name\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"category_custom_fields_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2153,7 +2143,7 @@ type Category_custom_field @db(name: \\"category_custom_fields\\") @indexes(valu value: String } -type Category_featured_topic @db(name: \\"category_featured_topics\\") @indexes(value: [ +type CategoryFeaturedTopic @db(name: \\"category_featured_topics\\") @indexes(value: [ {name: \\"cat_featured_threads\\", fields: [\\"category_id\\", \\"topic_id\\"], unique: true}, {name: \\"index_category_featured_topics_on_category_id_and_rank\\", fields: [\\"category_id\\", \\"rank\\"]} ]) { @@ -2165,7 +2155,7 @@ type Category_featured_topic @db(name: \\"category_featured_topics\\") @indexes( updated_at: DateTime! } -type Category_group @db(name: \\"category_groups\\") { +type CategoryGroup @db(name: \\"category_groups\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"category_groups_id_seq\\", initialValue: 1, allocationSize: 1) category_id: Int! created_at: DateTime! @@ -2174,9 +2164,7 @@ type Category_group @db(name: \\"category_groups\\") { updated_at: DateTime! } -type Category_search_datum @db(name: \\"category_search_data\\") @indexes(value: [ - {name: \\"idx_search_category\\", fields: [\\"search_data\\"]} -]) { +type CategorySearchDatum @db(name: \\"category_search_data\\") { category_id: Int! @id(strategy: NONE) locale: String raw_data: String @@ -2185,7 +2173,7 @@ type Category_search_datum @db(name: \\"category_search_data\\") @indexes(value: version: Int @default(value: 0) } -type Category_tag @db(name: \\"category_tags\\") @indexes(value: [ +type CategoryTag @db(name: \\"category_tags\\") @indexes(value: [ {name: \\"idx_category_tags_ix1\\", fields: [\\"category_id\\", \\"tag_id\\"], unique: true}, {name: \\"idx_category_tags_ix2\\", fields: [\\"category_id\\", \\"tag_id\\"], unique: true} ]) { @@ -2196,7 +2184,7 @@ type Category_tag @db(name: \\"category_tags\\") @indexes(value: [ updated_at: DateTime } -type Category_tag_group @db(name: \\"category_tag_groups\\") @indexes(value: [ +type CategoryTagGroup @db(name: \\"category_tag_groups\\") @indexes(value: [ {name: \\"idx_category_tag_groups_ix1\\", fields: [\\"category_id\\", \\"tag_group_id\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"category_tag_groups_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2206,7 +2194,7 @@ type Category_tag_group @db(name: \\"category_tag_groups\\") @indexes(value: [ updated_at: DateTime } -type Category_tag_stat @db(name: \\"category_tag_stats\\") @indexes(value: [ +type CategoryTagStat @db(name: \\"category_tag_stats\\") @indexes(value: [ {name: \\"index_category_tag_stats_on_category_id\\", fields: [\\"category_id\\"]}, {name: \\"index_category_tag_stats_on_category_id_and_tag_id\\", fields: [\\"category_id\\", \\"tag_id\\"], unique: true}, {name: \\"index_category_tag_stats_on_category_id_and_topic_count\\", fields: [\\"category_id\\", \\"topic_count\\"]}, @@ -2218,7 +2206,7 @@ type Category_tag_stat @db(name: \\"category_tag_stats\\") @indexes(value: [ topic_count: Int! @default(value: 0) } -type Category_user @db(name: \\"category_users\\") @indexes(value: [ +type CategoryUser @db(name: \\"category_users\\") @indexes(value: [ {name: \\"idx_category_users_u1\\", fields: [\\"category_id\\", \\"user_id\\", \\"notification_level\\"], unique: true}, {name: \\"idx_category_users_u2\\", fields: [\\"category_id\\", \\"user_id\\", \\"notification_level\\"], unique: true} ]) { @@ -2228,7 +2216,7 @@ type Category_user @db(name: \\"category_users\\") @indexes(value: [ user_id: Int! } -type Child_theme @db(name: \\"child_themes\\") @indexes(value: [ +type ChildTheme @db(name: \\"child_themes\\") @indexes(value: [ {name: \\"index_child_themes_on_child_theme_id_and_parent_theme_id\\", fields: [\\"parent_theme_id\\", \\"child_theme_id\\"], unique: true}, {name: \\"index_child_themes_on_parent_theme_id_and_child_theme_id\\", fields: [\\"parent_theme_id\\", \\"child_theme_id\\"], unique: true} ]) { @@ -2239,7 +2227,7 @@ type Child_theme @db(name: \\"child_themes\\") @indexes(value: [ updated_at: DateTime } -type Color_scheme @db(name: \\"color_schemes\\") { +type ColorScheme @db(name: \\"color_schemes\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"color_schemes_id_seq\\", initialValue: 1, allocationSize: 1) base_scheme_id: String created_at: DateTime! @@ -2250,7 +2238,7 @@ type Color_scheme @db(name: \\"color_schemes\\") { via_wizard: Boolean! @default(value: false) } -type Color_scheme_color @db(name: \\"color_scheme_colors\\") @indexes(value: [ +type ColorSchemeColor @db(name: \\"color_scheme_colors\\") @indexes(value: [ {name: \\"index_color_scheme_colors_on_color_scheme_id\\", fields: [\\"color_scheme_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"color_scheme_colors_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2261,7 +2249,7 @@ type Color_scheme_color @db(name: \\"color_scheme_colors\\") @indexes(value: [ updated_at: DateTime! } -type Custom_emoji @db(name: \\"custom_emojis\\") { +type CustomEmoji @db(name: \\"custom_emojis\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"custom_emojis_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! name: String! @unique @@ -2274,7 +2262,7 @@ type Developer @db(name: \\"developers\\") { user_id: Int! } -type Directory_item @db(name: \\"directory_items\\") @indexes(value: [ +type DirectoryItem @db(name: \\"directory_items\\") @indexes(value: [ {name: \\"index_directory_items_on_days_visited\\", fields: [\\"days_visited\\"]}, {name: \\"index_directory_items_on_likes_given\\", fields: [\\"likes_given\\"]}, {name: \\"index_directory_items_on_likes_received\\", fields: [\\"likes_received\\"]}, @@ -2311,7 +2299,7 @@ type Draft @db(name: \\"drafts\\") @indexes(value: [ user_id: Int! } -type Draft_sequence @db(name: \\"draft_sequences\\") @indexes(value: [ +type DraftSequence @db(name: \\"draft_sequences\\") @indexes(value: [ {name: \\"index_draft_sequences_on_user_id_and_draft_key\\", fields: [\\"user_id\\", \\"draft_key\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"draft_sequences_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2320,7 +2308,7 @@ type Draft_sequence @db(name: \\"draft_sequences\\") @indexes(value: [ user_id: Int! } -type Email_change_request @db(name: \\"email_change_requests\\") @indexes(value: [ +type EmailChangeRequest @db(name: \\"email_change_requests\\") @indexes(value: [ {name: \\"index_email_change_requests_on_user_id\\", fields: [\\"user_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"email_change_requests_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2334,7 +2322,7 @@ type Email_change_request @db(name: \\"email_change_requests\\") @indexes(value: user_id: Int! } -type Email_log @db(name: \\"email_logs\\") @indexes(value: [ +type EmailLog @db(name: \\"email_logs\\") @indexes(value: [ {name: \\"index_email_logs_on_bounced\\", fields: [\\"bounced\\"]}, {name: \\"index_email_logs_on_created_at\\", fields: [\\"created_at\\"]}, {name: \\"index_email_logs_on_message_id\\", fields: [\\"message_id\\"]}, @@ -2353,7 +2341,7 @@ type Email_log @db(name: \\"email_logs\\") @indexes(value: [ user_id: Int } -type Email_token @db(name: \\"email_tokens\\") @indexes(value: [ +type EmailToken @db(name: \\"email_tokens\\") @indexes(value: [ {name: \\"index_email_tokens_on_user_id\\", fields: [\\"user_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"email_tokens_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2366,7 +2354,7 @@ type Email_token @db(name: \\"email_tokens\\") @indexes(value: [ user_id: Int! } -type Embeddable_host @db(name: \\"embeddable_hosts\\") { +type EmbeddableHost @db(name: \\"embeddable_hosts\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"embeddable_hosts_id_seq\\", initialValue: 1, allocationSize: 1) category_id: Int! class_name: String @@ -2376,7 +2364,7 @@ type Embeddable_host @db(name: \\"embeddable_hosts\\") { updated_at: DateTime } -type Facebook_user_info @db(name: \\"facebook_user_infos\\") { +type FacebookUserInfo @db(name: \\"facebook_user_infos\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"facebook_user_infos_id_seq\\", initialValue: 1, allocationSize: 1) about_me: String avatar_url: String @@ -2395,7 +2383,7 @@ type Facebook_user_info @db(name: \\"facebook_user_infos\\") { website: String } -type Github_user_info @db(name: \\"github_user_infos\\") { +type GithubUserInfo @db(name: \\"github_user_infos\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"github_user_infos_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! github_user_id: Int! @unique @@ -2404,7 +2392,7 @@ type Github_user_info @db(name: \\"github_user_infos\\") { user_id: Int! @unique } -type Given_daily_like @db(name: \\"given_daily_likes\\") @indexes(value: [ +type GivenDailyLike @db(name: \\"given_daily_likes\\") @indexes(value: [ {name: \\"index_given_daily_likes_on_limit_reached_and_user_id\\", fields: [\\"user_id\\", \\"limit_reached\\"]}, {name: \\"index_given_daily_likes_on_user_id_and_given_date\\", fields: [\\"user_id\\", \\"given_date\\"], unique: true} ]) { @@ -2414,7 +2402,7 @@ type Given_daily_like @db(name: \\"given_daily_likes\\") @indexes(value: [ user_id: Int! } -type Google_user_info @db(name: \\"google_user_infos\\") { +type GoogleUserInfo @db(name: \\"google_user_infos\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"google_user_infos_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! email: String @@ -2460,7 +2448,7 @@ type Group @db(name: \\"groups\\") { visibility_level: Int! @default(value: 0) } -type Group_archived_message @db(name: \\"group_archived_messages\\") @indexes(value: [ +type GroupArchivedMessage @db(name: \\"group_archived_messages\\") @indexes(value: [ {name: \\"index_group_archived_messages_on_group_id_and_topic_id\\", fields: [\\"group_id\\", \\"topic_id\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"group_archived_messages_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2470,7 +2458,7 @@ type Group_archived_message @db(name: \\"group_archived_messages\\") @indexes(va updated_at: DateTime } -type Group_custom_field @db(name: \\"group_custom_fields\\") @indexes(value: [ +type GroupCustomField @db(name: \\"group_custom_fields\\") @indexes(value: [ {name: \\"index_group_custom_fields_on_group_id_and_name\\", fields: [\\"group_id\\", \\"name\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"group_custom_fields_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2481,7 +2469,7 @@ type Group_custom_field @db(name: \\"group_custom_fields\\") @indexes(value: [ value: String } -type Group_history @db(name: \\"group_histories\\") @indexes(value: [ +type GroupHistory @db(name: \\"group_histories\\") @indexes(value: [ {name: \\"index_group_histories_on_acting_user_id\\", fields: [\\"acting_user_id\\"]}, {name: \\"index_group_histories_on_action\\", fields: [\\"action\\"]}, {name: \\"index_group_histories_on_group_id\\", fields: [\\"group_id\\"]}, @@ -2499,7 +2487,7 @@ type Group_history @db(name: \\"group_histories\\") @indexes(value: [ updated_at: DateTime! } -type Group_mention @db(name: \\"group_mentions\\") @indexes(value: [ +type GroupMention @db(name: \\"group_mentions\\") @indexes(value: [ {name: \\"index_group_mentions_on_group_id_and_post_id\\", fields: [\\"post_id\\", \\"group_id\\"], unique: true}, {name: \\"index_group_mentions_on_post_id_and_group_id\\", fields: [\\"post_id\\", \\"group_id\\"], unique: true} ]) { @@ -2510,7 +2498,7 @@ type Group_mention @db(name: \\"group_mentions\\") @indexes(value: [ updated_at: DateTime } -type Group_user @db(name: \\"group_users\\") @indexes(value: [ +type GroupUser @db(name: \\"group_users\\") @indexes(value: [ {name: \\"index_group_users_on_group_id_and_user_id\\", fields: [\\"group_id\\", \\"user_id\\"], unique: true}, {name: \\"index_group_users_on_user_id_and_group_id\\", fields: [\\"group_id\\", \\"user_id\\"], unique: true} ]) { @@ -2523,14 +2511,14 @@ type Group_user @db(name: \\"group_users\\") @indexes(value: [ user_id: Int! } -type Groups_web_hook @db(name: \\"groups_web_hooks\\") @indexes(value: [ +type GroupsWebHook @db(name: \\"groups_web_hooks\\") @indexes(value: [ {name: \\"index_groups_web_hooks_on_web_hook_id_and_group_id\\", fields: [\\"web_hook_id\\", \\"group_id\\"], unique: true} ]) { group_id: Int! web_hook_id: Int! } -type Incoming_domain @db(name: \\"incoming_domains\\") @indexes(value: [ +type IncomingDomain @db(name: \\"incoming_domains\\") @indexes(value: [ {name: \\"index_incoming_domains_on_name_and_https_and_port\\", fields: [\\"name\\", \\"https\\", \\"port\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"incoming_domains_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2539,7 +2527,7 @@ type Incoming_domain @db(name: \\"incoming_domains\\") @indexes(value: [ port: Int! } -type Incoming_email @db(name: \\"incoming_emails\\") @indexes(value: [ +type IncomingEmail @db(name: \\"incoming_emails\\") @indexes(value: [ {name: \\"index_incoming_emails_on_created_at\\", fields: [\\"created_at\\"]}, {name: \\"index_incoming_emails_on_error\\", fields: [\\"error\\"]}, {name: \\"index_incoming_emails_on_message_id\\", fields: [\\"message_id\\"]}, @@ -2563,7 +2551,7 @@ type Incoming_email @db(name: \\"incoming_emails\\") @indexes(value: [ user_id: Int } -type Incoming_link @db(name: \\"incoming_links\\") @indexes(value: [ +type IncomingLink @db(name: \\"incoming_links\\") @indexes(value: [ {name: \\"index_incoming_links_on_created_at_and_user_id\\", fields: [\\"created_at\\", \\"user_id\\"]}, {name: \\"index_incoming_links_on_post_id\\", fields: [\\"post_id\\"]} ]) { @@ -2577,7 +2565,7 @@ type Incoming_link @db(name: \\"incoming_links\\") @indexes(value: [ user_id: Int } -type Incoming_referer @db(name: \\"incoming_referers\\") @indexes(value: [ +type IncomingReferer @db(name: \\"incoming_referers\\") @indexes(value: [ {name: \\"index_incoming_referers_on_path_and_incoming_domain_id\\", fields: [\\"path\\", \\"incoming_domain_id\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"incoming_referers_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2585,7 +2573,7 @@ type Incoming_referer @db(name: \\"incoming_referers\\") @indexes(value: [ path: String! } -type Instagram_user_info @db(name: \\"instagram_user_infos\\") { +type InstagramUserInfo @db(name: \\"instagram_user_infos\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"instagram_user_infos_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! instagram_user_id: Int @@ -2612,7 +2600,7 @@ type Invite @db(name: \\"invites\\") @indexes(value: [ user_id: Int } -type Invited_group @db(name: \\"invited_groups\\") { +type InvitedGroup @db(name: \\"invited_groups\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"invited_groups_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! group_id: Int @@ -2620,7 +2608,7 @@ type Invited_group @db(name: \\"invited_groups\\") { updated_at: DateTime! } -type Javascript_cach @db(name: \\"javascript_caches\\") @indexes(value: [ +type JavascriptCach @db(name: \\"javascript_caches\\") @indexes(value: [ {name: \\"index_javascript_caches_on_digest\\", fields: [\\"digest\\"]}, {name: \\"index_javascript_caches_on_theme_field_id\\", fields: [\\"theme_field_id\\"]} ]) { @@ -2632,7 +2620,7 @@ type Javascript_cach @db(name: \\"javascript_caches\\") @indexes(value: [ updated_at: DateTime! } -type Message_bus @db(name: \\"message_bus\\") @indexes(value: [ +type MessageBus @db(name: \\"message_bus\\") @indexes(value: [ {name: \\"index_message_bus_on_created_at\\", fields: [\\"created_at\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"message_bus_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2642,7 +2630,7 @@ type Message_bus @db(name: \\"message_bus\\") @indexes(value: [ name: String } -type Muted_user @db(name: \\"muted_users\\") @indexes(value: [ +type MutedUser @db(name: \\"muted_users\\") @indexes(value: [ {name: \\"index_muted_users_on_muted_user_id_and_user_id\\", fields: [\\"user_id\\", \\"muted_user_id\\"], unique: true}, {name: \\"index_muted_users_on_user_id_and_muted_user_id\\", fields: [\\"user_id\\", \\"muted_user_id\\"], unique: true} ]) { @@ -2672,7 +2660,7 @@ type Notification @db(name: \\"notifications\\") @indexes(value: [ user_id: Int! } -type Oauth2_user_info @db(name: \\"oauth2_user_infos\\") @indexes(value: [ +type Oauth2UserInfo @db(name: \\"oauth2_user_infos\\") @indexes(value: [ {name: \\"index_oauth2_user_infos_on_uid_and_provider\\", fields: [\\"uid\\", \\"provider\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"oauth2_user_infos_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2685,7 +2673,7 @@ type Oauth2_user_info @db(name: \\"oauth2_user_infos\\") @indexes(value: [ user_id: Int! } -type Onceoff_log @db(name: \\"onceoff_logs\\") @indexes(value: [ +type OnceoffLog @db(name: \\"onceoff_logs\\") @indexes(value: [ {name: \\"index_onceoff_logs_on_job_name\\", fields: [\\"job_name\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"onceoff_logs_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2694,7 +2682,7 @@ type Onceoff_log @db(name: \\"onceoff_logs\\") @indexes(value: [ updated_at: DateTime! } -type Optimized_image @db(name: \\"optimized_images\\") @indexes(value: [ +type OptimizedImage @db(name: \\"optimized_images\\") @indexes(value: [ {name: \\"index_optimized_images_on_upload_id\\", fields: [\\"upload_id\\"]}, {name: \\"index_optimized_images_on_upload_id_and_width_and_height\\", fields: [\\"width\\", \\"height\\", \\"upload_id\\"], unique: true} ]) { @@ -2719,7 +2707,7 @@ type Permalink @db(name: \\"permalinks\\") { url: String! @unique } -type Plugin_store_row @db(name: \\"plugin_store_rows\\") @indexes(value: [ +type PluginStoreRow @db(name: \\"plugin_store_rows\\") @indexes(value: [ {name: \\"index_plugin_store_rows_on_plugin_name_and_key\\", fields: [\\"plugin_name\\", \\"key\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"plugin_store_rows_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2797,7 +2785,7 @@ type Post @db(name: \\"posts\\") @indexes(value: [ word_count: Int } -type Post_action @db(name: \\"post_actions\\") @indexes(value: [ +type PostAction @db(name: \\"post_actions\\") @indexes(value: [ {name: \\"idx_unique_actions\\", fields: [\\"post_id\\", \\"user_id\\", \\"post_action_type_id\\", \\"targets_topic\\"], unique: true}, {name: \\"idx_unique_flags\\", fields: [\\"post_id\\", \\"user_id\\", \\"targets_topic\\"], unique: true}, {name: \\"index_post_actions_on_post_id\\", fields: [\\"post_id\\"]}, @@ -2822,7 +2810,7 @@ type Post_action @db(name: \\"post_actions\\") @indexes(value: [ user_id: Int! } -type Post_action_type @db(name: \\"post_action_types\\") { +type PostActionType @db(name: \\"post_action_types\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"post_action_types_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! icon: String @@ -2832,7 +2820,7 @@ type Post_action_type @db(name: \\"post_action_types\\") { updated_at: DateTime! } -type Post_custom_field @db(name: \\"post_custom_fields\\") @indexes(value: [ +type PostCustomField @db(name: \\"post_custom_fields\\") @indexes(value: [ {name: \\"idx_post_custom_fields_akismet\\", fields: [\\"post_id\\"]}, {name: \\"index_post_custom_fields_on_name_and_value\\", fields: [\\"name\\"]}, {name: \\"index_post_custom_fields_on_post_id_and_name\\", fields: [\\"post_id\\", \\"name\\"]} @@ -2845,7 +2833,7 @@ type Post_custom_field @db(name: \\"post_custom_fields\\") @indexes(value: [ value: String } -type Post_detail @db(name: \\"post_details\\") @indexes(value: [ +type PostDetail @db(name: \\"post_details\\") @indexes(value: [ {name: \\"index_post_details_on_post_id_and_key\\", fields: [\\"post_id\\", \\"key\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"post_details_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2857,7 +2845,7 @@ type Post_detail @db(name: \\"post_details\\") @indexes(value: [ value: String } -type Post_reply @db(name: \\"post_replies\\") @indexes(value: [ +type PostReply @db(name: \\"post_replies\\") @indexes(value: [ {name: \\"index_post_replies_on_post_id_and_reply_id\\", fields: [\\"post_id\\", \\"reply_id\\"], unique: true} ]) { created_at: DateTime! @@ -2866,7 +2854,7 @@ type Post_reply @db(name: \\"post_replies\\") @indexes(value: [ updated_at: DateTime! } -type Post_reply_key @db(name: \\"post_reply_keys\\") @indexes(value: [ +type PostReplyKey @db(name: \\"post_reply_keys\\") @indexes(value: [ {name: \\"index_post_reply_keys_on_user_id_and_post_id\\", fields: [\\"user_id\\", \\"post_id\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"post_reply_keys_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2877,7 +2865,7 @@ type Post_reply_key @db(name: \\"post_reply_keys\\") @indexes(value: [ user_id: Int! } -type Post_revision @db(name: \\"post_revisions\\") @indexes(value: [ +type PostRevision @db(name: \\"post_revisions\\") @indexes(value: [ {name: \\"index_post_revisions_on_post_id\\", fields: [\\"post_id\\"]}, {name: \\"index_post_revisions_on_post_id_and_number\\", fields: [\\"post_id\\", \\"number\\"]} ]) { @@ -2891,9 +2879,7 @@ type Post_revision @db(name: \\"post_revisions\\") @indexes(value: [ user_id: Int } -type Post_search_datum @db(name: \\"post_search_data\\") @indexes(value: [ - {name: \\"idx_search_post\\", fields: [\\"search_data\\"]} -]) { +type PostSearchDatum @db(name: \\"post_search_data\\") { post_id: Int! @id(strategy: NONE) locale: String raw_data: String @@ -2902,7 +2888,7 @@ type Post_search_datum @db(name: \\"post_search_data\\") @indexes(value: [ version: Int @default(value: 0) } -type Post_stat @db(name: \\"post_stats\\") @indexes(value: [ +type PostStat @db(name: \\"post_stats\\") @indexes(value: [ {name: \\"index_post_stats_on_post_id\\", fields: [\\"post_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"post_stats_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2914,7 +2900,7 @@ type Post_stat @db(name: \\"post_stats\\") @indexes(value: [ updated_at: DateTime } -type Post_timing @db(name: \\"post_timings\\") @indexes(value: [ +type PostTiming @db(name: \\"post_timings\\") @indexes(value: [ {name: \\"index_post_timings_on_user_id\\", fields: [\\"user_id\\"]}, {name: \\"post_timings_summary\\", fields: [\\"topic_id\\", \\"post_number\\"]}, {name: \\"post_timings_unique\\", fields: [\\"topic_id\\", \\"post_number\\", \\"user_id\\"], unique: true} @@ -2925,7 +2911,7 @@ type Post_timing @db(name: \\"post_timings\\") @indexes(value: [ user_id: Int! } -type Post_upload @db(name: \\"post_uploads\\") @indexes(value: [ +type PostUpload @db(name: \\"post_uploads\\") @indexes(value: [ {name: \\"idx_unique_post_uploads\\", fields: [\\"post_id\\", \\"upload_id\\"], unique: true}, {name: \\"index_post_uploads_on_post_id\\", fields: [\\"post_id\\"]}, {name: \\"index_post_uploads_on_upload_id\\", fields: [\\"upload_id\\"]} @@ -2935,7 +2921,7 @@ type Post_upload @db(name: \\"post_uploads\\") @indexes(value: [ upload_id: Int! } -type Push_subscription @db(name: \\"push_subscriptions\\") { +type PushSubscription @db(name: \\"push_subscriptions\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"push_subscriptions_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! data: String! @@ -2943,7 +2929,7 @@ type Push_subscription @db(name: \\"push_subscriptions\\") { user_id: Int! } -type Queued_post @db(name: \\"queued_posts\\") @indexes(value: [ +type QueuedPost @db(name: \\"queued_posts\\") @indexes(value: [ {name: \\"by_queue_status\\", fields: [\\"queue\\", \\"state\\", \\"created_at\\"]}, {name: \\"by_queue_status_topic\\", fields: [\\"queue\\", \\"state\\", \\"topic_id\\", \\"created_at\\"]} ]) { @@ -2962,7 +2948,7 @@ type Queued_post @db(name: \\"queued_posts\\") @indexes(value: [ user_id: Int! } -type Quoted_post @db(name: \\"quoted_posts\\") @indexes(value: [ +type QuotedPost @db(name: \\"quoted_posts\\") @indexes(value: [ {name: \\"index_quoted_posts_on_post_id_and_quoted_post_id\\", fields: [\\"post_id\\", \\"quoted_post_id\\"], unique: true}, {name: \\"index_quoted_posts_on_quoted_post_id_and_post_id\\", fields: [\\"post_id\\", \\"quoted_post_id\\"], unique: true} ]) { @@ -2973,7 +2959,7 @@ type Quoted_post @db(name: \\"quoted_posts\\") @indexes(value: [ updated_at: DateTime! } -type Remote_theme @db(name: \\"remote_themes\\") { +type RemoteTheme @db(name: \\"remote_themes\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"remote_themes_id_seq\\", initialValue: 1, allocationSize: 1) about_url: String branch: String @@ -2989,7 +2975,7 @@ type Remote_theme @db(name: \\"remote_themes\\") { updated_at: DateTime } -type Scheduler_stat @db(name: \\"scheduler_stats\\") { +type SchedulerStat @db(name: \\"scheduler_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"scheduler_stats_id_seq\\", initialValue: 1, allocationSize: 1) duration_ms: Int error: String @@ -3002,11 +2988,11 @@ type Scheduler_stat @db(name: \\"scheduler_stats\\") { success: Boolean } -type Schema_migration @db(name: \\"schema_migrations\\") { +type SchemaMigration @db(name: \\"schema_migrations\\") { version: ID! @id(strategy: NONE) } -type Schema_migration_detail @db(name: \\"schema_migration_details\\") @indexes(value: [ +type SchemaMigrationDetail @db(name: \\"schema_migration_details\\") @indexes(value: [ {name: \\"index_schema_migration_details_on_version\\", fields: [\\"version\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"schema_migration_details_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3020,7 +3006,7 @@ type Schema_migration_detail @db(name: \\"schema_migration_details\\") @indexes( version: String! } -type Screened_email @db(name: \\"screened_emails\\") @indexes(value: [ +type ScreenedEmail @db(name: \\"screened_emails\\") @indexes(value: [ {name: \\"index_screened_emails_on_last_match_at\\", fields: [\\"last_match_at\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"screened_emails_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3034,7 +3020,7 @@ type Screened_email @db(name: \\"screened_emails\\") @indexes(value: [ updated_at: DateTime! } -type Screened_ip_address @db(name: \\"screened_ip_addresses\\") @indexes(value: [ +type ScreenedIpAddress @db(name: \\"screened_ip_addresses\\") @indexes(value: [ {name: \\"index_screened_ip_addresses_on_last_match_at\\", fields: [\\"last_match_at\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"screened_ip_addresses_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3047,7 +3033,7 @@ type Screened_ip_address @db(name: \\"screened_ip_addresses\\") @indexes(value: updated_at: DateTime! } -type Screened_url @db(name: \\"screened_urls\\") @indexes(value: [ +type ScreenedUrl @db(name: \\"screened_urls\\") @indexes(value: [ {name: \\"index_screened_urls_on_last_match_at\\", fields: [\\"last_match_at\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"screened_urls_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3062,7 +3048,7 @@ type Screened_url @db(name: \\"screened_urls\\") @indexes(value: [ url: String! @unique } -type Search_log @db(name: \\"search_logs\\") { +type SearchLog @db(name: \\"search_logs\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"search_logs_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! # Type inet is not supported @@ -3074,7 +3060,7 @@ type Search_log @db(name: \\"search_logs\\") { user_id: Int } -type Shared_draft @db(name: \\"shared_drafts\\") @indexes(value: [ +type SharedDraft @db(name: \\"shared_drafts\\") @indexes(value: [ {name: \\"index_shared_drafts_on_category_id\\", fields: [\\"category_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"shared_drafts_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3084,7 +3070,7 @@ type Shared_draft @db(name: \\"shared_drafts\\") @indexes(value: [ updated_at: DateTime! } -type Single_sign_on_record @db(name: \\"single_sign_on_records\\") { +type SingleSignOnRecord @db(name: \\"single_sign_on_records\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"single_sign_on_records_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! external_avatar_url: String @@ -3099,7 +3085,7 @@ type Single_sign_on_record @db(name: \\"single_sign_on_records\\") { user_id: Int! } -type Site_setting @db(name: \\"site_settings\\") { +type SiteSetting @db(name: \\"site_settings\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"site_settings_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! data_type: Int! @@ -3108,7 +3094,7 @@ type Site_setting @db(name: \\"site_settings\\") { value: String } -type Skipped_email_log @db(name: \\"skipped_email_logs\\") @indexes(value: [ +type SkippedEmailLog @db(name: \\"skipped_email_logs\\") @indexes(value: [ {name: \\"index_skipped_email_logs_on_created_at\\", fields: [\\"created_at\\"]}, {name: \\"index_skipped_email_logs_on_post_id\\", fields: [\\"post_id\\"]}, {name: \\"index_skipped_email_logs_on_reason_type\\", fields: [\\"reason_type\\"]}, @@ -3125,7 +3111,7 @@ type Skipped_email_log @db(name: \\"skipped_email_logs\\") @indexes(value: [ user_id: Int } -type Stylesheet_cache @db(name: \\"stylesheet_cache\\") @indexes(value: [ +type StylesheetCache @db(name: \\"stylesheet_cache\\") @indexes(value: [ {name: \\"index_stylesheet_cache_on_target_and_digest\\", fields: [\\"target\\", \\"digest\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"stylesheet_cache_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3147,7 +3133,7 @@ type Tag @db(name: \\"tags\\") { updated_at: DateTime } -type Tag_group @db(name: \\"tag_groups\\") { +type TagGroup @db(name: \\"tag_groups\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"tag_groups_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime name: String! @@ -3156,7 +3142,7 @@ type Tag_group @db(name: \\"tag_groups\\") { updated_at: DateTime } -type Tag_group_membership @db(name: \\"tag_group_memberships\\") @indexes(value: [ +type TagGroupMembership @db(name: \\"tag_group_memberships\\") @indexes(value: [ {name: \\"index_tag_group_memberships_on_tag_group_id_and_tag_id\\", fields: [\\"tag_id\\", \\"tag_group_id\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"tag_group_memberships_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3166,7 +3152,7 @@ type Tag_group_membership @db(name: \\"tag_group_memberships\\") @indexes(value: updated_at: DateTime } -type Tag_group_permission @db(name: \\"tag_group_permissions\\") @indexes(value: [ +type TagGroupPermission @db(name: \\"tag_group_permissions\\") @indexes(value: [ {name: \\"index_tag_group_permissions_on_group_id\\", fields: [\\"group_id\\"]}, {name: \\"index_tag_group_permissions_on_tag_group_id\\", fields: [\\"tag_group_id\\"]} ]) { @@ -3178,9 +3164,7 @@ type Tag_group_permission @db(name: \\"tag_group_permissions\\") @indexes(value: updated_at: DateTime! } -type Tag_search_datum @db(name: \\"tag_search_data\\") @indexes(value: [ - {name: \\"idx_search_tag\\", fields: [\\"search_data\\"]} -]) { +type TagSearchDatum @db(name: \\"tag_search_data\\") { tag_id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"tag_search_data_tag_id_seq\\", initialValue: 1, allocationSize: 1) locale: String raw_data: String @@ -3189,7 +3173,7 @@ type Tag_search_datum @db(name: \\"tag_search_data\\") @indexes(value: [ version: Int @default(value: 0) } -type Tag_user @db(name: \\"tag_users\\") @indexes(value: [ +type TagUser @db(name: \\"tag_users\\") @indexes(value: [ {name: \\"idx_tag_users_ix1\\", fields: [\\"tag_id\\", \\"user_id\\", \\"notification_level\\"], unique: true}, {name: \\"idx_tag_users_ix2\\", fields: [\\"tag_id\\", \\"user_id\\", \\"notification_level\\"], unique: true} ]) { @@ -3215,7 +3199,7 @@ type Theme @db(name: \\"themes\\") { user_selectable: Boolean! @default(value: false) } -type Theme_field @db(name: \\"theme_fields\\") @indexes(value: [ +type ThemeField @db(name: \\"theme_fields\\") @indexes(value: [ {name: \\"theme_field_unique_index\\", fields: [\\"theme_id\\", \\"target_id\\", \\"name\\", \\"type_id\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"theme_fields_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3232,7 +3216,7 @@ type Theme_field @db(name: \\"theme_fields\\") @indexes(value: [ value_baked: String } -type Theme_setting @db(name: \\"theme_settings\\") { +type ThemeSetting @db(name: \\"theme_settings\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"theme_settings_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! data_type: Int! @@ -3242,7 +3226,7 @@ type Theme_setting @db(name: \\"theme_settings\\") { value: String } -type Top_topic @db(name: \\"top_topics\\") @indexes(value: [ +type TopTopic @db(name: \\"top_topics\\") @indexes(value: [ {name: \\"index_top_topics_on_all_score\\", fields: [\\"all_score\\"]}, {name: \\"index_top_topics_on_daily_likes_count\\", fields: [\\"daily_likes_count\\"]}, {name: \\"index_top_topics_on_daily_op_likes_count\\", fields: [\\"daily_op_likes_count\\"]}, @@ -3355,7 +3339,7 @@ type Topic @db(name: \\"topics\\") @indexes(value: [ word_count: Int } -type Topic_allowed_group @db(name: \\"topic_allowed_groups\\") @indexes(value: [ +type TopicAllowedGroup @db(name: \\"topic_allowed_groups\\") @indexes(value: [ {name: \\"index_topic_allowed_groups_on_group_id_and_topic_id\\", fields: [\\"group_id\\", \\"topic_id\\"], unique: true}, {name: \\"index_topic_allowed_groups_on_topic_id_and_group_id\\", fields: [\\"group_id\\", \\"topic_id\\"], unique: true} ]) { @@ -3364,7 +3348,7 @@ type Topic_allowed_group @db(name: \\"topic_allowed_groups\\") @indexes(value: [ topic_id: Int! } -type Topic_allowed_user @db(name: \\"topic_allowed_users\\") @indexes(value: [ +type TopicAllowedUser @db(name: \\"topic_allowed_users\\") @indexes(value: [ {name: \\"index_topic_allowed_users_on_topic_id_and_user_id\\", fields: [\\"user_id\\", \\"topic_id\\"], unique: true}, {name: \\"index_topic_allowed_users_on_user_id_and_topic_id\\", fields: [\\"user_id\\", \\"topic_id\\"], unique: true} ]) { @@ -3375,7 +3359,7 @@ type Topic_allowed_user @db(name: \\"topic_allowed_users\\") @indexes(value: [ user_id: Int! } -type Topic_custom_field @db(name: \\"topic_custom_fields\\") @indexes(value: [ +type TopicCustomField @db(name: \\"topic_custom_fields\\") @indexes(value: [ {name: \\"index_topic_custom_fields_on_topic_id_and_name\\", fields: [\\"topic_id\\", \\"name\\"]}, {name: \\"topic_custom_fields_value_key_idx\\", fields: [\\"name\\", \\"value\\"]} ]) { @@ -3387,7 +3371,7 @@ type Topic_custom_field @db(name: \\"topic_custom_fields\\") @indexes(value: [ value: String } -type Topic_embed @db(name: \\"topic_embeds\\") { +type TopicEmbed @db(name: \\"topic_embeds\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"topic_embeds_id_seq\\", initialValue: 1, allocationSize: 1) content_sha1: String created_at: DateTime! @@ -3399,7 +3383,7 @@ type Topic_embed @db(name: \\"topic_embeds\\") { updated_at: DateTime! } -type Topic_invite @db(name: \\"topic_invites\\") @indexes(value: [ +type TopicInvite @db(name: \\"topic_invites\\") @indexes(value: [ {name: \\"index_topic_invites_on_invite_id\\", fields: [\\"invite_id\\"]}, {name: \\"index_topic_invites_on_topic_id_and_invite_id\\", fields: [\\"topic_id\\", \\"invite_id\\"], unique: true} ]) { @@ -3410,7 +3394,7 @@ type Topic_invite @db(name: \\"topic_invites\\") @indexes(value: [ updated_at: DateTime! } -type Topic_link @db(name: \\"topic_links\\") @indexes(value: [ +type TopicLink @db(name: \\"topic_links\\") @indexes(value: [ {name: \\"index_topic_links_on_extension\\", fields: [\\"extension\\"]}, {name: \\"index_topic_links_on_link_post_id_and_reflection\\", fields: [\\"reflection\\", \\"link_post_id\\"]}, {name: \\"index_topic_links_on_post_id\\", fields: [\\"post_id\\"]}, @@ -3436,7 +3420,7 @@ type Topic_link @db(name: \\"topic_links\\") @indexes(value: [ user_id: Int! } -type Topic_link_click @db(name: \\"topic_link_clicks\\") @indexes(value: [ +type TopicLinkClick @db(name: \\"topic_link_clicks\\") @indexes(value: [ {name: \\"by_link\\", fields: [\\"topic_link_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"topic_link_clicks_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3448,9 +3432,7 @@ type Topic_link_click @db(name: \\"topic_link_clicks\\") @indexes(value: [ user_id: Int } -type Topic_search_datum @db(name: \\"topic_search_data\\") @indexes(value: [ - {name: \\"idx_search_topic\\", fields: [\\"search_data\\"]} -]) { +type TopicSearchDatum @db(name: \\"topic_search_data\\") { topic_id: Int! @id(strategy: NONE) locale: String! raw_data: String @@ -3459,7 +3441,7 @@ type Topic_search_datum @db(name: \\"topic_search_data\\") @indexes(value: [ version: Int @default(value: 0) } -type Topic_tag @db(name: \\"topic_tags\\") @indexes(value: [ +type TopicTag @db(name: \\"topic_tags\\") @indexes(value: [ {name: \\"index_topic_tags_on_topic_id_and_tag_id\\", fields: [\\"topic_id\\", \\"tag_id\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"topic_tags_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3469,7 +3451,7 @@ type Topic_tag @db(name: \\"topic_tags\\") @indexes(value: [ updated_at: DateTime } -type Topic_timer @db(name: \\"topic_timers\\") @indexes(value: [ +type TopicTimer @db(name: \\"topic_timers\\") @indexes(value: [ {name: \\"index_topic_timers_on_user_id\\", fields: [\\"user_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"topic_timers_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3486,7 +3468,7 @@ type Topic_timer @db(name: \\"topic_timers\\") @indexes(value: [ user_id: Int! } -type Topic_user @db(name: \\"topic_users\\") @indexes(value: [ +type TopicUser @db(name: \\"topic_users\\") @indexes(value: [ {name: \\"index_topic_users_on_topic_id_and_user_id\\", fields: [\\"user_id\\", \\"topic_id\\"], unique: true}, {name: \\"index_topic_users_on_user_id_and_topic_id\\", fields: [\\"user_id\\", \\"topic_id\\"], unique: true} ]) { @@ -3508,7 +3490,7 @@ type Topic_user @db(name: \\"topic_users\\") @indexes(value: [ user_id: Int! } -type Topic_view @db(name: \\"topic_views\\") @indexes(value: [ +type TopicView @db(name: \\"topic_views\\") @indexes(value: [ {name: \\"index_topic_views_on_topic_id_and_viewed_at\\", fields: [\\"topic_id\\", \\"viewed_at\\"]}, {name: \\"index_topic_views_on_user_id_and_viewed_at\\", fields: [\\"viewed_at\\", \\"user_id\\"]}, {name: \\"index_topic_views_on_viewed_at_and_topic_id\\", fields: [\\"topic_id\\", \\"viewed_at\\"]}, @@ -3521,7 +3503,7 @@ type Topic_view @db(name: \\"topic_views\\") @indexes(value: [ viewed_at: DateTime! } -type Translation_override @db(name: \\"translation_overrides\\") @indexes(value: [ +type TranslationOverride @db(name: \\"translation_overrides\\") @indexes(value: [ {name: \\"index_translation_overrides_on_locale_and_translation_key\\", fields: [\\"locale\\", \\"translation_key\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"translation_overrides_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3533,7 +3515,7 @@ type Translation_override @db(name: \\"translation_overrides\\") @indexes(value: value: String! } -type Twitter_user_info @db(name: \\"twitter_user_infos\\") { +type TwitterUserInfo @db(name: \\"twitter_user_infos\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"twitter_user_infos_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime! email: String @@ -3543,7 +3525,7 @@ type Twitter_user_info @db(name: \\"twitter_user_infos\\") { user_id: Int! @unique } -type Unsubscribe_key @db(name: \\"unsubscribe_keys\\") @indexes(value: [ +type UnsubscribeKey @db(name: \\"unsubscribe_keys\\") @indexes(value: [ {name: \\"index_unsubscribe_keys_on_created_at\\", fields: [\\"created_at\\"]} ]) { key: ID! @id(strategy: NONE) @@ -3624,7 +3606,7 @@ type User @db(name: \\"users\\") @indexes(value: [ views: Int! @default(value: 0) } -type User_action @db(name: \\"user_actions\\") @indexes(value: [ +type UserAction @db(name: \\"user_actions\\") @indexes(value: [ {name: \\"idx_unique_rows\\", fields: [\\"action_type\\", \\"user_id\\", \\"target_topic_id\\", \\"target_post_id\\", \\"acting_user_id\\"], unique: true}, {name: \\"idx_user_actions_speed_up_user_all\\", fields: [\\"action_type\\", \\"user_id\\", \\"created_at\\"]}, {name: \\"index_user_actions_on_acting_user_id\\", fields: [\\"acting_user_id\\"]}, @@ -3643,7 +3625,7 @@ type User_action @db(name: \\"user_actions\\") @indexes(value: [ user_id: Int! } -type User_api_key @db(name: \\"user_api_keys\\") @indexes(value: [ +type UserApiKey @db(name: \\"user_api_keys\\") @indexes(value: [ {name: \\"index_user_api_keys_on_user_id\\", fields: [\\"user_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"user_api_keys_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3659,7 +3641,7 @@ type User_api_key @db(name: \\"user_api_keys\\") @indexes(value: [ user_id: Int! } -type User_archived_message @db(name: \\"user_archived_messages\\") @indexes(value: [ +type UserArchivedMessage @db(name: \\"user_archived_messages\\") @indexes(value: [ {name: \\"index_user_archived_messages_on_user_id_and_topic_id\\", fields: [\\"user_id\\", \\"topic_id\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"user_archived_messages_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3669,7 +3651,7 @@ type User_archived_message @db(name: \\"user_archived_messages\\") @indexes(valu user_id: Int! } -type User_auth_token @db(name: \\"user_auth_tokens\\") { +type UserAuthToken @db(name: \\"user_auth_tokens\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"user_auth_tokens_id_seq\\", initialValue: 1, allocationSize: 1) auth_token: String! @unique auth_token_seen: Boolean! @default(value: false) @@ -3684,7 +3666,7 @@ type User_auth_token @db(name: \\"user_auth_tokens\\") { user_id: Int! } -type User_auth_token_log @db(name: \\"user_auth_token_logs\\") { +type UserAuthTokenLog @db(name: \\"user_auth_token_logs\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"user_auth_token_logs_id_seq\\", initialValue: 1, allocationSize: 1) action: String! auth_token: String @@ -3697,7 +3679,7 @@ type User_auth_token_log @db(name: \\"user_auth_token_logs\\") { user_id: Int } -type User_avatar @db(name: \\"user_avatars\\") @indexes(value: [ +type UserAvatar @db(name: \\"user_avatars\\") @indexes(value: [ {name: \\"index_user_avatars_on_custom_upload_id\\", fields: [\\"custom_upload_id\\"]}, {name: \\"index_user_avatars_on_gravatar_upload_id\\", fields: [\\"gravatar_upload_id\\"]}, {name: \\"index_user_avatars_on_user_id\\", fields: [\\"user_id\\"]} @@ -3711,7 +3693,7 @@ type User_avatar @db(name: \\"user_avatars\\") @indexes(value: [ user_id: Int! } -type User_badge @db(name: \\"user_badges\\") @indexes(value: [ +type UserBadge @db(name: \\"user_badges\\") @indexes(value: [ {name: \\"index_user_badges_on_badge_id_and_user_id\\", fields: [\\"badge_id\\", \\"user_id\\"]}, {name: \\"index_user_badges_on_badge_id_and_user_id_and_post_id\\", fields: [\\"badge_id\\", \\"user_id\\", \\"post_id\\"], unique: true}, {name: \\"index_user_badges_on_badge_id_and_user_id_and_seq\\", fields: [\\"badge_id\\", \\"user_id\\", \\"seq\\"], unique: true}, @@ -3727,7 +3709,7 @@ type User_badge @db(name: \\"user_badges\\") @indexes(value: [ user_id: Int! } -type User_custom_field @db(name: \\"user_custom_fields\\") @indexes(value: [ +type UserCustomField @db(name: \\"user_custom_fields\\") @indexes(value: [ {name: \\"index_user_custom_fields_on_user_id_and_name\\", fields: [\\"user_id\\", \\"name\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"user_custom_fields_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3738,7 +3720,7 @@ type User_custom_field @db(name: \\"user_custom_fields\\") @indexes(value: [ value: String } -type User_email @db(name: \\"user_emails\\") @indexes(value: [ +type UserEmail @db(name: \\"user_emails\\") @indexes(value: [ {name: \\"index_user_emails_on_user_id\\", fields: [\\"user_id\\"]}, {name: \\"index_user_emails_on_user_id_and_primary\\", fields: [\\"user_id\\", \\"primary\\"], unique: true} ]) { @@ -3750,7 +3732,7 @@ type User_email @db(name: \\"user_emails\\") @indexes(value: [ user_id: Int! } -type User_export @db(name: \\"user_exports\\") { +type UserExport @db(name: \\"user_exports\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"user_exports_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime file_name: String! @@ -3759,7 +3741,7 @@ type User_export @db(name: \\"user_exports\\") { user_id: Int! } -type User_field @db(name: \\"user_fields\\") { +type UserField @db(name: \\"user_fields\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"user_fields_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime description: String! @@ -3775,7 +3757,7 @@ type User_field @db(name: \\"user_fields\\") { updated_at: DateTime } -type User_field_option @db(name: \\"user_field_options\\") { +type UserFieldOption @db(name: \\"user_field_options\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"user_field_options_id_seq\\", initialValue: 1, allocationSize: 1) created_at: DateTime updated_at: DateTime @@ -3783,7 +3765,7 @@ type User_field_option @db(name: \\"user_field_options\\") { value: String! } -type User_history @db(name: \\"user_histories\\") @indexes(value: [ +type UserHistory @db(name: \\"user_histories\\") @indexes(value: [ {name: \\"index_user_histories_on_acting_user_id_and_action_and_id\\", fields: [\\"id\\", \\"action\\", \\"acting_user_id\\"]}, {name: \\"index_user_histories_on_action_and_id\\", fields: [\\"id\\", \\"action\\"]}, {name: \\"index_user_histories_on_category_id\\", fields: [\\"category_id\\"]}, @@ -3810,7 +3792,7 @@ type User_history @db(name: \\"user_histories\\") @indexes(value: [ updated_at: DateTime! } -type User_open_id @db(name: \\"user_open_ids\\") @indexes(value: [ +type UserOpenId @db(name: \\"user_open_ids\\") @indexes(value: [ {name: \\"index_user_open_ids_on_url\\", fields: [\\"url\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"user_open_ids_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3822,7 +3804,7 @@ type User_open_id @db(name: \\"user_open_ids\\") @indexes(value: [ user_id: Int! } -type User_option @db(name: \\"user_options\\") { +type UserOption @db(name: \\"user_options\\") { user_id: Int! @id(strategy: NONE) allow_private_messages: Boolean! @default(value: true) auto_track_topics_after_msecs: Int @@ -3851,7 +3833,7 @@ type User_option @db(name: \\"user_options\\") { theme_key_seq: Int! @default(value: 0) } -type User_profile @db(name: \\"user_profiles\\") @indexes(value: [ +type UserProfile @db(name: \\"user_profiles\\") @indexes(value: [ {name: \\"index_user_profiles_on_bio_cooked_version\\", fields: [\\"bio_cooked_version\\"]}, {name: \\"index_user_profiles_on_card_background\\", fields: [\\"card_background\\"]}, {name: \\"index_user_profiles_on_profile_background\\", fields: [\\"profile_background\\"]} @@ -3869,7 +3851,7 @@ type User_profile @db(name: \\"user_profiles\\") @indexes(value: [ website: String } -type User_profile_view @db(name: \\"user_profile_views\\") @indexes(value: [ +type UserProfileView @db(name: \\"user_profile_views\\") @indexes(value: [ {name: \\"index_user_profile_views_on_user_id\\", fields: [\\"user_id\\"]}, {name: \\"index_user_profile_views_on_user_profile_id\\", fields: [\\"user_profile_id\\"]}, {name: \\"unique_profile_view_user_or_ip\\", fields: [\\"user_profile_id\\", \\"viewed_at\\", \\"ip_address\\", \\"user_id\\"], unique: true} @@ -3882,9 +3864,7 @@ type User_profile_view @db(name: \\"user_profile_views\\") @indexes(value: [ viewed_at: DateTime! } -type User_search_datum @db(name: \\"user_search_data\\") @indexes(value: [ - {name: \\"idx_search_user\\", fields: [\\"search_data\\"]} -]) { +type UserSearchDatum @db(name: \\"user_search_data\\") { user_id: Int! @id(strategy: NONE) locale: String raw_data: String @@ -3893,7 +3873,7 @@ type User_search_datum @db(name: \\"user_search_data\\") @indexes(value: [ version: Int @default(value: 0) } -type User_second_factor @db(name: \\"user_second_factors\\") @indexes(value: [ +type UserSecondFactor @db(name: \\"user_second_factors\\") @indexes(value: [ {name: \\"index_user_second_factors_on_user_id\\", fields: [\\"user_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"user_second_factors_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3906,7 +3886,7 @@ type User_second_factor @db(name: \\"user_second_factors\\") @indexes(value: [ user_id: Int! } -type User_stat @db(name: \\"user_stats\\") { +type UserStat @db(name: \\"user_stats\\") { user_id: Int! @id(strategy: NONE) bounce_score: Float! @default(value: 0) days_visited: Int! @default(value: 0) @@ -3924,7 +3904,7 @@ type User_stat @db(name: \\"user_stats\\") { topics_entered: Int! @default(value: 0) } -type User_upload @db(name: \\"user_uploads\\") @indexes(value: [ +type UserUpload @db(name: \\"user_uploads\\") @indexes(value: [ {name: \\"index_user_uploads_on_upload_id_and_user_id\\", fields: [\\"upload_id\\", \\"user_id\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"user_uploads_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3933,7 +3913,7 @@ type User_upload @db(name: \\"user_uploads\\") @indexes(value: [ user_id: Int! } -type User_visit @db(name: \\"user_visits\\") @indexes(value: [ +type UserVisit @db(name: \\"user_visits\\") @indexes(value: [ {name: \\"index_user_visits_on_user_id_and_visited_at\\", fields: [\\"user_id\\", \\"visited_at\\"], unique: true}, {name: \\"index_user_visits_on_user_id_and_visited_at_and_time_read\\", fields: [\\"user_id\\", \\"visited_at\\", \\"time_read\\"]}, {name: \\"index_user_visits_on_visited_at_and_mobile\\", fields: [\\"visited_at\\", \\"mobile\\"]} @@ -3946,7 +3926,7 @@ type User_visit @db(name: \\"user_visits\\") @indexes(value: [ visited_at: DateTime! } -type User_warning @db(name: \\"user_warnings\\") @indexes(value: [ +type UserWarning @db(name: \\"user_warnings\\") @indexes(value: [ {name: \\"index_user_warnings_on_user_id\\", fields: [\\"user_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"user_warnings_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3957,7 +3937,7 @@ type User_warning @db(name: \\"user_warnings\\") @indexes(value: [ user_id: Int! } -type Watched_word @db(name: \\"watched_words\\") @indexes(value: [ +type WatchedWord @db(name: \\"watched_words\\") @indexes(value: [ {name: \\"index_watched_words_on_action_and_word\\", fields: [\\"word\\", \\"action\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"watched_words_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3967,7 +3947,7 @@ type Watched_word @db(name: \\"watched_words\\") @indexes(value: [ word: String! } -type Web_crawler_request @db(name: \\"web_crawler_requests\\") @indexes(value: [ +type WebCrawlerRequest @db(name: \\"web_crawler_requests\\") @indexes(value: [ {name: \\"index_web_crawler_requests_on_date_and_user_agent\\", fields: [\\"date\\", \\"user_agent\\"], unique: true} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"web_crawler_requests_id_seq\\", initialValue: 1, allocationSize: 1) @@ -3976,7 +3956,7 @@ type Web_crawler_request @db(name: \\"web_crawler_requests\\") @indexes(value: [ user_agent: String! } -type Web_hook @db(name: \\"web_hooks\\") { +type WebHook @db(name: \\"web_hooks\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"web_hooks_id_seq\\", initialValue: 1, allocationSize: 1) active: Boolean! @default(value: false) content_type: Int! @default(value: 1) @@ -3990,7 +3970,7 @@ type Web_hook @db(name: \\"web_hooks\\") { wildcard_web_hook: Boolean! @default(value: false) } -type Web_hook_event @db(name: \\"web_hook_events\\") @indexes(value: [ +type WebHookEvent @db(name: \\"web_hook_events\\") @indexes(value: [ {name: \\"index_web_hook_events_on_web_hook_id\\", fields: [\\"web_hook_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"web_hook_events_id_seq\\", initialValue: 1, allocationSize: 1) @@ -4005,12 +3985,12 @@ type Web_hook_event @db(name: \\"web_hook_events\\") @indexes(value: [ web_hook_id: Int! } -type Web_hook_event_type @db(name: \\"web_hook_event_types\\") { +type WebHookEventType @db(name: \\"web_hook_event_types\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"web_hook_event_types_id_seq\\", initialValue: 1, allocationSize: 1) name: String! } -type Web_hook_event_types_hook @db(name: \\"web_hook_event_types_hooks\\") @indexes(value: [ +type WebHookEventTypesHook @db(name: \\"web_hook_event_types_hooks\\") @indexes(value: [ {name: \\"idx_web_hook_event_types_hooks_on_ids\\", fields: [\\"web_hook_id\\", \\"web_hook_event_type_id\\"], unique: true} ]) { web_hook_event_type_id: Int! diff --git a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/issue-4081.ts.snap b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/issue-4081.ts.snap new file mode 100644 index 0000000000..41c37d216c --- /dev/null +++ b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/issue-4081.ts.snap @@ -0,0 +1,1627 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Introspector issue4081 relations 1`] = ` +"type AboutAboutpage @pgTable(name: \\"about_aboutpage\\") { + id: Int! @unique + heading: String! + heading_image: String! + text: String! +} + +type AboutTeammember @pgTable(name: \\"about_teammember\\") { + id: Int! @unique + description: String! + facebook_url: String! + instagram_url: String! + name: String! + order: Int! + page_id: Int! + picture: String! + position: String! + twitter_url: String! + visible: Boolean! + youtube_url: String! +} + +type AccountEmailaddress @pgTable(name: \\"account_emailaddress\\") { + id: Int! @unique + email: String! + primary: Boolean! + user_id: Int! + verified: Boolean! +} + +type AccountEmailconfirmation @pgTable(name: \\"account_emailconfirmation\\") { + id: Int! @unique + created: DateTime! + email_address_id: Int! + key: String! + sent: DateTime +} + +type AuthGroup @pgTable(name: \\"auth_group\\") { + id: Int! @unique + name: String! +} + +type AuthGroupPermission @pgTable(name: \\"auth_group_permissions\\") { + id: Int! @unique + group_id: Int! + permission_id: Int! +} + +type AuthPermission @pgTable(name: \\"auth_permission\\") { + id: Int! @unique + codename: String! + content_type_id: Int! + name: String! +} + +type ContactContactformcontent @pgTable(name: \\"contact_contactformcontent\\") { + id: Int! @unique + title: String! +} + +type ContactContactpage @pgTable(name: \\"contact_contactpage\\") { + id: Int! @unique + heading: String! + heading_image: String! +} + +type ContactThankyoupage @pgTable(name: \\"contact_thankyoupage\\") { + id: Int! @unique + button_link: String! + button_text: String! + heading: String! + text: String! + title: String! +} + +type DjangoAdminLog @pgTable(name: \\"django_admin_log\\") { + id: Int! @unique + action_flag: Int! + action_time: DateTime! + change_message: String! + content_type_id: Int + object_id: String + object_repr: String! + user_id: Int! +} + +type DjangoContentType @pgTable(name: \\"django_content_type\\") { + id: Int! @unique + app_label: String! + model: String! +} + +type DjangoMigration @pgTable(name: \\"django_migrations\\") { + id: Int! @unique + app: String! + applied: DateTime! + name: String! +} + +type DjangoSession @pgTable(name: \\"django_session\\") { + expire_date: DateTime! + session_data: String! + session_key: String! +} + +type DjangoSite @pgTable(name: \\"django_site\\") { + id: Int! @unique + domain: String! + name: String! +} + +type EventsEvent @pgTable(name: \\"events_event\\") { + id: Int! @unique + address_line_one: String! + address_line_two: String! + city: String! + country: String! + date: DateTime! + hosted_by: String! + location_name: String! + map_url: String! + name: String! + picture: String! + # Type time is not supported + # time: time! + topic: String! + visible: Boolean! +} + +type EventsEventspage @pgTable(name: \\"events_eventspage\\") { + id: Int! @unique + heading_image: String! +} + +type HelpHelppage @pgTable(name: \\"help_helppage\\") { + id: Int! @unique + heading: String! + heading_image: String! +} + +type HelpHelpquestion @pgTable(name: \\"help_helpquestion\\") { + id: Int! @unique + answer: String! + order: Int + section_id: Int! + thumbnail: String! + title: String! +} + +type HelpHelpsection @pgTable(name: \\"help_helpsection\\") { + id: Int! @unique + order: Int! + page_id: Int! + title: String! +} + +type JobsJob @pgTable(name: \\"jobs_job\\") { + id: Int! @unique + apply_page_form_title: String! + apply_page_title: String! + order: Int + page_id: Int! + picture: String! + position_description: String! + position_location: String! + position_name: String! + visible: Boolean! +} + +type JobsJobspage @pgTable(name: \\"jobs_jobspage\\") { + id: Int! @unique + heading: String! + heading_image: String! + text: String! +} + +type JobsNojobspage @pgTable(name: \\"jobs_nojobspage\\") { + id: Int! @unique + heading: String! + text: String! + title: String! + video_thumbnail: String! +} + +type JobsThankyoupage @pgTable(name: \\"jobs_thankyoupage\\") { + id: Int! @unique + button_link: String! + button_text: String! + heading: String! + text: String! + title: String! +} + +type LandingLandingpage @pgTable(name: \\"landing_landingpage\\") { + id: Int! @unique + section_four_image: String! + section_four_text: String! + section_four_title: String! + section_one_image: String! + section_one_title: String! + section_three_image: String! + section_three_text: String! + section_three_title: String! + section_two_image: String! + section_two_text: String! + section_two_title: String! +} + +type LanguagesLanguage @pgTable(name: \\"languages_language\\") { + id: Int! @unique + language: String! + language_code: String! +} + +type MediaArtist @pgTable(name: \\"media_artist\\") { + id: Int! @unique + artist: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type MediaCategory @pgTable(name: \\"media_category\\") { + id: Int! @unique + category: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type MediaGenre @pgTable(name: \\"media_genre\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + genre: String! + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type MediaMedia @pgTable(name: \\"media_media\\") { + id: Int! @unique + approved: Boolean! + artist_id: Int! + created_by_id: Int + created_date: DateTime + genre_id: Int + media_language_id: Int! + modified_date: DateTime + note: String! + region_id: Int + title: String! + translation_language_id: Int! + updated_by_id: Int +} + +type MediaMediaCategory @pgTable(name: \\"media_media_categories\\") { + id: Int! @unique + category_id: Int! + media_id: Int! +} + +type MediaMediasentence @pgTable(name: \\"media_mediasentence\\") { + id: Int! @unique + approved: Boolean! + created_by_id: Int + created_date: DateTime + learning_sentence_id: Int! + line: Int! + lyrics_sentence: String! + lyrics_translation: String! + lyrics_translation_chunks: String! + media_id: Int! + modified_date: DateTime + multiple: Boolean! + note: String! + order: Int! + place_in_line: Int! + repeat: Boolean! + repeat_of_line: Int! + repeated_in_line: String! + updated_by_id: Int + word_count: Int! +} + +type MediaRegion @pgTable(name: \\"media_region\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + region: String! + updated_by_id: Int +} + +type SentencesCustomhint @pgTable(name: \\"sentences_customhint\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + hint: String! + hint_type_id: Int! + modified_date: DateTime + note: String! + sentence_id: Int + updated_by_id: Int +} + +type SentencesEcast @pgTable(name: \\"sentences_ecast\\") { + id: Int! @unique + action_id: Int! + character_id: Int! + created_by_id: Int + created_date: DateTime + description: String! + emotion_id: Int! + modified_date: DateTime + note: String! + sentence_id: Int! + setting_id: Int! + theme_id: Int + updated_by_id: Int +} + +type SentencesEcastaction @pgTable(name: \\"sentences_ecastaction\\") { + id: Int! @unique + action: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SentencesEcastcharacter @pgTable(name: \\"sentences_ecastcharacter\\") { + id: Int! @unique + character: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SentencesEcastemotion @pgTable(name: \\"sentences_ecastemotion\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + emotion: String! + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SentencesEcastsetting @pgTable(name: \\"sentences_ecastsetting\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + setting: String! + updated_by_id: Int +} + +type SentencesEcasttheme @pgTable(name: \\"sentences_ecasttheme\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + theme: String! + updated_by_id: Int +} + +type SentencesHint @pgTable(name: \\"sentences_hint\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + hint: String! + hint_type_id: Int! + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SentencesHinttype @pgTable(name: \\"sentences_hinttype\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + type_name: String! + updated_by_id: Int +} + +type SentencesImagecategory @pgTable(name: \\"sentences_imagecategory\\") { + id: Int! @unique + category: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SentencesImagetype @pgTable(name: \\"sentences_imagetype\\") { + id: Int! @unique + name: String! +} + +type SentencesPosttranslationword @pgTable(name: \\"sentences_posttranslationword\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + language_id: Int + modified_date: DateTime + note: String! + post: String! + updated_by_id: Int +} + +type SentencesPretranslationword @pgTable(name: \\"sentences_pretranslationword\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + language_id: Int + modified_date: DateTime + note: String! + pre: String! + updated_by_id: Int +} + +type SentencesSentence @pgTable(name: \\"sentences_sentence\\") { + id: Int! @unique + approved: Boolean! + chunk_id: Int + created_by_id: Int + created_date: DateTime + english_sentence_punctuation_end: String! + english_sentence_punctuation_start: String! + fifth_chunk_id: Int + fifth_order: Boolean! + fifth_post_id: Int + fifth_pre_id: Int + fifth_underlined: Boolean! + first_chunk_id: Int + first_order: Boolean! + first_post_id: Int + first_pre_id: Int + first_underlined: Boolean! + fourth_chunk_id: Int + fourth_order: Boolean! + fourth_post_id: Int + fourth_pre_id: Int + fourth_underlined: Boolean! + learning_language_id: Int + learning_sentence_punctuation_end: String! + learning_sentence_punctuation_start: String! + modified_date: DateTime + note: String + second_chunk_id: Int + second_order: Boolean! + second_post_id: Int + second_pre_id: Int + second_underlined: Boolean! + sentence_audio_id: Int + sentence_category_id: Int + seventh_chunk_id: Int + seventh_order: Boolean! + seventh_post_id: Int + seventh_pre_id: Int + seventh_underlined: Boolean! + sixth_chunk_id: Int + sixth_order: Boolean! + sixth_post_id: Int + sixth_pre_id: Int + sixth_underlined: Boolean! + third_chunk_id: Int + third_order: Boolean! + third_post_id: Int + third_pre_id: Int + third_underlined: Boolean! + to_check: String + translation_language_id: Int + translation_sentence_punctuation_end: String! + translation_sentence_punctuation_start: String! + updated_by_id: Int +} + +type SentencesSentenceHint @pgTable(name: \\"sentences_sentence_hints\\") { + id: Int! @unique + hint_id: Int! + sentence_id: Int! +} + +type SentencesSentenceaudio @pgTable(name: \\"sentences_sentenceaudio\\") { + id: Int! @unique + audio_file: String + audio_language_id: Int! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + sentence_in_audio: String! + updated_by_id: Int +} + +type SentencesSentencecategory @pgTable(name: \\"sentences_sentencecategory\\") { + id: Int! @unique + category_type: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SentencesSentencegluer @pgTable(name: \\"sentences_sentencegluer\\") { + id: Int! @unique + learning_sentence_id: Int! + translation_sentence_id: Int! +} + +type SentencesSentenceimage @pgTable(name: \\"sentences_sentenceimage\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + description: String! + file: String + image_category_id: Int! + image_text_language_id: Int + modified_date: DateTime + note: String! + sentence_id: Int + updated_by_id: Int +} + +type SentencesSentencevideo @pgTable(name: \\"sentences_sentencevideo\\") { + id: Int! @unique + audio_language_id: Int! + created_by_id: Int + created_date: DateTime + description: String! + file: String + modified_date: DateTime + note: String! + sentence_id: Int + updated_by_id: Int + video_category_id: Int! + video_thumbnail: String +} + +type SentencesVideocategory @pgTable(name: \\"sentences_videocategory\\") { + id: Int! @unique + category: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SocialaccountSocialaccount @pgTable(name: \\"socialaccount_socialaccount\\") { + id: Int! @unique + date_joined: DateTime! + extra_data: String! + last_login: DateTime! + provider: String! + uid: String! + user_id: Int! +} + +type SocialaccountSocialapp @pgTable(name: \\"socialaccount_socialapp\\") { + id: Int! @unique + client_id: String! + key: String! + name: String! + provider: String! + secret: String! +} + +type SocialaccountSocialappSite @pgTable(name: \\"socialaccount_socialapp_sites\\") { + id: Int! @unique + site_id: Int! + socialapp_id: Int! +} + +type SocialaccountSocialtoken @pgTable(name: \\"socialaccount_socialtoken\\") { + id: Int! @unique + account_id: Int! + app_id: Int! + expires_at: DateTime + token: String! + token_secret: String! +} + +type UsersEmailnotification @pgTable(name: \\"users_emailnotifications\\") { + id: Int! @unique + events: Boolean! + new_features: Boolean! + news: Boolean! + progress: Boolean! + promotions: Boolean! + user_id: Int! +} + +type UsersProfile @pgTable(name: \\"users_profile\\") { + id: Int! @unique + censored_content: Boolean! + country: String! + date_of_birth: DateTime + gender: String! + learning_language_id: Int + profile_picture: String! + speaking_language_id: Int +} + +type UsersSpotssetting @pgTable(name: \\"users_spotssettings\\") { + id: Int! @unique + autoplay_sentence_audio: Boolean! + autoplay_word_audio: Boolean! + next_word_seconds: Int! + play_videos: Boolean! + reveal_seconds: Int! + show_keyboard_shortcuts: Boolean! + show_main_images: Boolean! + show_submitted_images: Boolean! + spots_page_auto: Boolean! + user_id: Int! +} + +type UsersSubscription @pgTable(name: \\"users_subscription\\") { + id: Int! @unique + expiration_date: DateTime! + expired: Boolean! + subscription_type_id: Int! +} + +type UsersSubscriptiontype @pgTable(name: \\"users_subscriptiontype\\") { + id: Int! @unique + name: String! + price: Float! +} + +type UsersUser @pgTable(name: \\"users_user\\") { + id: Int! @unique + date_joined: DateTime! + email: String! + first_name: String! + first_time: Boolean! + free_account: Boolean! + is_active: Boolean! + is_staff: Boolean! + is_superuser: Boolean! + last_login: DateTime + last_name: String! + password: String! + profile_id: Int + username: String! +} + +type UsersUserGroup @pgTable(name: \\"users_user_groups\\") { + id: Int! @unique + group_id: Int! + user_id: Int! +} + +type UsersUserUserPermission @pgTable(name: \\"users_user_user_permissions\\") { + id: Int! @unique + permission_id: Int! + user_id: Int! +} + +type UsersVocabularysetting @pgTable(name: \\"users_vocabularysettings\\") { + id: Int! @unique + sentence_audio_play_click: Boolean! + show_keyboard_shortcuts: Boolean! + user_id: Int! + word_audio_play_click: Boolean! +} + +type WordsChunk @pgTable(name: \\"words_chunk\\") { + id: Int! @unique + chunk_type_id: Int + created_by_id: Int + created_date: DateTime + learning_word_id: Int! + modified_date: DateTime + note: String! + translation_word_id: Int! + updated_by_id: Int + vocab: Boolean! +} + +type WordsChunktype @pgTable(name: \\"words_chunktype\\") { + id: Int! @unique + chunk_type: String + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type WordsEnglishword @pgTable(name: \\"words_englishword\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + definition: String! + english_word_note: String! + example_sentence: String! + grammar_id: Int! + modified_date: DateTime + updated_by_id: Int + word_in_english: String! +} + +type WordsGender @pgTable(name: \\"words_gender\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + gender: String! + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type WordsGrammar @pgTable(name: \\"words_grammar\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + examples: String! + grammar_group: String! + grammar_note: String! + modified_date: DateTime + name: String! + syntax: String! + tense_type_id: Int + updated_by_id: Int + user_grammar: String! +} + +type WordsKnownword @pgTable(name: \\"words_knownword\\") { + id: Int! @unique + created_at: DateTime! + user_id: Int! + word_id: Int! +} + +type WordsMasteredword @pgTable(name: \\"words_masteredword\\") { + id: Int! @unique + created_at: DateTime! + user_id: Int! + word_id: Int! +} + +type WordsPerson @pgTable(name: \\"words_person\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + person: String! + updated_by_id: Int +} + +type WordsTensetype @pgTable(name: \\"words_tensetype\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + tense_type: String! + updated_by_id: Int +} + +type WordsWord @pgTable(name: \\"words_word\\") { + id: Int! @unique + created_by_id: Int + created_date: DateTime + gender_id: Int + modified_date: DateTime + note: String! + number: String + person_id: Int + updated_by_id: Int + word: String! + word_audio_id: Int + word_grammar_id: Int + word_in_english_id: Int! + word_language_id: Int! +} + +type WordsWordaudio @pgTable(name: \\"words_wordaudio\\") { + id: Int! @unique + audio_file: String + audio_language_id: Int! + created_by_id: Int + created_date: DateTime + example_sentence: String! + ipa: String! + modified_date: DateTime + note: String! + updated_by_id: Int + word_in_audio: String! +}" +`; + +exports[`Introspector issue4081 relations 2`] = ` +"type AboutAboutpage @db(name: \\"about_aboutpage\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"about_aboutpage_id_seq\\", initialValue: 1, allocationSize: 1) + heading: String! + heading_image: String! + text: String! +} + +type AboutTeammember @db(name: \\"about_teammember\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"about_teammember_id_seq\\", initialValue: 1, allocationSize: 1) + description: String! + facebook_url: String! + instagram_url: String! + name: String! + order: Int! + page_id: Int! + picture: String! + position: String! + twitter_url: String! + visible: Boolean! + youtube_url: String! +} + +type AccountEmailaddress @db(name: \\"account_emailaddress\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"account_emailaddress_id_seq\\", initialValue: 1, allocationSize: 1) + email: String! + primary: Boolean! + user_id: Int! + verified: Boolean! +} + +type AccountEmailconfirmation @db(name: \\"account_emailconfirmation\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"account_emailconfirmation_id_seq\\", initialValue: 1, allocationSize: 1) + created: DateTime! + email_address_id: Int! + key: String! + sent: DateTime +} + +type AuthGroup @db(name: \\"auth_group\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"auth_group_id_seq\\", initialValue: 1, allocationSize: 1) + name: String! +} + +type AuthGroupPermission @db(name: \\"auth_group_permissions\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"auth_group_permissions_id_seq\\", initialValue: 1, allocationSize: 1) + group_id: Int! + permission_id: Int! +} + +type AuthPermission @db(name: \\"auth_permission\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"auth_permission_id_seq\\", initialValue: 1, allocationSize: 1) + codename: String! + content_type_id: Int! + name: String! +} + +type ContactContactformcontent @db(name: \\"contact_contactformcontent\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"contact_contactform_id_seq\\", initialValue: 1, allocationSize: 1) + title: String! +} + +type ContactContactpage @db(name: \\"contact_contactpage\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"contact_contactpage_id_seq\\", initialValue: 1, allocationSize: 1) + heading: String! + heading_image: String! +} + +type ContactThankyoupage @db(name: \\"contact_thankyoupage\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"contact_thankyoupage_id_seq\\", initialValue: 1, allocationSize: 1) + button_link: String! + button_text: String! + heading: String! + text: String! + title: String! +} + +type DjangoAdminLog @db(name: \\"django_admin_log\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"django_admin_log_id_seq\\", initialValue: 1, allocationSize: 1) + action_flag: Int! + action_time: DateTime! + change_message: String! + content_type_id: Int + object_id: String + object_repr: String! + user_id: Int! +} + +type DjangoContentType @db(name: \\"django_content_type\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"django_content_type_id_seq\\", initialValue: 1, allocationSize: 1) + app_label: String! + model: String! +} + +type DjangoMigration @db(name: \\"django_migrations\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"django_migrations_id_seq\\", initialValue: 1, allocationSize: 1) + app: String! + applied: DateTime! + name: String! +} + +type DjangoSession @db(name: \\"django_session\\") { + expire_date: DateTime! + session_data: String! + session_key: String! +} + +type DjangoSite @db(name: \\"django_site\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"django_site_id_seq\\", initialValue: 1, allocationSize: 1) + domain: String! + name: String! +} + +type EventsEvent @db(name: \\"events_event\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"events_event_id_seq\\", initialValue: 1, allocationSize: 1) + address_line_one: String! + address_line_two: String! + city: String! + country: String! + date: DateTime! + hosted_by: String! + location_name: String! + map_url: String! + name: String! + picture: String! + # Type time is not supported + # time: time! + topic: String! + visible: Boolean! +} + +type EventsEventspage @db(name: \\"events_eventspage\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"events_eventpage_id_seq\\", initialValue: 1, allocationSize: 1) + heading_image: String! +} + +type HelpHelppage @db(name: \\"help_helppage\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"help_helppage_id_seq\\", initialValue: 1, allocationSize: 1) + heading: String! + heading_image: String! +} + +type HelpHelpquestion @db(name: \\"help_helpquestion\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"help_helpquestion_id_seq\\", initialValue: 1, allocationSize: 1) + answer: String! + order: Int + section_id: Int! + thumbnail: String! + title: String! +} + +type HelpHelpsection @db(name: \\"help_helpsection\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"help_helpsection_id_seq\\", initialValue: 1, allocationSize: 1) + order: Int! + page_id: Int! + title: String! +} + +type JobsJob @db(name: \\"jobs_job\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"jobs_job_id_seq\\", initialValue: 1, allocationSize: 1) + apply_page_form_title: String! + apply_page_title: String! + order: Int + page_id: Int! + picture: String! + position_description: String! + position_location: String! + position_name: String! + visible: Boolean! +} + +type JobsJobspage @db(name: \\"jobs_jobspage\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"jobs_jobspage_id_seq\\", initialValue: 1, allocationSize: 1) + heading: String! + heading_image: String! + text: String! +} + +type JobsNojobspage @db(name: \\"jobs_nojobspage\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"jobs_nojobspage_id_seq\\", initialValue: 1, allocationSize: 1) + heading: String! + text: String! + title: String! + video_thumbnail: String! +} + +type JobsThankyoupage @db(name: \\"jobs_thankyoupage\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"jobs_thankyoupage_id_seq\\", initialValue: 1, allocationSize: 1) + button_link: String! + button_text: String! + heading: String! + text: String! + title: String! +} + +type LandingLandingpage @db(name: \\"landing_landingpage\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"landing_landingpage_id_seq\\", initialValue: 1, allocationSize: 1) + section_four_image: String! + section_four_text: String! + section_four_title: String! + section_one_image: String! + section_one_title: String! + section_three_image: String! + section_three_text: String! + section_three_title: String! + section_two_image: String! + section_two_text: String! + section_two_title: String! +} + +type LanguagesLanguage @db(name: \\"languages_language\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"languages_language_id_seq\\", initialValue: 1, allocationSize: 1) + language: String! + language_code: String! +} + +type MediaArtist @db(name: \\"media_artist\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"media_artist_id_seq\\", initialValue: 1, allocationSize: 1) + artist: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type MediaCategory @db(name: \\"media_category\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"media_category_id_seq\\", initialValue: 1, allocationSize: 1) + category: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type MediaGenre @db(name: \\"media_genre\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"media_genre_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + genre: String! + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type MediaMedia @db(name: \\"media_media\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"media_media_id_seq\\", initialValue: 1, allocationSize: 1) + approved: Boolean! + artist_id: Int! + created_by_id: Int + created_date: DateTime + genre_id: Int + media_language_id: Int! + modified_date: DateTime + note: String! + region_id: Int + title: String! + translation_language_id: Int! + updated_by_id: Int +} + +type MediaMediaCategory @db(name: \\"media_media_categories\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"media_media_categories_id_seq\\", initialValue: 1, allocationSize: 1) + category_id: Int! + media_id: Int! +} + +type MediaMediasentence @db(name: \\"media_mediasentence\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"media_mediasentence_id_seq\\", initialValue: 1, allocationSize: 1) + approved: Boolean! + created_by_id: Int + created_date: DateTime + learning_sentence_id: Int! + line: Int! + lyrics_sentence: String! + lyrics_translation: String! + lyrics_translation_chunks: String! + media_id: Int! + modified_date: DateTime + multiple: Boolean! + note: String! + order: Int! + place_in_line: Int! + repeat: Boolean! + repeat_of_line: Int! + repeated_in_line: String! + updated_by_id: Int + word_count: Int! +} + +type MediaRegion @db(name: \\"media_region\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"media_region_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + region: String! + updated_by_id: Int +} + +type SentencesCustomhint @db(name: \\"sentences_customhint\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_customhint_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + hint: String! + hint_type_id: Int! + modified_date: DateTime + note: String! + sentence_id: Int + updated_by_id: Int +} + +type SentencesEcast @db(name: \\"sentences_ecast\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_ecast_id_seq\\", initialValue: 1, allocationSize: 1) + action_id: Int! + character_id: Int! + created_by_id: Int + created_date: DateTime + description: String! + emotion_id: Int! + modified_date: DateTime + note: String! + sentence_id: Int! + setting_id: Int! + theme_id: Int + updated_by_id: Int +} + +type SentencesEcastaction @db(name: \\"sentences_ecastaction\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_ecastaction_id_seq\\", initialValue: 1, allocationSize: 1) + action: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SentencesEcastcharacter @db(name: \\"sentences_ecastcharacter\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_ecastcharacter_id_seq\\", initialValue: 1, allocationSize: 1) + character: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SentencesEcastemotion @db(name: \\"sentences_ecastemotion\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_ecastemotion_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + emotion: String! + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SentencesEcastsetting @db(name: \\"sentences_ecastsetting\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_ecastsetting_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + setting: String! + updated_by_id: Int +} + +type SentencesEcasttheme @db(name: \\"sentences_ecasttheme\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_ecasttheme_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + theme: String! + updated_by_id: Int +} + +type SentencesHint @db(name: \\"sentences_hint\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_hint_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + hint: String! + hint_type_id: Int! + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SentencesHinttype @db(name: \\"sentences_hinttype\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_hinttype_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + type_name: String! + updated_by_id: Int +} + +type SentencesImagecategory @db(name: \\"sentences_imagecategory\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_imagecategory_id_seq\\", initialValue: 1, allocationSize: 1) + category: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SentencesImagetype @db(name: \\"sentences_imagetype\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_imagetype_id_seq\\", initialValue: 1, allocationSize: 1) + name: String! +} + +type SentencesPosttranslationword @db(name: \\"sentences_posttranslationword\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_posttranslationword_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + language_id: Int + modified_date: DateTime + note: String! + post: String! + updated_by_id: Int +} + +type SentencesPretranslationword @db(name: \\"sentences_pretranslationword\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_pretranslationword_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + language_id: Int + modified_date: DateTime + note: String! + pre: String! + updated_by_id: Int +} + +type SentencesSentence @db(name: \\"sentences_sentence\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_sentence_id_seq\\", initialValue: 1, allocationSize: 1) + approved: Boolean! + chunk_id: Int + created_by_id: Int + created_date: DateTime + english_sentence_punctuation_end: String! + english_sentence_punctuation_start: String! + fifth_chunk_id: Int + fifth_order: Boolean! + fifth_post_id: Int + fifth_pre_id: Int + fifth_underlined: Boolean! + first_chunk_id: Int + first_order: Boolean! + first_post_id: Int + first_pre_id: Int + first_underlined: Boolean! + fourth_chunk_id: Int + fourth_order: Boolean! + fourth_post_id: Int + fourth_pre_id: Int + fourth_underlined: Boolean! + learning_language_id: Int + learning_sentence_punctuation_end: String! + learning_sentence_punctuation_start: String! + modified_date: DateTime + note: String + second_chunk_id: Int + second_order: Boolean! + second_post_id: Int + second_pre_id: Int + second_underlined: Boolean! + sentence_audio_id: Int + sentence_category_id: Int + seventh_chunk_id: Int + seventh_order: Boolean! + seventh_post_id: Int + seventh_pre_id: Int + seventh_underlined: Boolean! + sixth_chunk_id: Int + sixth_order: Boolean! + sixth_post_id: Int + sixth_pre_id: Int + sixth_underlined: Boolean! + third_chunk_id: Int + third_order: Boolean! + third_post_id: Int + third_pre_id: Int + third_underlined: Boolean! + to_check: String + translation_language_id: Int + translation_sentence_punctuation_end: String! + translation_sentence_punctuation_start: String! + updated_by_id: Int +} + +type SentencesSentenceHint @db(name: \\"sentences_sentence_hints\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_sentence_hints_id_seq\\", initialValue: 1, allocationSize: 1) + hint_id: Int! + sentence_id: Int! +} + +type SentencesSentenceaudio @db(name: \\"sentences_sentenceaudio\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_sentenceaudio_id_seq\\", initialValue: 1, allocationSize: 1) + audio_file: String + audio_language_id: Int! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + sentence_in_audio: String! + updated_by_id: Int +} + +type SentencesSentencecategory @db(name: \\"sentences_sentencecategory\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_sentencecategory_id_seq\\", initialValue: 1, allocationSize: 1) + category_type: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SentencesSentencegluer @db(name: \\"sentences_sentencegluer\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_sentencegluer_id_seq\\", initialValue: 1, allocationSize: 1) + learning_sentence_id: Int! + translation_sentence_id: Int! +} + +type SentencesSentenceimage @db(name: \\"sentences_sentenceimage\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_sentenceimage_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + description: String! + file: String + image_category_id: Int! + image_text_language_id: Int + modified_date: DateTime + note: String! + sentence_id: Int + updated_by_id: Int +} + +type SentencesSentencevideo @db(name: \\"sentences_sentencevideo\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_sentencevideo_id_seq\\", initialValue: 1, allocationSize: 1) + audio_language_id: Int! + created_by_id: Int + created_date: DateTime + description: String! + file: String + modified_date: DateTime + note: String! + sentence_id: Int + updated_by_id: Int + video_category_id: Int! + video_thumbnail: String +} + +type SentencesVideocategory @db(name: \\"sentences_videocategory\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sentences_videocategory_id_seq\\", initialValue: 1, allocationSize: 1) + category: String! + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type SocialaccountSocialaccount @db(name: \\"socialaccount_socialaccount\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"socialaccount_socialaccount_id_seq\\", initialValue: 1, allocationSize: 1) + date_joined: DateTime! + extra_data: String! + last_login: DateTime! + provider: String! + uid: String! + user_id: Int! +} + +type SocialaccountSocialapp @db(name: \\"socialaccount_socialapp\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"socialaccount_socialapp_id_seq\\", initialValue: 1, allocationSize: 1) + client_id: String! + key: String! + name: String! + provider: String! + secret: String! +} + +type SocialaccountSocialappSite @db(name: \\"socialaccount_socialapp_sites\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"socialaccount_socialapp_sites_id_seq\\", initialValue: 1, allocationSize: 1) + site_id: Int! + socialapp_id: Int! +} + +type SocialaccountSocialtoken @db(name: \\"socialaccount_socialtoken\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"socialaccount_socialtoken_id_seq\\", initialValue: 1, allocationSize: 1) + account_id: Int! + app_id: Int! + expires_at: DateTime + token: String! + token_secret: String! +} + +type UsersEmailnotification @db(name: \\"users_emailnotifications\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"users_emailnotifications_id_seq\\", initialValue: 1, allocationSize: 1) + events: Boolean! + new_features: Boolean! + news: Boolean! + progress: Boolean! + promotions: Boolean! + user_id: Int! +} + +type UsersProfile @db(name: \\"users_profile\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"users_profile_id_seq\\", initialValue: 1, allocationSize: 1) + censored_content: Boolean! + country: String! + date_of_birth: DateTime + gender: String! + learning_language_id: Int + profile_picture: String! + speaking_language_id: Int +} + +type UsersSpotssetting @db(name: \\"users_spotssettings\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"users_spotssettings_id_seq\\", initialValue: 1, allocationSize: 1) + autoplay_sentence_audio: Boolean! + autoplay_word_audio: Boolean! + next_word_seconds: Int! + play_videos: Boolean! + reveal_seconds: Int! + show_keyboard_shortcuts: Boolean! + show_main_images: Boolean! + show_submitted_images: Boolean! + spots_page_auto: Boolean! + user_id: Int! +} + +type UsersSubscription @db(name: \\"users_subscription\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"users_subscription_id_seq\\", initialValue: 1, allocationSize: 1) + expiration_date: DateTime! + expired: Boolean! + subscription_type_id: Int! +} + +type UsersSubscriptiontype @db(name: \\"users_subscriptiontype\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"users_subscriptiontype_id_seq\\", initialValue: 1, allocationSize: 1) + name: String! + price: Float! +} + +type UsersUser @db(name: \\"users_user\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"users_user_id_seq\\", initialValue: 1, allocationSize: 1) + date_joined: DateTime! + email: String! + first_name: String! + first_time: Boolean! + free_account: Boolean! + is_active: Boolean! + is_staff: Boolean! + is_superuser: Boolean! + last_login: DateTime + last_name: String! + password: String! + profile_id: Int + username: String! +} + +type UsersUserGroup @db(name: \\"users_user_groups\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"users_user_groups_id_seq\\", initialValue: 1, allocationSize: 1) + group_id: Int! + user_id: Int! +} + +type UsersUserUserPermission @db(name: \\"users_user_user_permissions\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"users_user_user_permissions_id_seq\\", initialValue: 1, allocationSize: 1) + permission_id: Int! + user_id: Int! +} + +type UsersVocabularysetting @db(name: \\"users_vocabularysettings\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"users_vocabularysettings_id_seq\\", initialValue: 1, allocationSize: 1) + sentence_audio_play_click: Boolean! + show_keyboard_shortcuts: Boolean! + user_id: Int! + word_audio_play_click: Boolean! +} + +type WordsChunk @db(name: \\"words_chunk\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"words_chunk_id_seq\\", initialValue: 1, allocationSize: 1) + chunk_type_id: Int + created_by_id: Int + created_date: DateTime + learning_word_id: Int! + modified_date: DateTime + note: String! + translation_word_id: Int! + updated_by_id: Int + vocab: Boolean! +} + +type WordsChunktype @db(name: \\"words_chunktype\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"words_chunktype_id_seq\\", initialValue: 1, allocationSize: 1) + chunk_type: String + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type WordsEnglishword @db(name: \\"words_englishword\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"words_englishword_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + definition: String! + english_word_note: String! + example_sentence: String! + grammar_id: Int! + modified_date: DateTime + updated_by_id: Int + word_in_english: String! +} + +type WordsGender @db(name: \\"words_gender\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"words_gender_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + gender: String! + modified_date: DateTime + note: String! + updated_by_id: Int +} + +type WordsGrammar @db(name: \\"words_grammar\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"words_grammar_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + examples: String! + grammar_group: String! + grammar_note: String! + modified_date: DateTime + name: String! + syntax: String! + tense_type_id: Int + updated_by_id: Int + user_grammar: String! +} + +type WordsKnownword @db(name: \\"words_knownword\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"words_knownword_id_seq\\", initialValue: 1, allocationSize: 1) + created_at: DateTime! + user_id: Int! + word_id: Int! +} + +type WordsMasteredword @db(name: \\"words_masteredword\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"words_masteredword_id_seq\\", initialValue: 1, allocationSize: 1) + created_at: DateTime! + user_id: Int! + word_id: Int! +} + +type WordsPerson @db(name: \\"words_person\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"words_person_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + person: String! + updated_by_id: Int +} + +type WordsTensetype @db(name: \\"words_tensetype\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"words_tensetype_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + modified_date: DateTime + note: String! + tense_type: String! + updated_by_id: Int +} + +type WordsWord @db(name: \\"words_word\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"words_word_id_seq\\", initialValue: 1, allocationSize: 1) + created_by_id: Int + created_date: DateTime + gender_id: Int + modified_date: DateTime + note: String! + number: String + person_id: Int + updated_by_id: Int + word: String! + word_audio_id: Int + word_grammar_id: Int + word_in_english_id: Int! + word_language_id: Int! +} + +type WordsWordaudio @db(name: \\"words_wordaudio\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"words_wordaudio_id_seq\\", initialValue: 1, allocationSize: 1) + audio_file: String + audio_language_id: Int! + created_by_id: Int + created_date: DateTime + example_sentence: String! + ipa: String! + modified_date: DateTime + note: String! + updated_by_id: Int + word_in_audio: String! +}" +`; diff --git a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/issue-4095.ts.snap b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/issue-4095.ts.snap new file mode 100644 index 0000000000..37424ca45d --- /dev/null +++ b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/issue-4095.ts.snap @@ -0,0 +1,257 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Introspector issue4095 relations 1`] = ` +"type BillingAccount { + id: Int! @unique + createdAt: DateTime! + customerId: Customer! + invoice: [Invoice] + item_BillingAccount: [ItemBillingAccount] + name: String! + updatedAt: DateTime! +} + +type Condition { + id: Int! @unique + createdAt: DateTime! + endDate: DateTime! + productId: Product! + startDate: DateTime! + updatedAt: DateTime! +} + +type Contract { + id: Int! @unique + activationDateTime: DateTime + code: String! + cost: [Cost] + createdAt: DateTime! + customerId: Customer! + enabled: Boolean @default(value: false) + globalCondition: [GlobalCondition] + item: [Item] + lookToBook: Float! + minimumFee: Float! + updatedAt: DateTime! +} + +type Cost { + id: Int! @unique + amount: Float + contractId: Contract + createdAt: DateTime! + itemId: Item + max: Int + min: Int + type: String! + updatedAt: DateTime! +} + +type Customer { + id: Int! @unique + billingAccount: [BillingAccount] + code: String! + contract: [Contract] + createdAt: DateTime! + name: String! + updatedAt: DateTime! +} + +type GlobalCondition { + id: Int! @unique + amount: Float + contractId: Contract! + createdAt: DateTime! + defaultFreeTrialDaysForItem: Int + globalEndFreeTrialPeriod: DateTime + globalStartFreeTrialPeriod: DateTime + millionSearches: Int + numConnections: Int + type: String! + updatedAt: DateTime! +} + +type Invoice { + id: Int! @unique + amount: Float! + amountPaid: Float! + billingAccountId: BillingAccount! + createdAt: DateTime! + endDate: DateTime! + invoiceBreakdown: [InvoiceBreakdown] + paymentMethod: String! + paymentStatus: String! + startDate: DateTime! + updatedAt: DateTime! +} + +type InvoiceBreakdown { + id: Int! @unique + amount: Float! + createdAt: DateTime! + endDate: DateTime! + invoiceId: Invoice! + itemBillingAccountId: ItemBillingAccount! + startDate: DateTime! + updatedAt: DateTime! +} + +type Item { + id: Int! @unique + contractId: Contract! + cost: [Cost] + createdAt: DateTime! + enabled: Boolean @default(value: false) + item_BillingAccount: [ItemBillingAccount] + product: [Product] + specificFreeTrialDays: Int + updatedAt: DateTime! +} + +type ItemBillingAccount @pgTable(name: \\"Item_BillingAccount\\") { + id: Int! @unique + billingAccountId: BillingAccount! + createdAt: DateTime! + invoiceBreakdown: [InvoiceBreakdown] + itemId: Item! + updatedAt: DateTime! +} + +type Product { + id: Int! @unique + condition: [Condition] + createdAt: DateTime! + itemId: Item! + system: String! + type: String! + updatedAt: DateTime! +}" +`; + +exports[`Introspector issue4095 relations 2`] = ` +"type BillingAccount { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"BillingAccount_id_seq\\", initialValue: 1, allocationSize: 1) + createdAt: DateTime! @createdAt + customerId: Customer! + invoice: [Invoice] + item_BillingAccount: [ItemBillingAccount] + name: String! + updatedAt: DateTime! @updatedAt +} + +type Condition { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"Condition_id_seq\\", initialValue: 1, allocationSize: 1) + createdAt: DateTime! @createdAt + endDate: DateTime! + productId: Product! + startDate: DateTime! + updatedAt: DateTime! @updatedAt +} + +type Contract { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"Contract_id_seq\\", initialValue: 1, allocationSize: 1) + activationDateTime: DateTime + code: String! + cost: [Cost] + createdAt: DateTime! @createdAt + customerId: Customer! + enabled: Boolean @default(value: false) + globalCondition: [GlobalCondition] + item: [Item] + lookToBook: Float! + minimumFee: Float! + updatedAt: DateTime! @updatedAt +} + +type Cost { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"Cost_id_seq\\", initialValue: 1, allocationSize: 1) + amount: Float + contractId: Contract + createdAt: DateTime! @createdAt + itemId: Item + max: Int + min: Int + type: String! + updatedAt: DateTime! @updatedAt +} + +type Customer { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"Customer_id_seq\\", initialValue: 1, allocationSize: 1) + billingAccount: [BillingAccount] + code: String! + contract: [Contract] + createdAt: DateTime! @createdAt + name: String! + updatedAt: DateTime! @updatedAt +} + +type GlobalCondition { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"GlobalCondition_id_seq\\", initialValue: 1, allocationSize: 1) + amount: Float + contractId: Contract! + createdAt: DateTime! @createdAt + defaultFreeTrialDaysForItem: Int + globalEndFreeTrialPeriod: DateTime + globalStartFreeTrialPeriod: DateTime + millionSearches: Int + numConnections: Int + type: String! + updatedAt: DateTime! @updatedAt +} + +type Invoice { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"Invoice_id_seq\\", initialValue: 1, allocationSize: 1) + amount: Float! + amountPaid: Float! + billingAccountId: BillingAccount! + createdAt: DateTime! @createdAt + endDate: DateTime! + invoiceBreakdown: [InvoiceBreakdown] + paymentMethod: String! + paymentStatus: String! + startDate: DateTime! + updatedAt: DateTime! @updatedAt +} + +type InvoiceBreakdown { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"InvoiceBreakdown_id_seq\\", initialValue: 1, allocationSize: 1) + amount: Float! + createdAt: DateTime! @createdAt + endDate: DateTime! + invoiceId: Invoice! + itemBillingAccountId: ItemBillingAccount! + startDate: DateTime! + updatedAt: DateTime! @updatedAt +} + +type Item { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"Item_id_seq\\", initialValue: 1, allocationSize: 1) + contractId: Contract! + cost: [Cost] + createdAt: DateTime! @createdAt + enabled: Boolean @default(value: false) + item_BillingAccount: [ItemBillingAccount] + product: [Product] + specificFreeTrialDays: Int + updatedAt: DateTime! @updatedAt +} + +type ItemBillingAccount @db(name: \\"Item_BillingAccount\\") { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"Item_BillingAccount_id_seq\\", initialValue: 1, allocationSize: 1) + billingAccountId: BillingAccount! + createdAt: DateTime! @createdAt + invoiceBreakdown: [InvoiceBreakdown] + itemId: Item! + updatedAt: DateTime! @updatedAt +} + +type Product { + id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"Product_id_seq\\", initialValue: 1, allocationSize: 1) + condition: [Condition] + createdAt: DateTime! @createdAt + itemId: Item! + system: String! + type: String! + updatedAt: DateTime! @updatedAt +}" +`; diff --git a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/largeSchema.test.ts.snap b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/largeSchema.test.ts.snap index d4b28a2b78..4fb11f344a 100644 --- a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/largeSchema.test.ts.snap +++ b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/largeSchema.test.ts.snap @@ -5,7 +5,7 @@ exports[`Introspector large example database 1`] = ` {name: \\"idx_actor_last_name\\", fields: [\\"last_name\\"]} ]) @pgTable(name: \\"actor\\") { id: Int! @pgColumn(name: \\"actor_id\\") @unique - film_actor: [Film_actor] + film_actor: [FilmActor] first_name: String! last_name: String! last_update: DateTime! @@ -27,7 +27,7 @@ type Address @pgTable(name: \\"address\\") { type Category @pgTable(name: \\"category\\") { id: Int! @pgColumn(name: \\"category_id\\") @unique - film_category: [Film_category] + film_category: [FilmCategory] last_update: DateTime! name: String! } @@ -71,15 +71,15 @@ type Film @indexes(value: [ ]) @pgTable(name: \\"film\\") { id: Int! @pgColumn(name: \\"film_id\\") @unique description: String - film_actor: [Film_actor] - film_category: [Film_category] + film_actor: [FilmActor] + film_category: [FilmCategory] # Type tsvector is not supported # fulltext: tsvector! inventory: [Inventory] language_id: Language! last_update: DateTime! length: Int - rating: Mpaa_rating + rating: MpaaRating release_year: Int rental_duration: Int! @default(value: 3) rental_rate: Float! @default(value: 4.99) @@ -88,7 +88,7 @@ type Film @indexes(value: [ title: String! } -type Film_actor @pgTable(name: \\"film_actor\\") { +type FilmActor @pgTable(name: \\"film_actor\\") { # Multiple ID fields (compound indexes) are not supported # id: Actor! @pgColumn(name: \\"actor_id\\") @unique # Multiple ID fields (compound indexes) are not supported @@ -96,7 +96,7 @@ type Film_actor @pgTable(name: \\"film_actor\\") { last_update: DateTime! } -type Film_category @pgTable(name: \\"film_category\\") { +type FilmCategory @pgTable(name: \\"film_category\\") { # Multiple ID fields (compound indexes) are not supported # id: Film! @pgColumn(name: \\"film_id\\") @unique # Multiple ID fields (compound indexes) are not supported @@ -121,7 +121,7 @@ type Language @pgTable(name: \\"language\\") { name: String! } -enum Mpaa_rating { +enum MpaaRating { G NC-17 PG @@ -182,7 +182,7 @@ exports[`Introspector large example database 2`] = ` {name: \\"idx_actor_last_name\\", fields: [\\"last_name\\"]} ]) { actor_id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"actor_actor_id_seq\\", initialValue: 1, allocationSize: 1) - film_actor: [Film_actor] + film_actor: [FilmActor] first_name: String! last_name: String! last_update: DateTime! @@ -204,7 +204,7 @@ type Address @db(name: \\"address\\") { type Category @db(name: \\"category\\") { category_id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"category_category_id_seq\\", initialValue: 1, allocationSize: 1) - film_category: [Film_category] + film_category: [FilmCategory] last_update: DateTime! name: String! } @@ -248,15 +248,15 @@ type Film @db(name: \\"film\\") @indexes(value: [ ]) { film_id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"film_film_id_seq\\", initialValue: 1, allocationSize: 1) description: String - film_actor: [Film_actor] - film_category: [Film_category] + film_actor: [FilmActor] + film_category: [FilmCategory] # Type tsvector is not supported # fulltext: tsvector! inventory: [Inventory] language_id: Language! last_update: DateTime! length: Int - rating: Mpaa_rating + rating: MpaaRating release_year: Int rental_duration: Int! @default(value: 3) rental_rate: Float! @default(value: 4.99) @@ -265,7 +265,7 @@ type Film @db(name: \\"film\\") @indexes(value: [ title: String! } -type Film_actor @db(name: \\"film_actor\\") { +type FilmActor @db(name: \\"film_actor\\") { # Multiple ID fields (compound indexes) are not supported # actor_id: Actor! @id(strategy: NONE) # Multiple ID fields (compound indexes) are not supported @@ -273,7 +273,7 @@ type Film_actor @db(name: \\"film_actor\\") { last_update: DateTime! } -type Film_category @db(name: \\"film_category\\") { +type FilmCategory @db(name: \\"film_category\\") { # Multiple ID fields (compound indexes) are not supported # film_id: Film! @id(strategy: NONE) # Multiple ID fields (compound indexes) are not supported @@ -298,7 +298,7 @@ type Language @db(name: \\"language\\") { name: String! } -enum Mpaa_rating { +enum MpaaRating { G NC-17 PG diff --git a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/relations.test.ts.snap b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/relations.test.ts.snap index 15400dec3f..8a5d819c2c 100644 --- a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/relations.test.ts.snap +++ b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/relations.test.ts.snap @@ -60,17 +60,17 @@ exports[`Introspector relation with relation table 1`] = ` "type Bill @pgTable(name: \\"bill\\") { id: Int! @unique bill: String! - bill_product: [Bill_product] + bill_product: [BillProduct] } -type Bill_product @linkTable @pgTable(name: \\"bill_product\\") { +type BillProduct @linkTable @pgTable(name: \\"bill_product\\") { bill_id: Bill product_id: Product } type Product @pgTable(name: \\"product\\") { id: Int! @unique - bill_product: [Bill_product] + bill_product: [BillProduct] product: String! }" `; @@ -79,17 +79,17 @@ exports[`Introspector relation with relation table 2`] = ` "type Bill @db(name: \\"bill\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"bill_id_seq\\", initialValue: 1, allocationSize: 1) bill: String! - bill_product: [Bill_product] + bill_product: [BillProduct] } -type Bill_product @db(name: \\"bill_product\\") @linkTable { +type BillProduct @db(name: \\"bill_product\\") @linkTable { bill_id: Bill product_id: Product } type Product @db(name: \\"product\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"product_id_seq\\", initialValue: 1, allocationSize: 1) - bill_product: [Bill_product] + bill_product: [BillProduct] product: String! }" `; @@ -98,10 +98,10 @@ exports[`Introspector relation with relation table with extra column 1`] = ` "type Bill @pgTable(name: \\"bill\\") { id: Int! @unique bill: String! - bill_product: [Bill_product] + bill_product: [BillProduct] } -type Bill_product @pgTable(name: \\"bill_product\\") { +type BillProduct @pgTable(name: \\"bill_product\\") { bill_id: Bill product_id: Product some_other_column: String! @@ -109,7 +109,7 @@ type Bill_product @pgTable(name: \\"bill_product\\") { type Product @pgTable(name: \\"product\\") { id: Int! @unique - bill_product: [Bill_product] + bill_product: [BillProduct] product: String! }" `; @@ -118,10 +118,10 @@ exports[`Introspector relation with relation table with extra column 2`] = ` "type Bill @db(name: \\"bill\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"bill_id_seq\\", initialValue: 1, allocationSize: 1) bill: String! - bill_product: [Bill_product] + bill_product: [BillProduct] } -type Bill_product @db(name: \\"bill_product\\") { +type BillProduct @db(name: \\"bill_product\\") { bill_id: Bill product_id: Product some_other_column: String! @@ -129,7 +129,7 @@ type Bill_product @db(name: \\"bill_product\\") { type Product @db(name: \\"product\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"product_id_seq\\", initialValue: 1, allocationSize: 1) - bill_product: [Bill_product] + bill_product: [BillProduct] product: String! }" `; diff --git a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/scalars.test.ts.snap b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/scalars.test.ts.snap index 2a98568a18..a438772ff5 100644 --- a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/scalars.test.ts.snap +++ b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/scalars.test.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Introspector DateTime columns 1`] = ` -"type Boolean @pgTable(name: \\"Booleans\\") { +"type Booleans { a: DateTime b: DateTime! c: DateTime! @@ -9,7 +9,7 @@ exports[`Introspector DateTime columns 1`] = ` `; exports[`Introspector DateTime columns 2`] = ` -"type Boolean @db(name: \\"Booleans\\") { +"type Booleans { a: DateTime b: DateTime! c: DateTime! @@ -17,14 +17,14 @@ exports[`Introspector DateTime columns 2`] = ` `; exports[`Introspector Json columns 1`] = ` -"type Boolean @pgTable(name: \\"Booleans\\") { +"type Booleans { a: Json b: Json! }" `; exports[`Introspector Json columns 2`] = ` -"type Boolean @db(name: \\"Booleans\\") { +"type Booleans { a: Json b: Json! }" @@ -49,21 +49,21 @@ exports[`Introspector Unmapped columns 2`] = ` `; exports[`Introspector boolean columns 1`] = ` -"type Boolean @pgTable(name: \\"Booleans\\") { +"type Booleans { a: Boolean b: Boolean! }" `; exports[`Introspector boolean columns 2`] = ` -"type Boolean @db(name: \\"Booleans\\") { +"type Booleans { a: Boolean b: Boolean! }" `; exports[`Introspector float columns 1`] = ` -"type Float @pgTable(name: \\"Floats\\") { +"type Floats { a: Float @default(value: 3.4) b: Float c: Float @@ -78,7 +78,7 @@ exports[`Introspector float columns 1`] = ` `; exports[`Introspector float columns 2`] = ` -"type Float @db(name: \\"Floats\\") { +"type Floats { a: Float @default(value: 3.4) b: Float c: Float @@ -93,7 +93,7 @@ exports[`Introspector float columns 2`] = ` `; exports[`Introspector int columns 1`] = ` -"type Int @pgTable(name: \\"Ints\\") { +"type Ints { a: Int @default(value: 3) b: Int c: Int @@ -104,7 +104,7 @@ exports[`Introspector int columns 1`] = ` `; exports[`Introspector int columns 2`] = ` -"type Int @db(name: \\"Ints\\") { +"type Ints { a: Int @default(value: 3) b: Int c: Int @@ -115,7 +115,7 @@ exports[`Introspector int columns 2`] = ` `; exports[`Introspector text columns 1`] = ` -"type String @pgTable(name: \\"Strings\\") { +"type Strings { id: ID! @pgColumn(name: \\"pk\\") @unique a: String @unique aa: String! @unique @@ -131,7 +131,7 @@ exports[`Introspector text columns 1`] = ` `; exports[`Introspector text columns 2`] = ` -"type String @db(name: \\"Strings\\") { +"type Strings { pk: ID! @id(strategy: NONE) a: String @unique aa: String! @unique @@ -147,7 +147,7 @@ exports[`Introspector text columns 2`] = ` `; exports[`Introspector uuid columns 1`] = ` -"type UUID @pgTable(name: \\"UUIDs\\") { +"type UUIDs { id: UUID! @pgColumn(name: \\"pk\\") @unique nk: UUID! tk: UUID @@ -155,7 +155,7 @@ exports[`Introspector uuid columns 1`] = ` `; exports[`Introspector uuid columns 2`] = ` -"type UUID @db(name: \\"UUIDs\\") { +"type UUIDs { pk: UUID! @id(strategy: NONE) nk: UUID! tk: UUID diff --git a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/sportsdb.ts.snap b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/sportsdb.ts.snap index 84a64867e9..705e56847f 100644 --- a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/sportsdb.ts.snap +++ b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/__snapshots__/sportsdb.ts.snap @@ -31,21 +31,21 @@ type Affiliation @indexes(value: [ ]) @pgTable(name: \\"affiliations\\") { id: Int! @unique affiliation_key: String! - affiliation_phases: [Affiliation_phase] - affiliation_phases: [Affiliation_phase] + affiliation_phases: [AffiliationPhase] + affiliation_phases: [AffiliationPhase] affiliation_type: String - affiliations_documents: [Affiliations_document] - affiliations_events: [Affiliations_event] - affiliations_media: [Affiliations_media] + affiliations_documents: [AffiliationsDocument] + affiliations_events: [AffiliationsEvent] + affiliations_media: [AffiliationsMedia] positions: [Position] publisher_id: Publisher! seasons: [Season] - standing_subgroups: [Standing_subgroup] + standing_subgroups: [StandingSubgroup] standings: [Standing] - team_phases: [Team_phase] + team_phases: [TeamPhase] } -type Affiliation_phase @pgTable(name: \\"affiliation_phases\\") { +type AffiliationPhase @pgTable(name: \\"affiliation_phases\\") { id: Int! @unique affiliation_id: Affiliation! ancestor_affiliation_id: Affiliation @@ -55,27 +55,27 @@ type Affiliation_phase @pgTable(name: \\"affiliation_phases\\") { start_season_id: Season } -type Affiliations_document @linkTable @pgTable(name: \\"affiliations_documents\\") { +type AffiliationsDocument @linkTable @pgTable(name: \\"affiliations_documents\\") { affiliation_id: Affiliation! document_id: Document! } -type Affiliations_event @linkTable @pgTable(name: \\"affiliations_events\\") { +type AffiliationsEvent @linkTable @pgTable(name: \\"affiliations_events\\") { affiliation_id: Affiliation! event_id: Event! } -type Affiliations_media @linkTable @pgTable(name: \\"affiliations_media\\") { +type AffiliationsMedia @linkTable @pgTable(name: \\"affiliations_media\\") { affiliation_id: Affiliation! media_id: Media! } -type American_football_action_participant @indexes(value: [ +type AmericanFootballActionParticipant @indexes(value: [ {name: \\"idx_american_football_action_participants_1\\", fields: [\\"participant_role\\"]}, {name: \\"idx_american_football_action_participants_2\\", fields: [\\"score_type\\"]} ]) @pgTable(name: \\"american_football_action_participants\\") { id: Int! @unique - american_football_action_play_id: American_football_action_play! + american_football_action_play_id: AmericanFootballActionPlay! field_line: Int participant_role: String! person_id: Person! @@ -85,14 +85,14 @@ type American_football_action_participant @indexes(value: [ yards_gained: Int } -type American_football_action_play @indexes(value: [ +type AmericanFootballActionPlay @indexes(value: [ {name: \\"idx_american_football_action_plays_1\\", fields: [\\"play_type\\"]}, {name: \\"idx_american_football_action_plays_2\\", fields: [\\"score_attempt_type\\"]}, {name: \\"idx_american_football_action_plays_3\\", fields: [\\"drive_result\\"]} ]) @pgTable(name: \\"american_football_action_plays\\") { id: Int! @unique - american_football_action_participants: [American_football_action_participant] - american_football_event_state_id: American_football_event_state! + american_football_action_participants: [AmericanFootballActionParticipant] + american_football_event_state_id: AmericanFootballEventState! comment: String drive_result: String play_type: String @@ -100,7 +100,7 @@ type American_football_action_play @indexes(value: [ score_attempt_type: String } -type American_football_defensive_stat @pgTable(name: \\"american_football_defensive_stats\\") { +type AmericanFootballDefensiveStat @pgTable(name: \\"american_football_defensive_stats\\") { id: Int! @unique interceptions_average: String interceptions_longest: String @@ -116,7 +116,7 @@ type American_football_defensive_stat @pgTable(name: \\"american_football_defens tackles_total: String } -type American_football_down_progress_stat @pgTable(name: \\"american_football_down_progress_stats\\") { +type AmericanFootballDownProgressStat @pgTable(name: \\"american_football_down_progress_stats\\") { id: Int! @unique conversions_fourth_down: String conversions_fourth_down_attempts: String @@ -130,11 +130,11 @@ type American_football_down_progress_stat @pgTable(name: \\"american_football_do first_downs_total: String } -type American_football_event_state @indexes(value: [ +type AmericanFootballEventState @indexes(value: [ {name: \\"idx_american_football_event_states_1\\", fields: [\\"current_state\\"]} ]) @pgTable(name: \\"american_football_event_states\\") { id: Int! @unique - american_football_action_plays: [American_football_action_play] + american_football_action_plays: [AmericanFootballActionPlay] clock_state: String context: String current_state: Int @@ -150,7 +150,7 @@ type American_football_event_state @indexes(value: [ team_in_possession_id: Team } -type American_football_fumbles_stat @pgTable(name: \\"american_football_fumbles_stats\\") { +type AmericanFootballFumblesStat @pgTable(name: \\"american_football_fumbles_stats\\") { id: Int! @unique fumbles_committed: String fumbles_forced: String @@ -167,7 +167,7 @@ type American_football_fumbles_stat @pgTable(name: \\"american_football_fumbles_ fumbles_yards_gained: String } -type American_football_offensive_stat @pgTable(name: \\"american_football_offensive_stats\\") { +type AmericanFootballOffensiveStat @pgTable(name: \\"american_football_offensive_stats\\") { id: Int! @unique offensive_plays_average_yards_per: String offensive_plays_number: String @@ -176,7 +176,7 @@ type American_football_offensive_stat @pgTable(name: \\"american_football_offens turnovers_giveaway: String } -type American_football_passing_stat @pgTable(name: \\"american_football_passing_stats\\") { +type AmericanFootballPassingStat @pgTable(name: \\"american_football_passing_stats\\") { id: Int! @unique passer_rating: String passes_attempts: String @@ -199,14 +199,14 @@ type American_football_passing_stat @pgTable(name: \\"american_football_passing_ receptions_yards: String } -type American_football_penalties_stat @pgTable(name: \\"american_football_penalties_stats\\") { +type AmericanFootballPenaltiesStat @pgTable(name: \\"american_football_penalties_stats\\") { id: Int! @unique penalties_total: String penalty_first_downs: String penalty_yards: String } -type American_football_rushing_stat @pgTable(name: \\"american_football_rushing_stats\\") { +type AmericanFootballRushingStat @pgTable(name: \\"american_football_rushing_stats\\") { id: Int! @unique rushes_attempts: String rushes_first_down: String @@ -216,13 +216,13 @@ type American_football_rushing_stat @pgTable(name: \\"american_football_rushing_ rushing_average_yards_per: String } -type American_football_sacks_against_stat @pgTable(name: \\"american_football_sacks_against_stats\\") { +type AmericanFootballSacksAgainstStat @pgTable(name: \\"american_football_sacks_against_stats\\") { id: Int! @unique sacks_against_total: String sacks_against_yards: String } -type American_football_scoring_stat @pgTable(name: \\"american_football_scoring_stats\\") { +type AmericanFootballScoringStat @pgTable(name: \\"american_football_scoring_stats\\") { id: Int! @unique extra_points_attempts: String extra_points_blocked: String @@ -243,7 +243,7 @@ type American_football_scoring_stat @pgTable(name: \\"american_football_scoring_ two_point_conversions_made: String } -type American_football_special_teams_stat @pgTable(name: \\"american_football_special_teams_stats\\") { +type AmericanFootballSpecialTeamsStat @pgTable(name: \\"american_football_special_teams_stats\\") { id: Int! @unique fair_catches: String punts_average: String @@ -276,9 +276,9 @@ type American_football_special_teams_stat @pgTable(name: \\"american_football_sp touchbacks_total_percentage: String } -type Baseball_action_contact_detail @pgTable(name: \\"baseball_action_contact_details\\") { +type BaseballActionContactDetail @pgTable(name: \\"baseball_action_contact_details\\") { id: Int! @unique - baseball_action_pitch_id: Baseball_action_pitch! + baseball_action_pitch_id: BaseballActionPitch! comment: String location: String strength: String @@ -287,15 +287,15 @@ type Baseball_action_contact_detail @pgTable(name: \\"baseball_action_contact_de velocity: Int } -type Baseball_action_pitch @indexes(value: [ +type BaseballActionPitch @indexes(value: [ {name: \\"idx_baseball_action_pitches_1\\", fields: [\\"umpire_call\\"]}, {name: \\"idx_baseball_action_pitches_2\\", fields: [\\"pitch_type\\"]} ]) @pgTable(name: \\"baseball_action_pitches\\") { id: Int! @unique ball_type: String - baseball_action_contact_details: [Baseball_action_contact_detail] - baseball_action_play_id: Baseball_action_play! - baseball_defensive_group_id: Baseball_defensive_group + baseball_action_contact_details: [BaseballActionContactDetail] + baseball_action_play_id: BaseballActionPlay! + baseball_defensive_group_id: BaseballDefensiveGroup comment: String pitch_location: String pitch_type: String @@ -307,13 +307,13 @@ type Baseball_action_pitch @indexes(value: [ umpire_call: String } -type Baseball_action_play @indexes(value: [ +type BaseballActionPlay @indexes(value: [ {name: \\"idx_baseball_action_plays_1\\", fields: [\\"play_type\\"]} ]) @pgTable(name: \\"baseball_action_plays\\") { id: Int! @unique - baseball_action_pitches: [Baseball_action_pitch] + baseball_action_pitches: [BaseballActionPitch] baseball_defensive_group_id: Int - baseball_event_state_id: Baseball_event_state! + baseball_event_state_id: BaseballEventState! comment: String earned_runs_scored: String notation: String @@ -327,9 +327,9 @@ type Baseball_action_play @indexes(value: [ runs_scored: Int } -type Baseball_action_substitution @pgTable(name: \\"baseball_action_substitutions\\") { +type BaseballActionSubstitution @pgTable(name: \\"baseball_action_substitutions\\") { id: Int! @unique - baseball_event_state_id: Baseball_event_state! + baseball_event_state_id: BaseballEventState! comment: String person_original_id: Person person_original_lineup_slot: Int @@ -342,20 +342,20 @@ type Baseball_action_substitution @pgTable(name: \\"baseball_action_substitution substitution_reason: String } -type Baseball_defensive_group @linkTable @pgTable(name: \\"baseball_defensive_group\\") { +type BaseballDefensiveGroup @linkTable @pgTable(name: \\"baseball_defensive_group\\") { id: Int! @unique - baseball_action_pitches: [Baseball_action_pitch] - baseball_defensive_players: [Baseball_defensive_player] + baseball_action_pitches: [BaseballActionPitch] + baseball_defensive_players: [BaseballDefensivePlayer] } -type Baseball_defensive_player @pgTable(name: \\"baseball_defensive_players\\") { +type BaseballDefensivePlayer @pgTable(name: \\"baseball_defensive_players\\") { id: Int! @unique - baseball_defensive_group_id: Baseball_defensive_group! + baseball_defensive_group_id: BaseballDefensiveGroup! player_id: Person! position_id: Position! } -type Baseball_defensive_stat @pgTable(name: \\"baseball_defensive_stats\\") { +type BaseballDefensiveStat @pgTable(name: \\"baseball_defensive_stats\\") { id: Int! @unique assists: Int defensive_average: Float @@ -368,14 +368,14 @@ type Baseball_defensive_stat @pgTable(name: \\"baseball_defensive_stats\\") { triple_plays: Int } -type Baseball_event_state @indexes(value: [ +type BaseballEventState @indexes(value: [ {name: \\"idx_baseball_event_states_1\\", fields: [\\"current_state\\"]} ]) @pgTable(name: \\"baseball_event_states\\") { id: Int! @unique at_bat_number: Int balls: Int - baseball_action_plays: [Baseball_action_play] - baseball_action_substitutions: [Baseball_action_substitution] + baseball_action_plays: [BaseballActionPlay] + baseball_action_substitutions: [BaseballActionSubstitution] batter_id: Person batter_side: String context: String @@ -396,7 +396,7 @@ type Baseball_event_state @indexes(value: [ strikes: Int } -type Baseball_offensive_stat @pgTable(name: \\"baseball_offensive_stats\\") { +type BaseballOffensiveStat @pgTable(name: \\"baseball_offensive_stats\\") { id: Int! @unique at_bats: Int at_bats_per_home_run: Float @@ -433,7 +433,7 @@ type Baseball_offensive_stat @pgTable(name: \\"baseball_offensive_stats\\") { triples: Int } -type Baseball_pitching_stat @pgTable(name: \\"baseball_pitching_stats\\") { +type BaseballPitchingStat @pgTable(name: \\"baseball_pitching_stats\\") { id: Int! @unique balks: Int bases_on_balls: Int @@ -466,7 +466,7 @@ type Baseball_pitching_stat @pgTable(name: \\"baseball_pitching_stats\\") { wins: Int } -type Basketball_defensive_stat @pgTable(name: \\"basketball_defensive_stats\\") { +type BasketballDefensiveStat @pgTable(name: \\"basketball_defensive_stats\\") { id: Int! @unique blocks_per_game: String blocks_total: String @@ -474,7 +474,7 @@ type Basketball_defensive_stat @pgTable(name: \\"basketball_defensive_stats\\") steals_total: String } -type Basketball_event_state @pgTable(name: \\"basketball_event_states\\") { +type BasketballEventState @pgTable(name: \\"basketball_event_states\\") { id: Int! @unique context: String current_state: Int @@ -485,7 +485,7 @@ type Basketball_event_state @pgTable(name: \\"basketball_event_states\\") { sequence_number: Int } -type Basketball_offensive_stat @pgTable(name: \\"basketball_offensive_stats\\") { +type BasketballOffensiveStat @pgTable(name: \\"basketball_offensive_stats\\") { id: Int! @unique assists_per_game: String assists_total: String @@ -515,7 +515,7 @@ type Basketball_offensive_stat @pgTable(name: \\"basketball_offensive_stats\\") turnovers_total: String } -type Basketball_rebounding_stat @pgTable(name: \\"basketball_rebounding_stats\\") { +type BasketballReboundingStat @pgTable(name: \\"basketball_rebounding_stats\\") { id: Int! @unique rebounds_defensive: String rebounds_offensive: String @@ -527,7 +527,7 @@ type Basketball_rebounding_stat @pgTable(name: \\"basketball_rebounding_stats\\" team_rebounds_total: String } -type Basketball_team_stat @pgTable(name: \\"basketball_team_stats\\") { +type BasketballTeamStat @pgTable(name: \\"basketball_team_stats\\") { id: Int! @unique fouls_total: String largest_lead: String @@ -540,14 +540,14 @@ type Bookmaker @pgTable(name: \\"bookmakers\\") { bookmaker_key: String location_id: Location publisher_id: Publisher! - wagering_moneylines: [Wagering_moneyline] - wagering_odds_lines: [Wagering_odds_line] - wagering_runlines: [Wagering_runline] - wagering_straight_spread_lines: [Wagering_straight_spread_line] - wagering_total_score_lines: [Wagering_total_score_line] + wagering_moneylines: [WageringMoneyline] + wagering_odds_lines: [WageringOddsLine] + wagering_runlines: [WageringRunline] + wagering_straight_spread_lines: [WageringStraightSpreadLine] + wagering_total_score_lines: [WageringTotalScoreLine] } -type Core_person_stat @pgTable(name: \\"core_person_stats\\") { +type CorePersonStat @pgTable(name: \\"core_person_stats\\") { id: Int! @unique events_played: Int events_started: Int @@ -557,7 +557,7 @@ type Core_person_stat @pgTable(name: \\"core_person_stats\\") { time_played_total: String } -type Core_stat @pgTable(name: \\"core_stats\\") { +type CoreStat @pgTable(name: \\"core_stats\\") { id: Int! @unique score: String score_attempts: String @@ -567,13 +567,13 @@ type Core_stat @pgTable(name: \\"core_stats\\") { score_percentage_opposing: String } -type Db_info @indexes(value: [ +type DbInfo @indexes(value: [ {name: \\"idx_db_info_1\\", fields: [\\"version\\"]} ]) @pgTable(name: \\"db_info\\") { version: String! @default(value: \\"16\\") } -type Display_name @pgTable(name: \\"display_names\\") { +type DisplayName @pgTable(name: \\"display_names\\") { id: Int! @unique abbreviation: String alias: String @@ -596,84 +596,84 @@ type Document @indexes(value: [ {name: \\"idx_documents_5\\", fields: [\\"revision_id\\"]} ]) @pgTable(name: \\"documents\\") { id: Int! @unique - affiliations_documents: [Affiliations_document] + affiliations_documents: [AffiliationsDocument] date_time: DateTime db_loading_date_time: DateTime doc_id: String! - document_contents: [Document_content] - document_fixture_id: Document_fixture! - document_fixtures_events: [Document_fixtures_event] - document_package_entry: [Document_package_entry] - documents_media: [Documents_media] - events_documents: [Events_document] + document_contents: [DocumentContent] + document_fixture_id: DocumentFixture! + document_fixtures_events: [DocumentFixturesEvent] + document_package_entry: [DocumentPackageEntry] + documents_media: [DocumentsMedia] + events_documents: [EventsDocument] language: String - latest_revisions: [Latest_revision] - persons_documents: [Persons_document] + latest_revisions: [LatestRevision] + persons_documents: [PersonsDocument] priority: String publisher_id: Publisher! revision_id: String source_id: Publisher stats_coverage: String - teams_documents: [Teams_document] + teams_documents: [TeamsDocument] title: String } -type Document_class @indexes(value: [ +type DocumentClass @indexes(value: [ {name: \\"idx_document_classes_1\\", fields: [\\"name\\"]} ]) @pgTable(name: \\"document_classes\\") { id: Int! @unique - document_fixtures: [Document_fixture] + document_fixtures: [DocumentFixture] name: String } -type Document_content @pgTable(name: \\"document_contents\\") { +type DocumentContent @pgTable(name: \\"document_contents\\") { id: Int! @unique abstract: String document_id: Document! sportsml: String } -type Document_fixture @indexes(value: [ +type DocumentFixture @indexes(value: [ {name: \\"idx_document_fixtures_1\\", fields: [\\"fixture_key\\"]} ]) @pgTable(name: \\"document_fixtures\\") { id: Int! @unique - document_class_id: Document_class! - document_fixtures_events: [Document_fixtures_event] + document_class_id: DocumentClass! + document_fixtures_events: [DocumentFixturesEvent] documents: [Document] fixture_key: String name: String publisher_id: Publisher! } -type Document_fixtures_event @pgTable(name: \\"document_fixtures_events\\") { +type DocumentFixturesEvent @pgTable(name: \\"document_fixtures_events\\") { id: Int! @unique - document_fixture_id: Document_fixture! + document_fixture_id: DocumentFixture! event_id: Event! last_update: DateTime latest_document_id: Document! } -type Document_package @pgTable(name: \\"document_packages\\") { +type DocumentPackage @pgTable(name: \\"document_packages\\") { id: Int! @unique date_time: DateTime - document_package_entry: [Document_package_entry] + document_package_entry: [DocumentPackageEntry] package_key: String package_name: String } -type Document_package_entry @pgTable(name: \\"document_package_entry\\") { +type DocumentPackageEntry @pgTable(name: \\"document_package_entry\\") { id: Int! @unique document_id: Document! - document_package_id: Document_package! + document_package_id: DocumentPackage! headline: String rank: String short_headline: String } -type Documents_media @pgTable(name: \\"documents_media\\") { +type DocumentsMedia @pgTable(name: \\"documents_media\\") { id: Int! @unique document_id: Document! - media_caption_id: Media_caption! + media_caption_id: MediaCaption! media_id: Media! } @@ -681,53 +681,53 @@ type Event @indexes(value: [ {name: \\"idx_events_1\\", fields: [\\"event_key\\"]} ]) @pgTable(name: \\"events\\") { id: Int! @unique - affiliations_events: [Affiliations_event] - american_football_event_states: [American_football_event_state] + affiliations_events: [AffiliationsEvent] + american_football_event_states: [AmericanFootballEventState] attendance: String - baseball_event_states: [Baseball_event_state] - basketball_event_states: [Basketball_event_state] - document_fixtures_events: [Document_fixtures_event] + baseball_event_states: [BaseballEventState] + basketball_event_states: [BasketballEventState] + document_fixtures_events: [DocumentFixturesEvent] duration: String event_key: String! event_status: String - events_documents: [Events_document] - events_media: [Events_media] - events_sub_seasons: [Events_sub_season] - ice_hockey_event_states: [Ice_hockey_event_state] + events_documents: [EventsDocument] + events_media: [EventsMedia] + events_sub_seasons: [EventsSubSeason] + ice_hockey_event_states: [IceHockeyEventState] last_update: DateTime - motor_racing_event_states: [Motor_racing_event_state] - participants_events: [Participants_event] - person_event_metadata: [Person_event_metadatum] + motor_racing_event_states: [MotorRacingEventState] + participants_events: [ParticipantsEvent] + person_event_metadata: [PersonEventMetadatum] publisher_id: Publisher! site_alignment: String site_id: Site - soccer_event_states: [Soccer_event_state] + soccer_event_states: [SoccerEventState] start_date_time: DateTime - tennis_event_states: [Tennis_event_state] - wagering_moneylines: [Wagering_moneyline] - wagering_odds_lines: [Wagering_odds_line] - wagering_runlines: [Wagering_runline] - wagering_straight_spread_lines: [Wagering_straight_spread_line] - wagering_total_score_lines: [Wagering_total_score_line] - weather_conditions: [Weather_condition] + tennis_event_states: [TennisEventState] + wagering_moneylines: [WageringMoneyline] + wagering_odds_lines: [WageringOddsLine] + wagering_runlines: [WageringRunline] + wagering_straight_spread_lines: [WageringStraightSpreadLine] + wagering_total_score_lines: [WageringTotalScoreLine] + weather_conditions: [WeatherCondition] } -type Events_document @linkTable @pgTable(name: \\"events_documents\\") { +type EventsDocument @linkTable @pgTable(name: \\"events_documents\\") { document_id: Document! event_id: Event! } -type Events_media @linkTable @pgTable(name: \\"events_media\\") { +type EventsMedia @linkTable @pgTable(name: \\"events_media\\") { event_id: Event! media_id: Media! } -type Events_sub_season @linkTable @pgTable(name: \\"events_sub_seasons\\") { +type EventsSubSeason @linkTable @pgTable(name: \\"events_sub_seasons\\") { event_id: Event! - sub_season_id: Sub_season! + sub_season_id: SubSeason! } -type Ice_hockey_action_participant @pgTable(name: \\"ice_hockey_action_participants\\") { +type IceHockeyActionParticipant @pgTable(name: \\"ice_hockey_action_participants\\") { id: Int! @unique ice_hockey_action_play_id: Int! participant_role: String! @@ -735,7 +735,7 @@ type Ice_hockey_action_participant @pgTable(name: \\"ice_hockey_action_participa point_credit: Int } -type Ice_hockey_action_play @pgTable(name: \\"ice_hockey_action_plays\\") { +type IceHockeyActionPlay @pgTable(name: \\"ice_hockey_action_plays\\") { id: Int! @unique comment: String ice_hockey_event_state_id: Int! @@ -744,7 +744,7 @@ type Ice_hockey_action_play @pgTable(name: \\"ice_hockey_action_plays\\") { score_attempt_type: String } -type Ice_hockey_defensive_stat @pgTable(name: \\"ice_hockey_defensive_stats\\") { +type IceHockeyDefensiveStat @pgTable(name: \\"ice_hockey_defensive_stats\\") { id: Int! @unique goals_against_average: String goals_empty_net_allowed: String @@ -766,7 +766,7 @@ type Ice_hockey_defensive_stat @pgTable(name: \\"ice_hockey_defensive_stats\\") takeaways: String } -type Ice_hockey_event_state @pgTable(name: \\"ice_hockey_event_states\\") { +type IceHockeyEventState @pgTable(name: \\"ice_hockey_event_states\\") { id: Int! @unique context: String current_state: Int @@ -777,7 +777,7 @@ type Ice_hockey_event_state @pgTable(name: \\"ice_hockey_event_states\\") { sequence_number: Int } -type Ice_hockey_offensive_stat @pgTable(name: \\"ice_hockey_offensive_stats\\") { +type IceHockeyOffensiveStat @pgTable(name: \\"ice_hockey_offensive_stats\\") { id: Int! @unique assists: String faceoff_losses: String @@ -803,12 +803,12 @@ type Ice_hockey_offensive_stat @pgTable(name: \\"ice_hockey_offensive_stats\\") shots_penalty_shot_taken: String } -type Ice_hockey_player_stat @pgTable(name: \\"ice_hockey_player_stats\\") { +type IceHockeyPlayerStat @pgTable(name: \\"ice_hockey_player_stats\\") { id: Int! @unique plus_minus: String } -type Injury_phase @indexes(value: [ +type InjuryPhase @indexes(value: [ {name: \\"idx_injury_phases_2\\", fields: [\\"injury_status\\"]}, {name: \\"idx_injury_phases_3\\", fields: [\\"start_date_time\\"]}, {name: \\"idx_injury_phases_4\\", fields: [\\"end_date_time\\"]} @@ -826,23 +826,23 @@ type Injury_phase @indexes(value: [ start_date_time: DateTime } -type Key_alias @indexes(value: [ +type KeyAlias @indexes(value: [ {name: \\"idx_key_aliases_2\\", fields: [\\"key_id\\"]} ]) @pgTable(name: \\"key_aliases\\") { id: Int! @unique key_id: Int! - key_root_id: Key_root! + key_root_id: KeyRoot! } -type Key_root @indexes(value: [ +type KeyRoot @indexes(value: [ {name: \\"idx_key_aliases_1\\", fields: [\\"key_type\\"]} ]) @pgTable(name: \\"key_roots\\") { id: Int! @unique - key_aliases: [Key_alias] + key_aliases: [KeyAlias] key_type: String } -type Latest_revision @indexes(value: [ +type LatestRevision @indexes(value: [ {name: \\"idx_latest_revisions_1\\", fields: [\\"revision_id\\"]} ]) @pgTable(name: \\"latest_revisions\\") { id: Int! @unique @@ -870,37 +870,37 @@ type Location @indexes(value: [ type Media @pgTable(name: \\"media\\") { id: Int! @unique - affiliations_media: [Affiliations_media] + affiliations_media: [AffiliationsMedia] creation_location_id: Location! credit_id: Person! date_time: String db_loading_date_time: DateTime - documents_media: [Documents_media] - events_media: [Events_media] - media_captions: [Media_caption] - media_contents: [Media_content] - media_keywords: [Media_keyword] + documents_media: [DocumentsMedia] + events_media: [EventsMedia] + media_captions: [MediaCaption] + media_contents: [MediaContent] + media_keywords: [MediaKeyword] media_type: String object_id: Int - persons_media: [Persons_media] + persons_media: [PersonsMedia] publisher_id: Publisher! revision_id: Int source_id: Int - teams_media: [Teams_media] + teams_media: [TeamsMedia] } -type Media_caption @pgTable(name: \\"media_captions\\") { +type MediaCaption @pgTable(name: \\"media_captions\\") { id: Int! @unique caption: String caption_author_id: Person! caption_size: String caption_type: String - documents_media: [Documents_media] + documents_media: [DocumentsMedia] language: String media_id: Media! } -type Media_content @pgTable(name: \\"media_contents\\") { +type MediaContent @pgTable(name: \\"media_contents\\") { id: Int! @unique duration: String file_size: String @@ -913,13 +913,13 @@ type Media_content @pgTable(name: \\"media_contents\\") { width: String } -type Media_keyword @pgTable(name: \\"media_keywords\\") { +type MediaKeyword @pgTable(name: \\"media_keywords\\") { id: Int! @unique keyword: String media_id: Media! } -type Motor_racing_event_state @pgTable(name: \\"motor_racing_event_states\\") { +type MotorRacingEventState @pgTable(name: \\"motor_racing_event_states\\") { id: Int! @unique context: String current_state: Int @@ -931,7 +931,7 @@ type Motor_racing_event_state @pgTable(name: \\"motor_racing_event_states\\") { time_elapsed: String } -type Motor_racing_qualifying_stat @pgTable(name: \\"motor_racing_qualifying_stats\\") { +type MotorRacingQualifyingStat @pgTable(name: \\"motor_racing_qualifying_stats\\") { id: Int! @unique grid: String pole_position: String @@ -942,7 +942,7 @@ type Motor_racing_qualifying_stat @pgTable(name: \\"motor_racing_qualifying_stat qualifying_time: String } -type Motor_racing_race_stat @pgTable(name: \\"motor_racing_race_stats\\") { +type MotorRacingRaceStat @pgTable(name: \\"motor_racing_race_stats\\") { id: Int! @unique bonus: String distance_completed: String @@ -972,7 +972,7 @@ type Motor_racing_race_stat @pgTable(name: \\"motor_racing_race_stats\\") { wins: String } -type Outcome_total @pgTable(name: \\"outcome_totals\\") { +type OutcomeTotal @pgTable(name: \\"outcome_totals\\") { id: Int! @unique losses: String outcome_holder_id: Int @@ -982,7 +982,7 @@ type Outcome_total @pgTable(name: \\"outcome_totals\\") { points_scored_for: String rank: String standing_points: String - standing_subgroup_id: Standing_subgroup! + standing_subgroup_id: StandingSubgroup! streak_duration: String streak_end: DateTime streak_start: DateTime @@ -994,7 +994,7 @@ type Outcome_total @pgTable(name: \\"outcome_totals\\") { wins: String } -type Participants_event @indexes(value: [ +type ParticipantsEvent @indexes(value: [ {name: \\"idx_participants_events_1\\", fields: [\\"participant_type\\"]}, {name: \\"idx_participants_events_2\\", fields: [\\"participant_id\\"]}, {name: \\"idx_participants_events_3\\", fields: [\\"alignment\\"]}, @@ -1013,44 +1013,44 @@ type Participants_event @indexes(value: [ type Period @pgTable(name: \\"periods\\") { id: Int! @unique - participant_event_id: Participants_event! + participant_event_id: ParticipantsEvent! period_value: String score: String - sub_periods: [Sub_period] + sub_periods: [SubPeriod] } type Person @indexes(value: [ {name: \\"idx_persons_1\\", fields: [\\"person_key\\"]} ]) @pgTable(name: \\"persons\\") { id: Int! @unique - american_football_action_participants: [American_football_action_participant] - baseball_action_substitutions: [Baseball_action_substitution] - baseball_action_substitutions: [Baseball_action_substitution] - baseball_defensive_players: [Baseball_defensive_player] - baseball_event_states: [Baseball_event_state] - baseball_event_states: [Baseball_event_state] - baseball_event_states: [Baseball_event_state] - baseball_event_states: [Baseball_event_state] - baseball_event_states: [Baseball_event_state] + american_football_action_participants: [AmericanFootballActionParticipant] + baseball_action_substitutions: [BaseballActionSubstitution] + baseball_action_substitutions: [BaseballActionSubstitution] + baseball_defensive_players: [BaseballDefensivePlayer] + baseball_event_states: [BaseballEventState] + baseball_event_states: [BaseballEventState] + baseball_event_states: [BaseballEventState] + baseball_event_states: [BaseballEventState] + baseball_event_states: [BaseballEventState] birth_date: String birth_location_id: Location death_date: String death_location_id: Location gender: String hometown_location_id: Location - injury_phases: [Injury_phase] + injury_phases: [InjuryPhase] media: [Media] - media_captions: [Media_caption] - person_event_metadata: [Person_event_metadatum] + media_captions: [MediaCaption] + person_event_metadata: [PersonEventMetadatum] person_key: String! - person_phases: [Person_phase] - persons_documents: [Persons_document] - persons_media: [Persons_media] + person_phases: [PersonPhase] + persons_documents: [PersonsDocument] + persons_media: [PersonsMedia] publisher_id: Publisher! residence_location_id: Location } -type Person_event_metadatum @indexes(value: [ +type PersonEventMetadatum @indexes(value: [ {name: \\"idx_person_event_metadata_1\\", fields: [\\"status\\"]} ]) @pgTable(name: \\"person_event_metadata\\") { id: Int! @unique @@ -1066,7 +1066,7 @@ type Person_event_metadatum @indexes(value: [ weight: String } -type Person_phase @indexes(value: [ +type PersonPhase @indexes(value: [ {name: \\"idx_person_phases_1\\", fields: [\\"membership_type\\"]}, {name: \\"idx_person_phases_2\\", fields: [\\"membership_id\\"]}, {name: \\"idx_person_phases_3\\", fields: [\\"phase_status\\"]} @@ -1094,12 +1094,12 @@ type Person_phase @indexes(value: [ weight: String } -type Persons_document @linkTable @pgTable(name: \\"persons_documents\\") { +type PersonsDocument @linkTable @pgTable(name: \\"persons_documents\\") { document_id: Document! person_id: Person! } -type Persons_media @linkTable @pgTable(name: \\"persons_media\\") { +type PersonsMedia @linkTable @pgTable(name: \\"persons_media\\") { media_id: Media! person_id: Person! } @@ -1110,12 +1110,12 @@ type Position @indexes(value: [ id: Int! @unique abbreviation: String! affiliation_id: Affiliation! - baseball_action_substitutions: [Baseball_action_substitution] - baseball_action_substitutions: [Baseball_action_substitution] - baseball_defensive_players: [Baseball_defensive_player] - core_person_stats: [Core_person_stat] - person_event_metadata: [Person_event_metadatum] - person_phases: [Person_phase] + baseball_action_substitutions: [BaseballActionSubstitution] + baseball_action_substitutions: [BaseballActionSubstitution] + baseball_defensive_players: [BaseballDefensivePlayer] + core_person_stats: [CorePersonStat] + person_event_metadata: [PersonEventMetadatum] + person_phases: [PersonPhase] } type Publisher @indexes(value: [ @@ -1124,7 +1124,7 @@ type Publisher @indexes(value: [ id: Int! @unique affiliations: [Affiliation] bookmakers: [Bookmaker] - document_fixtures: [Document_fixture] + document_fixtures: [DocumentFixture] documents: [Document] documents: [Document] events: [Event] @@ -1143,30 +1143,30 @@ type Role @indexes(value: [ ]) @pgTable(name: \\"roles\\") { id: Int! @unique comment: String - person_event_metadata: [Person_event_metadatum] - person_phases: [Person_phase] + person_event_metadata: [PersonEventMetadatum] + person_phases: [PersonPhase] role_key: String! role_name: String - team_phases: [Team_phase] + team_phases: [TeamPhase] } type Season @indexes(value: [ {name: \\"idx_seasons_1\\", fields: [\\"season_key\\"]} ]) @pgTable(name: \\"seasons\\") { id: Int! @unique - affiliation_phases: [Affiliation_phase] - affiliation_phases: [Affiliation_phase] + affiliation_phases: [AffiliationPhase] + affiliation_phases: [AffiliationPhase] end_date_time: DateTime - injury_phases: [Injury_phase] + injury_phases: [InjuryPhase] league_id: Affiliation! - person_phases: [Person_phase] - person_phases: [Person_phase] + person_phases: [PersonPhase] + person_phases: [PersonPhase] publisher_id: Publisher! season_key: Int! start_date_time: DateTime - sub_seasons: [Sub_season] - team_phases: [Team_phase] - team_phases: [Team_phase] + sub_seasons: [SubSeason] + team_phases: [TeamPhase] + team_phases: [TeamPhase] } type Site @indexes(value: [ @@ -1180,7 +1180,7 @@ type Site @indexes(value: [ teams: [Team] } -type Soccer_defensive_stat @pgTable(name: \\"soccer_defensive_stats\\") { +type SoccerDefensiveStat @pgTable(name: \\"soccer_defensive_stats\\") { id: Int! @unique catches_punches: String goals_against_average: String @@ -1196,7 +1196,7 @@ type Soccer_defensive_stat @pgTable(name: \\"soccer_defensive_stats\\") { shutouts: String } -type Soccer_event_state @pgTable(name: \\"soccer_event_states\\") { +type SoccerEventState @pgTable(name: \\"soccer_event_states\\") { id: Int! @unique context: String current_state: Int @@ -1209,7 +1209,7 @@ type Soccer_event_state @pgTable(name: \\"soccer_event_states\\") { sequence_number: Int } -type Soccer_foul_stat @pgTable(name: \\"soccer_foul_stats\\") { +type SoccerFoulStat @pgTable(name: \\"soccer_foul_stats\\") { id: Int! @unique caution_points_pending: String caution_points_total: String @@ -1220,7 +1220,7 @@ type Soccer_foul_stat @pgTable(name: \\"soccer_foul_stats\\") { fouls_suffered: String } -type Soccer_offensive_stat @pgTable(name: \\"soccer_offensive_stats\\") { +type SoccerOffensiveStat @pgTable(name: \\"soccer_offensive_stats\\") { id: Int! @unique assists_game_tying: String assists_game_winning: String @@ -1261,15 +1261,15 @@ type Standing @pgTable(name: \\"standings\\") { scoping_label: String site_scope: String source: String - standing_subgroups: [Standing_subgroup] + standing_subgroups: [StandingSubgroup] standing_type: String - sub_season_id: Sub_season! + sub_season_id: SubSeason! } -type Standing_subgroup @pgTable(name: \\"standing_subgroups\\") { +type StandingSubgroup @pgTable(name: \\"standing_subgroups\\") { id: Int! @unique affiliation_id: Affiliation! - outcome_totals: [Outcome_total] + outcome_totals: [OutcomeTotal] standing_id: Standing! } @@ -1292,20 +1292,20 @@ type Stat @indexes(value: [ stat_repository_type: String } -type Sub_period @pgTable(name: \\"sub_periods\\") { +type SubPeriod @pgTable(name: \\"sub_periods\\") { id: Int! @unique period_id: Period! score: String sub_period_value: String } -type Sub_season @indexes(value: [ +type SubSeason @indexes(value: [ {name: \\"idx_sub_seasons_1\\", fields: [\\"sub_season_key\\"]}, {name: \\"idx_sub_seasons_2\\", fields: [\\"sub_season_type\\"]} ]) @pgTable(name: \\"sub_seasons\\") { id: Int! @unique end_date_time: DateTime - events_sub_seasons: [Events_sub_season] + events_sub_seasons: [EventsSubSeason] season_id: Season! standings: [Standing] start_date_time: DateTime @@ -1315,22 +1315,22 @@ type Sub_season @indexes(value: [ type Team @pgTable(name: \\"teams\\") { id: Int! @unique - american_football_event_states: [American_football_event_state] + american_football_event_states: [AmericanFootballEventState] home_site_id: Site - person_event_metadata: [Person_event_metadatum] + person_event_metadata: [PersonEventMetadatum] publisher_id: Publisher! team_key: String! - team_phases: [Team_phase] - teams_documents: [Teams_document] - teams_media: [Teams_media] - wagering_moneylines: [Wagering_moneyline] - wagering_odds_lines: [Wagering_odds_line] - wagering_runlines: [Wagering_runline] - wagering_straight_spread_lines: [Wagering_straight_spread_line] - wagering_total_score_lines: [Wagering_total_score_line] + team_phases: [TeamPhase] + teams_documents: [TeamsDocument] + teams_media: [TeamsMedia] + wagering_moneylines: [WageringMoneyline] + wagering_odds_lines: [WageringOddsLine] + wagering_runlines: [WageringRunline] + wagering_straight_spread_lines: [WageringStraightSpreadLine] + wagering_total_score_lines: [WageringTotalScoreLine] } -type Team_american_football_stat @pgTable(name: \\"team_american_football_stats\\") { +type TeamAmericanFootballStat @pgTable(name: \\"team_american_football_stats\\") { id: Int! @unique average_starting_position: String time_of_possession: String @@ -1339,7 +1339,7 @@ type Team_american_football_stat @pgTable(name: \\"team_american_football_stats\ yards_per_attempt: String } -type Team_phase @pgTable(name: \\"team_phases\\") { +type TeamPhase @pgTable(name: \\"team_phases\\") { id: Int! @unique affiliation_id: Affiliation! end_date_time: String @@ -1351,24 +1351,24 @@ type Team_phase @pgTable(name: \\"team_phases\\") { team_id: Team! } -type Teams_document @linkTable @pgTable(name: \\"teams_documents\\") { +type TeamsDocument @linkTable @pgTable(name: \\"teams_documents\\") { document_id: Document! team_id: Team! } -type Teams_media @linkTable @pgTable(name: \\"teams_media\\") { +type TeamsMedia @linkTable @pgTable(name: \\"teams_media\\") { media_id: Media! team_id: Team! } -type Tennis_action_point @pgTable(name: \\"tennis_action_points\\") { +type TennisActionPoint @pgTable(name: \\"tennis_action_points\\") { id: Int! @unique sequence_number: String sub_period_id: String win_type: String } -type Tennis_action_volley @pgTable(name: \\"tennis_action_volleys\\") { +type TennisActionVolley @pgTable(name: \\"tennis_action_volleys\\") { id: Int! @unique landing_location: String result: String @@ -1379,7 +1379,7 @@ type Tennis_action_volley @pgTable(name: \\"tennis_action_volleys\\") { trajectory_details: String } -type Tennis_event_state @pgTable(name: \\"tennis_event_states\\") { +type TennisEventState @pgTable(name: \\"tennis_event_states\\") { id: Int! @unique context: String current_state: Int @@ -1394,7 +1394,7 @@ type Tennis_event_state @pgTable(name: \\"tennis_event_states\\") { tennis_set: String } -type Tennis_return_stat @pgTable(name: \\"tennis_return_stats\\") { +type TennisReturnStat @pgTable(name: \\"tennis_return_stats\\") { id: Int! @unique break_points_converted: String break_points_converted_pct: String @@ -1410,7 +1410,7 @@ type Tennis_return_stat @pgTable(name: \\"tennis_return_stats\\") { second_service_return_points_won_pct: String } -type Tennis_service_stat @pgTable(name: \\"tennis_service_stats\\") { +type TennisServiceStat @pgTable(name: \\"tennis_service_stats\\") { id: Int! @unique aces: String break_points_played: String @@ -1429,7 +1429,7 @@ type Tennis_service_stat @pgTable(name: \\"tennis_service_stats\\") { services_played: String } -type Wagering_moneyline @pgTable(name: \\"wagering_moneylines\\") { +type WageringMoneyline @pgTable(name: \\"wagering_moneylines\\") { id: Int! @unique bookmaker_id: Bookmaker! comment: String @@ -1444,7 +1444,7 @@ type Wagering_moneyline @pgTable(name: \\"wagering_moneylines\\") { vigorish: String } -type Wagering_odds_line @pgTable(name: \\"wagering_odds_lines\\") { +type WageringOddsLine @pgTable(name: \\"wagering_odds_lines\\") { id: Int! @unique bookmaker_id: Bookmaker! comment: String @@ -1460,7 +1460,7 @@ type Wagering_odds_line @pgTable(name: \\"wagering_odds_lines\\") { team_id: Team! } -type Wagering_runline @pgTable(name: \\"wagering_runlines\\") { +type WageringRunline @pgTable(name: \\"wagering_runlines\\") { id: Int! @unique bookmaker_id: Bookmaker! comment: String @@ -1476,7 +1476,7 @@ type Wagering_runline @pgTable(name: \\"wagering_runlines\\") { vigorish: String } -type Wagering_straight_spread_line @pgTable(name: \\"wagering_straight_spread_lines\\") { +type WageringStraightSpreadLine @pgTable(name: \\"wagering_straight_spread_lines\\") { id: Int! @unique bookmaker_id: Bookmaker! comment: String @@ -1491,7 +1491,7 @@ type Wagering_straight_spread_line @pgTable(name: \\"wagering_straight_spread_li vigorish: String } -type Wagering_total_score_line @pgTable(name: \\"wagering_total_score_lines\\") { +type WageringTotalScoreLine @pgTable(name: \\"wagering_total_score_lines\\") { id: Int! @unique bookmaker_id: Bookmaker! comment: String @@ -1508,7 +1508,7 @@ type Wagering_total_score_line @pgTable(name: \\"wagering_total_score_lines\\") vigorish: String } -type Weather_condition @pgTable(name: \\"weather_conditions\\") { +type WeatherCondition @pgTable(name: \\"weather_conditions\\") { id: Int! @unique clouds: String event_id: Event! @@ -1552,21 +1552,21 @@ type Affiliation @db(name: \\"affiliations\\") @indexes(value: [ ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"affiliations_id_seq\\", initialValue: 1, allocationSize: 1) affiliation_key: String! - affiliation_phases: [Affiliation_phase] - affiliation_phases: [Affiliation_phase] + affiliation_phases: [AffiliationPhase] + affiliation_phases: [AffiliationPhase] affiliation_type: String - affiliations_documents: [Affiliations_document] - affiliations_events: [Affiliations_event] - affiliations_media: [Affiliations_media] + affiliations_documents: [AffiliationsDocument] + affiliations_events: [AffiliationsEvent] + affiliations_media: [AffiliationsMedia] positions: [Position] publisher_id: Publisher! seasons: [Season] - standing_subgroups: [Standing_subgroup] + standing_subgroups: [StandingSubgroup] standings: [Standing] - team_phases: [Team_phase] + team_phases: [TeamPhase] } -type Affiliation_phase @db(name: \\"affiliation_phases\\") { +type AffiliationPhase @db(name: \\"affiliation_phases\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"affiliation_phases_id_seq\\", initialValue: 1, allocationSize: 1) affiliation_id: Affiliation! ancestor_affiliation_id: Affiliation @@ -1576,27 +1576,27 @@ type Affiliation_phase @db(name: \\"affiliation_phases\\") { start_season_id: Season } -type Affiliations_document @db(name: \\"affiliations_documents\\") @linkTable { +type AffiliationsDocument @db(name: \\"affiliations_documents\\") @linkTable { affiliation_id: Affiliation! document_id: Document! } -type Affiliations_event @db(name: \\"affiliations_events\\") @linkTable { +type AffiliationsEvent @db(name: \\"affiliations_events\\") @linkTable { affiliation_id: Affiliation! event_id: Event! } -type Affiliations_media @db(name: \\"affiliations_media\\") @linkTable { +type AffiliationsMedia @db(name: \\"affiliations_media\\") @linkTable { affiliation_id: Affiliation! media_id: Media! } -type American_football_action_participant @db(name: \\"american_football_action_participants\\") @indexes(value: [ +type AmericanFootballActionParticipant @db(name: \\"american_football_action_participants\\") @indexes(value: [ {name: \\"idx_american_football_action_participants_1\\", fields: [\\"participant_role\\"]}, {name: \\"idx_american_football_action_participants_2\\", fields: [\\"score_type\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"american_football_action_participants_id_seq\\", initialValue: 1, allocationSize: 1) - american_football_action_play_id: American_football_action_play! + american_football_action_play_id: AmericanFootballActionPlay! field_line: Int participant_role: String! person_id: Person! @@ -1606,14 +1606,14 @@ type American_football_action_participant @db(name: \\"american_football_action_ yards_gained: Int } -type American_football_action_play @db(name: \\"american_football_action_plays\\") @indexes(value: [ +type AmericanFootballActionPlay @db(name: \\"american_football_action_plays\\") @indexes(value: [ {name: \\"idx_american_football_action_plays_1\\", fields: [\\"play_type\\"]}, {name: \\"idx_american_football_action_plays_2\\", fields: [\\"score_attempt_type\\"]}, {name: \\"idx_american_football_action_plays_3\\", fields: [\\"drive_result\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"american_football_action_plays_id_seq\\", initialValue: 1, allocationSize: 1) - american_football_action_participants: [American_football_action_participant] - american_football_event_state_id: American_football_event_state! + american_football_action_participants: [AmericanFootballActionParticipant] + american_football_event_state_id: AmericanFootballEventState! comment: String drive_result: String play_type: String @@ -1621,7 +1621,7 @@ type American_football_action_play @db(name: \\"american_football_action_plays\\ score_attempt_type: String } -type American_football_defensive_stat @db(name: \\"american_football_defensive_stats\\") { +type AmericanFootballDefensiveStat @db(name: \\"american_football_defensive_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"american_football_defensive_stats_id_seq\\", initialValue: 1, allocationSize: 1) interceptions_average: String interceptions_longest: String @@ -1637,7 +1637,7 @@ type American_football_defensive_stat @db(name: \\"american_football_defensive_s tackles_total: String } -type American_football_down_progress_stat @db(name: \\"american_football_down_progress_stats\\") { +type AmericanFootballDownProgressStat @db(name: \\"american_football_down_progress_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"american_football_down_progress_stats_id_seq\\", initialValue: 1, allocationSize: 1) conversions_fourth_down: String conversions_fourth_down_attempts: String @@ -1651,11 +1651,11 @@ type American_football_down_progress_stat @db(name: \\"american_football_down_pr first_downs_total: String } -type American_football_event_state @db(name: \\"american_football_event_states\\") @indexes(value: [ +type AmericanFootballEventState @db(name: \\"american_football_event_states\\") @indexes(value: [ {name: \\"idx_american_football_event_states_1\\", fields: [\\"current_state\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"american_football_event_states_id_seq\\", initialValue: 1, allocationSize: 1) - american_football_action_plays: [American_football_action_play] + american_football_action_plays: [AmericanFootballActionPlay] clock_state: String context: String current_state: Int @@ -1671,7 +1671,7 @@ type American_football_event_state @db(name: \\"american_football_event_states\\ team_in_possession_id: Team } -type American_football_fumbles_stat @db(name: \\"american_football_fumbles_stats\\") { +type AmericanFootballFumblesStat @db(name: \\"american_football_fumbles_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"american_football_fumbles_stats_id_seq\\", initialValue: 1, allocationSize: 1) fumbles_committed: String fumbles_forced: String @@ -1688,7 +1688,7 @@ type American_football_fumbles_stat @db(name: \\"american_football_fumbles_stats fumbles_yards_gained: String } -type American_football_offensive_stat @db(name: \\"american_football_offensive_stats\\") { +type AmericanFootballOffensiveStat @db(name: \\"american_football_offensive_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"american_football_offensive_stats_id_seq\\", initialValue: 1, allocationSize: 1) offensive_plays_average_yards_per: String offensive_plays_number: String @@ -1697,7 +1697,7 @@ type American_football_offensive_stat @db(name: \\"american_football_offensive_s turnovers_giveaway: String } -type American_football_passing_stat @db(name: \\"american_football_passing_stats\\") { +type AmericanFootballPassingStat @db(name: \\"american_football_passing_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"american_football_passing_stats_id_seq\\", initialValue: 1, allocationSize: 1) passer_rating: String passes_attempts: String @@ -1720,14 +1720,14 @@ type American_football_passing_stat @db(name: \\"american_football_passing_stats receptions_yards: String } -type American_football_penalties_stat @db(name: \\"american_football_penalties_stats\\") { +type AmericanFootballPenaltiesStat @db(name: \\"american_football_penalties_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"american_football_penalties_stats_id_seq\\", initialValue: 1, allocationSize: 1) penalties_total: String penalty_first_downs: String penalty_yards: String } -type American_football_rushing_stat @db(name: \\"american_football_rushing_stats\\") { +type AmericanFootballRushingStat @db(name: \\"american_football_rushing_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"american_football_rushing_stats_id_seq\\", initialValue: 1, allocationSize: 1) rushes_attempts: String rushes_first_down: String @@ -1737,13 +1737,13 @@ type American_football_rushing_stat @db(name: \\"american_football_rushing_stats rushing_average_yards_per: String } -type American_football_sacks_against_stat @db(name: \\"american_football_sacks_against_stats\\") { +type AmericanFootballSacksAgainstStat @db(name: \\"american_football_sacks_against_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"american_football_sacks_against_stats_id_seq\\", initialValue: 1, allocationSize: 1) sacks_against_total: String sacks_against_yards: String } -type American_football_scoring_stat @db(name: \\"american_football_scoring_stats\\") { +type AmericanFootballScoringStat @db(name: \\"american_football_scoring_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"american_football_scoring_stats_id_seq\\", initialValue: 1, allocationSize: 1) extra_points_attempts: String extra_points_blocked: String @@ -1764,7 +1764,7 @@ type American_football_scoring_stat @db(name: \\"american_football_scoring_stats two_point_conversions_made: String } -type American_football_special_teams_stat @db(name: \\"american_football_special_teams_stats\\") { +type AmericanFootballSpecialTeamsStat @db(name: \\"american_football_special_teams_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"american_football_special_teams_stats_id_seq\\", initialValue: 1, allocationSize: 1) fair_catches: String punts_average: String @@ -1797,9 +1797,9 @@ type American_football_special_teams_stat @db(name: \\"american_football_special touchbacks_total_percentage: String } -type Baseball_action_contact_detail @db(name: \\"baseball_action_contact_details\\") { +type BaseballActionContactDetail @db(name: \\"baseball_action_contact_details\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"baseball_action_contact_details_id_seq\\", initialValue: 1, allocationSize: 1) - baseball_action_pitch_id: Baseball_action_pitch! + baseball_action_pitch_id: BaseballActionPitch! comment: String location: String strength: String @@ -1808,15 +1808,15 @@ type Baseball_action_contact_detail @db(name: \\"baseball_action_contact_details velocity: Int } -type Baseball_action_pitch @db(name: \\"baseball_action_pitches\\") @indexes(value: [ +type BaseballActionPitch @db(name: \\"baseball_action_pitches\\") @indexes(value: [ {name: \\"idx_baseball_action_pitches_1\\", fields: [\\"umpire_call\\"]}, {name: \\"idx_baseball_action_pitches_2\\", fields: [\\"pitch_type\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"baseball_action_pitches_id_seq\\", initialValue: 1, allocationSize: 1) ball_type: String - baseball_action_contact_details: [Baseball_action_contact_detail] - baseball_action_play_id: Baseball_action_play! - baseball_defensive_group_id: Baseball_defensive_group + baseball_action_contact_details: [BaseballActionContactDetail] + baseball_action_play_id: BaseballActionPlay! + baseball_defensive_group_id: BaseballDefensiveGroup comment: String pitch_location: String pitch_type: String @@ -1828,13 +1828,13 @@ type Baseball_action_pitch @db(name: \\"baseball_action_pitches\\") @indexes(val umpire_call: String } -type Baseball_action_play @db(name: \\"baseball_action_plays\\") @indexes(value: [ +type BaseballActionPlay @db(name: \\"baseball_action_plays\\") @indexes(value: [ {name: \\"idx_baseball_action_plays_1\\", fields: [\\"play_type\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"baseball_action_plays_id_seq\\", initialValue: 1, allocationSize: 1) - baseball_action_pitches: [Baseball_action_pitch] + baseball_action_pitches: [BaseballActionPitch] baseball_defensive_group_id: Int - baseball_event_state_id: Baseball_event_state! + baseball_event_state_id: BaseballEventState! comment: String earned_runs_scored: String notation: String @@ -1848,9 +1848,9 @@ type Baseball_action_play @db(name: \\"baseball_action_plays\\") @indexes(value: runs_scored: Int } -type Baseball_action_substitution @db(name: \\"baseball_action_substitutions\\") { +type BaseballActionSubstitution @db(name: \\"baseball_action_substitutions\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"baseball_action_substitutions_id_seq\\", initialValue: 1, allocationSize: 1) - baseball_event_state_id: Baseball_event_state! + baseball_event_state_id: BaseballEventState! comment: String person_original_id: Person person_original_lineup_slot: Int @@ -1863,20 +1863,20 @@ type Baseball_action_substitution @db(name: \\"baseball_action_substitutions\\") substitution_reason: String } -type Baseball_defensive_group @db(name: \\"baseball_defensive_group\\") @linkTable { +type BaseballDefensiveGroup @db(name: \\"baseball_defensive_group\\") @linkTable { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"baseball_defensive_group_id_seq\\", initialValue: 1, allocationSize: 1) - baseball_action_pitches: [Baseball_action_pitch] - baseball_defensive_players: [Baseball_defensive_player] + baseball_action_pitches: [BaseballActionPitch] + baseball_defensive_players: [BaseballDefensivePlayer] } -type Baseball_defensive_player @db(name: \\"baseball_defensive_players\\") { +type BaseballDefensivePlayer @db(name: \\"baseball_defensive_players\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"baseball_defensive_players_id_seq\\", initialValue: 1, allocationSize: 1) - baseball_defensive_group_id: Baseball_defensive_group! + baseball_defensive_group_id: BaseballDefensiveGroup! player_id: Person! position_id: Position! } -type Baseball_defensive_stat @db(name: \\"baseball_defensive_stats\\") { +type BaseballDefensiveStat @db(name: \\"baseball_defensive_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"baseball_defensive_stats_id_seq\\", initialValue: 1, allocationSize: 1) assists: Int defensive_average: Float @@ -1889,14 +1889,14 @@ type Baseball_defensive_stat @db(name: \\"baseball_defensive_stats\\") { triple_plays: Int } -type Baseball_event_state @db(name: \\"baseball_event_states\\") @indexes(value: [ +type BaseballEventState @db(name: \\"baseball_event_states\\") @indexes(value: [ {name: \\"idx_baseball_event_states_1\\", fields: [\\"current_state\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"baseball_event_states_id_seq\\", initialValue: 1, allocationSize: 1) at_bat_number: Int balls: Int - baseball_action_plays: [Baseball_action_play] - baseball_action_substitutions: [Baseball_action_substitution] + baseball_action_plays: [BaseballActionPlay] + baseball_action_substitutions: [BaseballActionSubstitution] batter_id: Person batter_side: String context: String @@ -1917,7 +1917,7 @@ type Baseball_event_state @db(name: \\"baseball_event_states\\") @indexes(value: strikes: Int } -type Baseball_offensive_stat @db(name: \\"baseball_offensive_stats\\") { +type BaseballOffensiveStat @db(name: \\"baseball_offensive_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"baseball_offensive_stats_id_seq\\", initialValue: 1, allocationSize: 1) at_bats: Int at_bats_per_home_run: Float @@ -1954,7 +1954,7 @@ type Baseball_offensive_stat @db(name: \\"baseball_offensive_stats\\") { triples: Int } -type Baseball_pitching_stat @db(name: \\"baseball_pitching_stats\\") { +type BaseballPitchingStat @db(name: \\"baseball_pitching_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"baseball_pitching_stats_id_seq\\", initialValue: 1, allocationSize: 1) balks: Int bases_on_balls: Int @@ -1987,7 +1987,7 @@ type Baseball_pitching_stat @db(name: \\"baseball_pitching_stats\\") { wins: Int } -type Basketball_defensive_stat @db(name: \\"basketball_defensive_stats\\") { +type BasketballDefensiveStat @db(name: \\"basketball_defensive_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"basketball_defensive_stats_id_seq\\", initialValue: 1, allocationSize: 1) blocks_per_game: String blocks_total: String @@ -1995,7 +1995,7 @@ type Basketball_defensive_stat @db(name: \\"basketball_defensive_stats\\") { steals_total: String } -type Basketball_event_state @db(name: \\"basketball_event_states\\") { +type BasketballEventState @db(name: \\"basketball_event_states\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"basketball_event_states_id_seq\\", initialValue: 1, allocationSize: 1) context: String current_state: Int @@ -2006,7 +2006,7 @@ type Basketball_event_state @db(name: \\"basketball_event_states\\") { sequence_number: Int } -type Basketball_offensive_stat @db(name: \\"basketball_offensive_stats\\") { +type BasketballOffensiveStat @db(name: \\"basketball_offensive_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"basketball_offensive_stats_id_seq\\", initialValue: 1, allocationSize: 1) assists_per_game: String assists_total: String @@ -2036,7 +2036,7 @@ type Basketball_offensive_stat @db(name: \\"basketball_offensive_stats\\") { turnovers_total: String } -type Basketball_rebounding_stat @db(name: \\"basketball_rebounding_stats\\") { +type BasketballReboundingStat @db(name: \\"basketball_rebounding_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"basketball_rebounding_stats_id_seq\\", initialValue: 1, allocationSize: 1) rebounds_defensive: String rebounds_offensive: String @@ -2048,7 +2048,7 @@ type Basketball_rebounding_stat @db(name: \\"basketball_rebounding_stats\\") { team_rebounds_total: String } -type Basketball_team_stat @db(name: \\"basketball_team_stats\\") { +type BasketballTeamStat @db(name: \\"basketball_team_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"basketball_team_stats_id_seq\\", initialValue: 1, allocationSize: 1) fouls_total: String largest_lead: String @@ -2061,14 +2061,14 @@ type Bookmaker @db(name: \\"bookmakers\\") { bookmaker_key: String location_id: Location publisher_id: Publisher! - wagering_moneylines: [Wagering_moneyline] - wagering_odds_lines: [Wagering_odds_line] - wagering_runlines: [Wagering_runline] - wagering_straight_spread_lines: [Wagering_straight_spread_line] - wagering_total_score_lines: [Wagering_total_score_line] + wagering_moneylines: [WageringMoneyline] + wagering_odds_lines: [WageringOddsLine] + wagering_runlines: [WageringRunline] + wagering_straight_spread_lines: [WageringStraightSpreadLine] + wagering_total_score_lines: [WageringTotalScoreLine] } -type Core_person_stat @db(name: \\"core_person_stats\\") { +type CorePersonStat @db(name: \\"core_person_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"core_person_stats_id_seq\\", initialValue: 1, allocationSize: 1) events_played: Int events_started: Int @@ -2078,7 +2078,7 @@ type Core_person_stat @db(name: \\"core_person_stats\\") { time_played_total: String } -type Core_stat @db(name: \\"core_stats\\") { +type CoreStat @db(name: \\"core_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"core_stats_id_seq\\", initialValue: 1, allocationSize: 1) score: String score_attempts: String @@ -2088,13 +2088,13 @@ type Core_stat @db(name: \\"core_stats\\") { score_percentage_opposing: String } -type Db_info @db(name: \\"db_info\\") @indexes(value: [ +type DbInfo @db(name: \\"db_info\\") @indexes(value: [ {name: \\"idx_db_info_1\\", fields: [\\"version\\"]} ]) { version: String! @default(value: \\"16\\") } -type Display_name @db(name: \\"display_names\\") { +type DisplayName @db(name: \\"display_names\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"display_names_id_seq\\", initialValue: 1, allocationSize: 1) abbreviation: String alias: String @@ -2117,84 +2117,84 @@ type Document @db(name: \\"documents\\") @indexes(value: [ {name: \\"idx_documents_5\\", fields: [\\"revision_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"documents_id_seq\\", initialValue: 1, allocationSize: 1) - affiliations_documents: [Affiliations_document] + affiliations_documents: [AffiliationsDocument] date_time: DateTime db_loading_date_time: DateTime doc_id: String! - document_contents: [Document_content] - document_fixture_id: Document_fixture! - document_fixtures_events: [Document_fixtures_event] - document_package_entry: [Document_package_entry] - documents_media: [Documents_media] - events_documents: [Events_document] + document_contents: [DocumentContent] + document_fixture_id: DocumentFixture! + document_fixtures_events: [DocumentFixturesEvent] + document_package_entry: [DocumentPackageEntry] + documents_media: [DocumentsMedia] + events_documents: [EventsDocument] language: String - latest_revisions: [Latest_revision] - persons_documents: [Persons_document] + latest_revisions: [LatestRevision] + persons_documents: [PersonsDocument] priority: String publisher_id: Publisher! revision_id: String source_id: Publisher stats_coverage: String - teams_documents: [Teams_document] + teams_documents: [TeamsDocument] title: String } -type Document_class @db(name: \\"document_classes\\") @indexes(value: [ +type DocumentClass @db(name: \\"document_classes\\") @indexes(value: [ {name: \\"idx_document_classes_1\\", fields: [\\"name\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"document_classes_id_seq\\", initialValue: 1, allocationSize: 1) - document_fixtures: [Document_fixture] + document_fixtures: [DocumentFixture] name: String } -type Document_content @db(name: \\"document_contents\\") { +type DocumentContent @db(name: \\"document_contents\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"document_contents_id_seq\\", initialValue: 1, allocationSize: 1) abstract: String document_id: Document! sportsml: String } -type Document_fixture @db(name: \\"document_fixtures\\") @indexes(value: [ +type DocumentFixture @db(name: \\"document_fixtures\\") @indexes(value: [ {name: \\"idx_document_fixtures_1\\", fields: [\\"fixture_key\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"document_fixtures_id_seq\\", initialValue: 1, allocationSize: 1) - document_class_id: Document_class! - document_fixtures_events: [Document_fixtures_event] + document_class_id: DocumentClass! + document_fixtures_events: [DocumentFixturesEvent] documents: [Document] fixture_key: String name: String publisher_id: Publisher! } -type Document_fixtures_event @db(name: \\"document_fixtures_events\\") { +type DocumentFixturesEvent @db(name: \\"document_fixtures_events\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"document_fixtures_events_id_seq\\", initialValue: 1, allocationSize: 1) - document_fixture_id: Document_fixture! + document_fixture_id: DocumentFixture! event_id: Event! last_update: DateTime latest_document_id: Document! } -type Document_package @db(name: \\"document_packages\\") { +type DocumentPackage @db(name: \\"document_packages\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"document_packages_id_seq\\", initialValue: 1, allocationSize: 1) date_time: DateTime - document_package_entry: [Document_package_entry] + document_package_entry: [DocumentPackageEntry] package_key: String package_name: String } -type Document_package_entry @db(name: \\"document_package_entry\\") { +type DocumentPackageEntry @db(name: \\"document_package_entry\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"document_package_entry_id_seq\\", initialValue: 1, allocationSize: 1) document_id: Document! - document_package_id: Document_package! + document_package_id: DocumentPackage! headline: String rank: String short_headline: String } -type Documents_media @db(name: \\"documents_media\\") { +type DocumentsMedia @db(name: \\"documents_media\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"documents_media_id_seq\\", initialValue: 1, allocationSize: 1) document_id: Document! - media_caption_id: Media_caption! + media_caption_id: MediaCaption! media_id: Media! } @@ -2202,53 +2202,53 @@ type Event @db(name: \\"events\\") @indexes(value: [ {name: \\"idx_events_1\\", fields: [\\"event_key\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"events_id_seq\\", initialValue: 1, allocationSize: 1) - affiliations_events: [Affiliations_event] - american_football_event_states: [American_football_event_state] + affiliations_events: [AffiliationsEvent] + american_football_event_states: [AmericanFootballEventState] attendance: String - baseball_event_states: [Baseball_event_state] - basketball_event_states: [Basketball_event_state] - document_fixtures_events: [Document_fixtures_event] + baseball_event_states: [BaseballEventState] + basketball_event_states: [BasketballEventState] + document_fixtures_events: [DocumentFixturesEvent] duration: String event_key: String! event_status: String - events_documents: [Events_document] - events_media: [Events_media] - events_sub_seasons: [Events_sub_season] - ice_hockey_event_states: [Ice_hockey_event_state] + events_documents: [EventsDocument] + events_media: [EventsMedia] + events_sub_seasons: [EventsSubSeason] + ice_hockey_event_states: [IceHockeyEventState] last_update: DateTime - motor_racing_event_states: [Motor_racing_event_state] - participants_events: [Participants_event] - person_event_metadata: [Person_event_metadatum] + motor_racing_event_states: [MotorRacingEventState] + participants_events: [ParticipantsEvent] + person_event_metadata: [PersonEventMetadatum] publisher_id: Publisher! site_alignment: String site_id: Site - soccer_event_states: [Soccer_event_state] + soccer_event_states: [SoccerEventState] start_date_time: DateTime - tennis_event_states: [Tennis_event_state] - wagering_moneylines: [Wagering_moneyline] - wagering_odds_lines: [Wagering_odds_line] - wagering_runlines: [Wagering_runline] - wagering_straight_spread_lines: [Wagering_straight_spread_line] - wagering_total_score_lines: [Wagering_total_score_line] - weather_conditions: [Weather_condition] + tennis_event_states: [TennisEventState] + wagering_moneylines: [WageringMoneyline] + wagering_odds_lines: [WageringOddsLine] + wagering_runlines: [WageringRunline] + wagering_straight_spread_lines: [WageringStraightSpreadLine] + wagering_total_score_lines: [WageringTotalScoreLine] + weather_conditions: [WeatherCondition] } -type Events_document @db(name: \\"events_documents\\") @linkTable { +type EventsDocument @db(name: \\"events_documents\\") @linkTable { document_id: Document! event_id: Event! } -type Events_media @db(name: \\"events_media\\") @linkTable { +type EventsMedia @db(name: \\"events_media\\") @linkTable { event_id: Event! media_id: Media! } -type Events_sub_season @db(name: \\"events_sub_seasons\\") @linkTable { +type EventsSubSeason @db(name: \\"events_sub_seasons\\") @linkTable { event_id: Event! - sub_season_id: Sub_season! + sub_season_id: SubSeason! } -type Ice_hockey_action_participant @db(name: \\"ice_hockey_action_participants\\") { +type IceHockeyActionParticipant @db(name: \\"ice_hockey_action_participants\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"ice_hockey_action_participants_id_seq\\", initialValue: 1, allocationSize: 1) ice_hockey_action_play_id: Int! participant_role: String! @@ -2256,7 +2256,7 @@ type Ice_hockey_action_participant @db(name: \\"ice_hockey_action_participants\\ point_credit: Int } -type Ice_hockey_action_play @db(name: \\"ice_hockey_action_plays\\") { +type IceHockeyActionPlay @db(name: \\"ice_hockey_action_plays\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"ice_hockey_action_plays_id_seq\\", initialValue: 1, allocationSize: 1) comment: String ice_hockey_event_state_id: Int! @@ -2265,7 +2265,7 @@ type Ice_hockey_action_play @db(name: \\"ice_hockey_action_plays\\") { score_attempt_type: String } -type Ice_hockey_defensive_stat @db(name: \\"ice_hockey_defensive_stats\\") { +type IceHockeyDefensiveStat @db(name: \\"ice_hockey_defensive_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"ice_hockey_defensive_stats_id_seq\\", initialValue: 1, allocationSize: 1) goals_against_average: String goals_empty_net_allowed: String @@ -2287,7 +2287,7 @@ type Ice_hockey_defensive_stat @db(name: \\"ice_hockey_defensive_stats\\") { takeaways: String } -type Ice_hockey_event_state @db(name: \\"ice_hockey_event_states\\") { +type IceHockeyEventState @db(name: \\"ice_hockey_event_states\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"ice_hockey_event_states_id_seq\\", initialValue: 1, allocationSize: 1) context: String current_state: Int @@ -2298,7 +2298,7 @@ type Ice_hockey_event_state @db(name: \\"ice_hockey_event_states\\") { sequence_number: Int } -type Ice_hockey_offensive_stat @db(name: \\"ice_hockey_offensive_stats\\") { +type IceHockeyOffensiveStat @db(name: \\"ice_hockey_offensive_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"ice_hockey_offensive_stats_id_seq\\", initialValue: 1, allocationSize: 1) assists: String faceoff_losses: String @@ -2324,12 +2324,12 @@ type Ice_hockey_offensive_stat @db(name: \\"ice_hockey_offensive_stats\\") { shots_penalty_shot_taken: String } -type Ice_hockey_player_stat @db(name: \\"ice_hockey_player_stats\\") { +type IceHockeyPlayerStat @db(name: \\"ice_hockey_player_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"ice_hockey_player_stats_id_seq\\", initialValue: 1, allocationSize: 1) plus_minus: String } -type Injury_phase @db(name: \\"injury_phases\\") @indexes(value: [ +type InjuryPhase @db(name: \\"injury_phases\\") @indexes(value: [ {name: \\"idx_injury_phases_2\\", fields: [\\"injury_status\\"]}, {name: \\"idx_injury_phases_3\\", fields: [\\"start_date_time\\"]}, {name: \\"idx_injury_phases_4\\", fields: [\\"end_date_time\\"]} @@ -2347,23 +2347,23 @@ type Injury_phase @db(name: \\"injury_phases\\") @indexes(value: [ start_date_time: DateTime } -type Key_alias @db(name: \\"key_aliases\\") @indexes(value: [ +type KeyAlias @db(name: \\"key_aliases\\") @indexes(value: [ {name: \\"idx_key_aliases_2\\", fields: [\\"key_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"key_aliases_id_seq\\", initialValue: 1, allocationSize: 1) key_id: Int! - key_root_id: Key_root! + key_root_id: KeyRoot! } -type Key_root @db(name: \\"key_roots\\") @indexes(value: [ +type KeyRoot @db(name: \\"key_roots\\") @indexes(value: [ {name: \\"idx_key_aliases_1\\", fields: [\\"key_type\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"key_roots_id_seq\\", initialValue: 1, allocationSize: 1) - key_aliases: [Key_alias] + key_aliases: [KeyAlias] key_type: String } -type Latest_revision @db(name: \\"latest_revisions\\") @indexes(value: [ +type LatestRevision @db(name: \\"latest_revisions\\") @indexes(value: [ {name: \\"idx_latest_revisions_1\\", fields: [\\"revision_id\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"latest_revisions_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2391,37 +2391,37 @@ type Location @db(name: \\"locations\\") @indexes(value: [ type Media @db(name: \\"media\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"media_id_seq\\", initialValue: 1, allocationSize: 1) - affiliations_media: [Affiliations_media] + affiliations_media: [AffiliationsMedia] creation_location_id: Location! credit_id: Person! date_time: String db_loading_date_time: DateTime - documents_media: [Documents_media] - events_media: [Events_media] - media_captions: [Media_caption] - media_contents: [Media_content] - media_keywords: [Media_keyword] + documents_media: [DocumentsMedia] + events_media: [EventsMedia] + media_captions: [MediaCaption] + media_contents: [MediaContent] + media_keywords: [MediaKeyword] media_type: String object_id: Int - persons_media: [Persons_media] + persons_media: [PersonsMedia] publisher_id: Publisher! revision_id: Int source_id: Int - teams_media: [Teams_media] + teams_media: [TeamsMedia] } -type Media_caption @db(name: \\"media_captions\\") { +type MediaCaption @db(name: \\"media_captions\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"media_captions_id_seq\\", initialValue: 1, allocationSize: 1) caption: String caption_author_id: Person! caption_size: String caption_type: String - documents_media: [Documents_media] + documents_media: [DocumentsMedia] language: String media_id: Media! } -type Media_content @db(name: \\"media_contents\\") { +type MediaContent @db(name: \\"media_contents\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"media_contents_id_seq\\", initialValue: 1, allocationSize: 1) duration: String file_size: String @@ -2434,13 +2434,13 @@ type Media_content @db(name: \\"media_contents\\") { width: String } -type Media_keyword @db(name: \\"media_keywords\\") { +type MediaKeyword @db(name: \\"media_keywords\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"media_keywords_id_seq\\", initialValue: 1, allocationSize: 1) keyword: String media_id: Media! } -type Motor_racing_event_state @db(name: \\"motor_racing_event_states\\") { +type MotorRacingEventState @db(name: \\"motor_racing_event_states\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"motor_racing_event_states_id_seq\\", initialValue: 1, allocationSize: 1) context: String current_state: Int @@ -2452,7 +2452,7 @@ type Motor_racing_event_state @db(name: \\"motor_racing_event_states\\") { time_elapsed: String } -type Motor_racing_qualifying_stat @db(name: \\"motor_racing_qualifying_stats\\") { +type MotorRacingQualifyingStat @db(name: \\"motor_racing_qualifying_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"motor_racing_qualifying_stats_id_seq\\", initialValue: 1, allocationSize: 1) grid: String pole_position: String @@ -2463,7 +2463,7 @@ type Motor_racing_qualifying_stat @db(name: \\"motor_racing_qualifying_stats\\") qualifying_time: String } -type Motor_racing_race_stat @db(name: \\"motor_racing_race_stats\\") { +type MotorRacingRaceStat @db(name: \\"motor_racing_race_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"motor_racing_race_stats_id_seq\\", initialValue: 1, allocationSize: 1) bonus: String distance_completed: String @@ -2493,7 +2493,7 @@ type Motor_racing_race_stat @db(name: \\"motor_racing_race_stats\\") { wins: String } -type Outcome_total @db(name: \\"outcome_totals\\") { +type OutcomeTotal @db(name: \\"outcome_totals\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"outcome_totals_id_seq\\", initialValue: 1, allocationSize: 1) losses: String outcome_holder_id: Int @@ -2503,7 +2503,7 @@ type Outcome_total @db(name: \\"outcome_totals\\") { points_scored_for: String rank: String standing_points: String - standing_subgroup_id: Standing_subgroup! + standing_subgroup_id: StandingSubgroup! streak_duration: String streak_end: DateTime streak_start: DateTime @@ -2515,7 +2515,7 @@ type Outcome_total @db(name: \\"outcome_totals\\") { wins: String } -type Participants_event @db(name: \\"participants_events\\") @indexes(value: [ +type ParticipantsEvent @db(name: \\"participants_events\\") @indexes(value: [ {name: \\"idx_participants_events_1\\", fields: [\\"participant_type\\"]}, {name: \\"idx_participants_events_2\\", fields: [\\"participant_id\\"]}, {name: \\"idx_participants_events_3\\", fields: [\\"alignment\\"]}, @@ -2534,44 +2534,44 @@ type Participants_event @db(name: \\"participants_events\\") @indexes(value: [ type Period @db(name: \\"periods\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"periods_id_seq\\", initialValue: 1, allocationSize: 1) - participant_event_id: Participants_event! + participant_event_id: ParticipantsEvent! period_value: String score: String - sub_periods: [Sub_period] + sub_periods: [SubPeriod] } type Person @db(name: \\"persons\\") @indexes(value: [ {name: \\"idx_persons_1\\", fields: [\\"person_key\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"persons_id_seq\\", initialValue: 1, allocationSize: 1) - american_football_action_participants: [American_football_action_participant] - baseball_action_substitutions: [Baseball_action_substitution] - baseball_action_substitutions: [Baseball_action_substitution] - baseball_defensive_players: [Baseball_defensive_player] - baseball_event_states: [Baseball_event_state] - baseball_event_states: [Baseball_event_state] - baseball_event_states: [Baseball_event_state] - baseball_event_states: [Baseball_event_state] - baseball_event_states: [Baseball_event_state] + american_football_action_participants: [AmericanFootballActionParticipant] + baseball_action_substitutions: [BaseballActionSubstitution] + baseball_action_substitutions: [BaseballActionSubstitution] + baseball_defensive_players: [BaseballDefensivePlayer] + baseball_event_states: [BaseballEventState] + baseball_event_states: [BaseballEventState] + baseball_event_states: [BaseballEventState] + baseball_event_states: [BaseballEventState] + baseball_event_states: [BaseballEventState] birth_date: String birth_location_id: Location death_date: String death_location_id: Location gender: String hometown_location_id: Location - injury_phases: [Injury_phase] + injury_phases: [InjuryPhase] media: [Media] - media_captions: [Media_caption] - person_event_metadata: [Person_event_metadatum] + media_captions: [MediaCaption] + person_event_metadata: [PersonEventMetadatum] person_key: String! - person_phases: [Person_phase] - persons_documents: [Persons_document] - persons_media: [Persons_media] + person_phases: [PersonPhase] + persons_documents: [PersonsDocument] + persons_media: [PersonsMedia] publisher_id: Publisher! residence_location_id: Location } -type Person_event_metadatum @db(name: \\"person_event_metadata\\") @indexes(value: [ +type PersonEventMetadatum @db(name: \\"person_event_metadata\\") @indexes(value: [ {name: \\"idx_person_event_metadata_1\\", fields: [\\"status\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"person_event_metadata_id_seq\\", initialValue: 1, allocationSize: 1) @@ -2587,7 +2587,7 @@ type Person_event_metadatum @db(name: \\"person_event_metadata\\") @indexes(valu weight: String } -type Person_phase @db(name: \\"person_phases\\") @indexes(value: [ +type PersonPhase @db(name: \\"person_phases\\") @indexes(value: [ {name: \\"idx_person_phases_1\\", fields: [\\"membership_type\\"]}, {name: \\"idx_person_phases_2\\", fields: [\\"membership_id\\"]}, {name: \\"idx_person_phases_3\\", fields: [\\"phase_status\\"]} @@ -2615,12 +2615,12 @@ type Person_phase @db(name: \\"person_phases\\") @indexes(value: [ weight: String } -type Persons_document @db(name: \\"persons_documents\\") @linkTable { +type PersonsDocument @db(name: \\"persons_documents\\") @linkTable { document_id: Document! person_id: Person! } -type Persons_media @db(name: \\"persons_media\\") @linkTable { +type PersonsMedia @db(name: \\"persons_media\\") @linkTable { media_id: Media! person_id: Person! } @@ -2631,12 +2631,12 @@ type Position @db(name: \\"positions\\") @indexes(value: [ id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"positions_id_seq\\", initialValue: 1, allocationSize: 1) abbreviation: String! affiliation_id: Affiliation! - baseball_action_substitutions: [Baseball_action_substitution] - baseball_action_substitutions: [Baseball_action_substitution] - baseball_defensive_players: [Baseball_defensive_player] - core_person_stats: [Core_person_stat] - person_event_metadata: [Person_event_metadatum] - person_phases: [Person_phase] + baseball_action_substitutions: [BaseballActionSubstitution] + baseball_action_substitutions: [BaseballActionSubstitution] + baseball_defensive_players: [BaseballDefensivePlayer] + core_person_stats: [CorePersonStat] + person_event_metadata: [PersonEventMetadatum] + person_phases: [PersonPhase] } type Publisher @db(name: \\"publishers\\") @indexes(value: [ @@ -2645,7 +2645,7 @@ type Publisher @db(name: \\"publishers\\") @indexes(value: [ id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"publishers_id_seq\\", initialValue: 1, allocationSize: 1) affiliations: [Affiliation] bookmakers: [Bookmaker] - document_fixtures: [Document_fixture] + document_fixtures: [DocumentFixture] documents: [Document] documents: [Document] events: [Event] @@ -2664,30 +2664,30 @@ type Role @db(name: \\"roles\\") @indexes(value: [ ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"roles_id_seq\\", initialValue: 1, allocationSize: 1) comment: String - person_event_metadata: [Person_event_metadatum] - person_phases: [Person_phase] + person_event_metadata: [PersonEventMetadatum] + person_phases: [PersonPhase] role_key: String! role_name: String - team_phases: [Team_phase] + team_phases: [TeamPhase] } type Season @db(name: \\"seasons\\") @indexes(value: [ {name: \\"idx_seasons_1\\", fields: [\\"season_key\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"seasons_id_seq\\", initialValue: 1, allocationSize: 1) - affiliation_phases: [Affiliation_phase] - affiliation_phases: [Affiliation_phase] + affiliation_phases: [AffiliationPhase] + affiliation_phases: [AffiliationPhase] end_date_time: DateTime - injury_phases: [Injury_phase] + injury_phases: [InjuryPhase] league_id: Affiliation! - person_phases: [Person_phase] - person_phases: [Person_phase] + person_phases: [PersonPhase] + person_phases: [PersonPhase] publisher_id: Publisher! season_key: Int! start_date_time: DateTime - sub_seasons: [Sub_season] - team_phases: [Team_phase] - team_phases: [Team_phase] + sub_seasons: [SubSeason] + team_phases: [TeamPhase] + team_phases: [TeamPhase] } type Site @db(name: \\"sites\\") @indexes(value: [ @@ -2701,7 +2701,7 @@ type Site @db(name: \\"sites\\") @indexes(value: [ teams: [Team] } -type Soccer_defensive_stat @db(name: \\"soccer_defensive_stats\\") { +type SoccerDefensiveStat @db(name: \\"soccer_defensive_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"soccer_defensive_stats_id_seq\\", initialValue: 1, allocationSize: 1) catches_punches: String goals_against_average: String @@ -2717,7 +2717,7 @@ type Soccer_defensive_stat @db(name: \\"soccer_defensive_stats\\") { shutouts: String } -type Soccer_event_state @db(name: \\"soccer_event_states\\") { +type SoccerEventState @db(name: \\"soccer_event_states\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"soccer_event_states_id_seq\\", initialValue: 1, allocationSize: 1) context: String current_state: Int @@ -2730,7 +2730,7 @@ type Soccer_event_state @db(name: \\"soccer_event_states\\") { sequence_number: Int } -type Soccer_foul_stat @db(name: \\"soccer_foul_stats\\") { +type SoccerFoulStat @db(name: \\"soccer_foul_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"soccer_foul_stats_id_seq\\", initialValue: 1, allocationSize: 1) caution_points_pending: String caution_points_total: String @@ -2741,7 +2741,7 @@ type Soccer_foul_stat @db(name: \\"soccer_foul_stats\\") { fouls_suffered: String } -type Soccer_offensive_stat @db(name: \\"soccer_offensive_stats\\") { +type SoccerOffensiveStat @db(name: \\"soccer_offensive_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"soccer_offensive_stats_id_seq\\", initialValue: 1, allocationSize: 1) assists_game_tying: String assists_game_winning: String @@ -2782,15 +2782,15 @@ type Standing @db(name: \\"standings\\") { scoping_label: String site_scope: String source: String - standing_subgroups: [Standing_subgroup] + standing_subgroups: [StandingSubgroup] standing_type: String - sub_season_id: Sub_season! + sub_season_id: SubSeason! } -type Standing_subgroup @db(name: \\"standing_subgroups\\") { +type StandingSubgroup @db(name: \\"standing_subgroups\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"standing_subgroups_id_seq\\", initialValue: 1, allocationSize: 1) affiliation_id: Affiliation! - outcome_totals: [Outcome_total] + outcome_totals: [OutcomeTotal] standing_id: Standing! } @@ -2813,20 +2813,20 @@ type Stat @db(name: \\"stats\\") @indexes(value: [ stat_repository_type: String } -type Sub_period @db(name: \\"sub_periods\\") { +type SubPeriod @db(name: \\"sub_periods\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sub_periods_id_seq\\", initialValue: 1, allocationSize: 1) period_id: Period! score: String sub_period_value: String } -type Sub_season @db(name: \\"sub_seasons\\") @indexes(value: [ +type SubSeason @db(name: \\"sub_seasons\\") @indexes(value: [ {name: \\"idx_sub_seasons_1\\", fields: [\\"sub_season_key\\"]}, {name: \\"idx_sub_seasons_2\\", fields: [\\"sub_season_type\\"]} ]) { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"sub_seasons_id_seq\\", initialValue: 1, allocationSize: 1) end_date_time: DateTime - events_sub_seasons: [Events_sub_season] + events_sub_seasons: [EventsSubSeason] season_id: Season! standings: [Standing] start_date_time: DateTime @@ -2836,22 +2836,22 @@ type Sub_season @db(name: \\"sub_seasons\\") @indexes(value: [ type Team @db(name: \\"teams\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"teams_id_seq\\", initialValue: 1, allocationSize: 1) - american_football_event_states: [American_football_event_state] + american_football_event_states: [AmericanFootballEventState] home_site_id: Site - person_event_metadata: [Person_event_metadatum] + person_event_metadata: [PersonEventMetadatum] publisher_id: Publisher! team_key: String! - team_phases: [Team_phase] - teams_documents: [Teams_document] - teams_media: [Teams_media] - wagering_moneylines: [Wagering_moneyline] - wagering_odds_lines: [Wagering_odds_line] - wagering_runlines: [Wagering_runline] - wagering_straight_spread_lines: [Wagering_straight_spread_line] - wagering_total_score_lines: [Wagering_total_score_line] + team_phases: [TeamPhase] + teams_documents: [TeamsDocument] + teams_media: [TeamsMedia] + wagering_moneylines: [WageringMoneyline] + wagering_odds_lines: [WageringOddsLine] + wagering_runlines: [WageringRunline] + wagering_straight_spread_lines: [WageringStraightSpreadLine] + wagering_total_score_lines: [WageringTotalScoreLine] } -type Team_american_football_stat @db(name: \\"team_american_football_stats\\") { +type TeamAmericanFootballStat @db(name: \\"team_american_football_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"team_american_football_stats_id_seq\\", initialValue: 1, allocationSize: 1) average_starting_position: String time_of_possession: String @@ -2860,7 +2860,7 @@ type Team_american_football_stat @db(name: \\"team_american_football_stats\\") { yards_per_attempt: String } -type Team_phase @db(name: \\"team_phases\\") { +type TeamPhase @db(name: \\"team_phases\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"team_phases_id_seq\\", initialValue: 1, allocationSize: 1) affiliation_id: Affiliation! end_date_time: String @@ -2872,24 +2872,24 @@ type Team_phase @db(name: \\"team_phases\\") { team_id: Team! } -type Teams_document @db(name: \\"teams_documents\\") @linkTable { +type TeamsDocument @db(name: \\"teams_documents\\") @linkTable { document_id: Document! team_id: Team! } -type Teams_media @db(name: \\"teams_media\\") @linkTable { +type TeamsMedia @db(name: \\"teams_media\\") @linkTable { media_id: Media! team_id: Team! } -type Tennis_action_point @db(name: \\"tennis_action_points\\") { +type TennisActionPoint @db(name: \\"tennis_action_points\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"tennis_action_points_id_seq\\", initialValue: 1, allocationSize: 1) sequence_number: String sub_period_id: String win_type: String } -type Tennis_action_volley @db(name: \\"tennis_action_volleys\\") { +type TennisActionVolley @db(name: \\"tennis_action_volleys\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"tennis_action_volleys_id_seq\\", initialValue: 1, allocationSize: 1) landing_location: String result: String @@ -2900,7 +2900,7 @@ type Tennis_action_volley @db(name: \\"tennis_action_volleys\\") { trajectory_details: String } -type Tennis_event_state @db(name: \\"tennis_event_states\\") { +type TennisEventState @db(name: \\"tennis_event_states\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"tennis_event_states_id_seq\\", initialValue: 1, allocationSize: 1) context: String current_state: Int @@ -2915,7 +2915,7 @@ type Tennis_event_state @db(name: \\"tennis_event_states\\") { tennis_set: String } -type Tennis_return_stat @db(name: \\"tennis_return_stats\\") { +type TennisReturnStat @db(name: \\"tennis_return_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"tennis_return_stats_id_seq\\", initialValue: 1, allocationSize: 1) break_points_converted: String break_points_converted_pct: String @@ -2931,7 +2931,7 @@ type Tennis_return_stat @db(name: \\"tennis_return_stats\\") { second_service_return_points_won_pct: String } -type Tennis_service_stat @db(name: \\"tennis_service_stats\\") { +type TennisServiceStat @db(name: \\"tennis_service_stats\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"tennis_service_stats_id_seq\\", initialValue: 1, allocationSize: 1) aces: String break_points_played: String @@ -2950,7 +2950,7 @@ type Tennis_service_stat @db(name: \\"tennis_service_stats\\") { services_played: String } -type Wagering_moneyline @db(name: \\"wagering_moneylines\\") { +type WageringMoneyline @db(name: \\"wagering_moneylines\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"wagering_moneylines_id_seq\\", initialValue: 1, allocationSize: 1) bookmaker_id: Bookmaker! comment: String @@ -2965,7 +2965,7 @@ type Wagering_moneyline @db(name: \\"wagering_moneylines\\") { vigorish: String } -type Wagering_odds_line @db(name: \\"wagering_odds_lines\\") { +type WageringOddsLine @db(name: \\"wagering_odds_lines\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"wagering_odds_lines_id_seq\\", initialValue: 1, allocationSize: 1) bookmaker_id: Bookmaker! comment: String @@ -2981,7 +2981,7 @@ type Wagering_odds_line @db(name: \\"wagering_odds_lines\\") { team_id: Team! } -type Wagering_runline @db(name: \\"wagering_runlines\\") { +type WageringRunline @db(name: \\"wagering_runlines\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"wagering_runlines_id_seq\\", initialValue: 1, allocationSize: 1) bookmaker_id: Bookmaker! comment: String @@ -2997,7 +2997,7 @@ type Wagering_runline @db(name: \\"wagering_runlines\\") { vigorish: String } -type Wagering_straight_spread_line @db(name: \\"wagering_straight_spread_lines\\") { +type WageringStraightSpreadLine @db(name: \\"wagering_straight_spread_lines\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"wagering_straight_spread_lines_id_seq\\", initialValue: 1, allocationSize: 1) bookmaker_id: Bookmaker! comment: String @@ -3012,7 +3012,7 @@ type Wagering_straight_spread_line @db(name: \\"wagering_straight_spread_lines\\ vigorish: String } -type Wagering_total_score_line @db(name: \\"wagering_total_score_lines\\") { +type WageringTotalScoreLine @db(name: \\"wagering_total_score_lines\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"wagering_total_score_lines_id_seq\\", initialValue: 1, allocationSize: 1) bookmaker_id: Bookmaker! comment: String @@ -3029,7 +3029,7 @@ type Wagering_total_score_line @db(name: \\"wagering_total_score_lines\\") { vigorish: String } -type Weather_condition @db(name: \\"weather_conditions\\") { +type WeatherCondition @db(name: \\"weather_conditions\\") { id: Int! @id(strategy: SEQUENCE) @sequence(name: \\"weather_conditions_id_seq\\", initialValue: 1, allocationSize: 1) clouds: String event_id: Event! diff --git a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/issue-4081.ts b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/issue-4081.ts new file mode 100644 index 0000000000..2d3183b5ac --- /dev/null +++ b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/issue-4081.ts @@ -0,0 +1,2467 @@ +import testSchema from '../common' + +describe('Introspector', () => { + test('issue4081 relations', async () => { + await testSchema(` + -- PostgreSQL database dump + + -- Dumped from database version 9.5.10 + -- Dumped by pg_dump version 10.5 + + SET statement_timeout = 0; + SET lock_timeout = 0; + SET idle_in_transaction_session_timeout = 0; + SET client_encoding = 'UTF8'; + SET standard_conforming_strings = on; + SELECT pg_catalog.set_config('search_path', '', false); + SET check_function_bodies = false; + SET client_min_messages = warning; + SET row_security = off; + + + + + CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; + COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; + + + SET default_tablespace = ''; + + SET default_with_oids = false; + + + CREATE TABLE issue4081.about_aboutpage ( + id integer NOT NULL, + heading character varying(300) NOT NULL, + text text NOT NULL, + heading_image character varying(100) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.about_aboutpage_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.about_aboutpage_id_seq OWNED BY issue4081.about_aboutpage.id; + + + + CREATE TABLE issue4081.about_teammember ( + id integer NOT NULL, + picture character varying(100) NOT NULL, + name character varying(300) NOT NULL, + "position" character varying(300) NOT NULL, + description text NOT NULL, + facebook_url character varying(200) NOT NULL, + twitter_url character varying(200) NOT NULL, + youtube_url character varying(200) NOT NULL, + instagram_url character varying(200) NOT NULL, + "order" integer NOT NULL, + visible boolean NOT NULL, + page_id integer NOT NULL, + CONSTRAINT about_teammember_order_check CHECK (("order" >= 0)) + ); + + + + + CREATE SEQUENCE issue4081.about_teammember_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.about_teammember_id_seq OWNED BY issue4081.about_teammember.id; + + + + CREATE TABLE issue4081.account_emailaddress ( + id integer NOT NULL, + email character varying(254) NOT NULL, + verified boolean NOT NULL, + "primary" boolean NOT NULL, + user_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.account_emailaddress_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.account_emailaddress_id_seq OWNED BY issue4081.account_emailaddress.id; + + + + CREATE TABLE issue4081.account_emailconfirmation ( + id integer NOT NULL, + created timestamp with time zone NOT NULL, + sent timestamp with time zone, + key character varying(64) NOT NULL, + email_address_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.account_emailconfirmation_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.account_emailconfirmation_id_seq OWNED BY issue4081.account_emailconfirmation.id; + + + + CREATE TABLE issue4081.auth_group ( + id integer NOT NULL, + name character varying(80) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.auth_group_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.auth_group_id_seq OWNED BY issue4081.auth_group.id; + + + + CREATE TABLE issue4081.auth_group_permissions ( + id integer NOT NULL, + group_id integer NOT NULL, + permission_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.auth_group_permissions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.auth_group_permissions_id_seq OWNED BY issue4081.auth_group_permissions.id; + + + + CREATE TABLE issue4081.auth_permission ( + id integer NOT NULL, + name character varying(255) NOT NULL, + content_type_id integer NOT NULL, + codename character varying(100) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.auth_permission_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.auth_permission_id_seq OWNED BY issue4081.auth_permission.id; + + + + CREATE TABLE issue4081.contact_contactformcontent ( + id integer NOT NULL, + title character varying(300) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.contact_contactform_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.contact_contactform_id_seq OWNED BY issue4081.contact_contactformcontent.id; + + + + CREATE TABLE issue4081.contact_contactpage ( + id integer NOT NULL, + heading character varying(300) NOT NULL, + heading_image character varying(100) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.contact_contactpage_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.contact_contactpage_id_seq OWNED BY issue4081.contact_contactpage.id; + + + + CREATE TABLE issue4081.contact_thankyoupage ( + id integer NOT NULL, + heading character varying(300) NOT NULL, + title character varying(300) NOT NULL, + text text NOT NULL, + button_link character varying(200) NOT NULL, + button_text character varying(10) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.contact_thankyoupage_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.contact_thankyoupage_id_seq OWNED BY issue4081.contact_thankyoupage.id; + + + + CREATE TABLE issue4081.django_admin_log ( + id integer NOT NULL, + action_time timestamp with time zone NOT NULL, + object_id text, + object_repr character varying(200) NOT NULL, + action_flag smallint NOT NULL, + change_message text NOT NULL, + content_type_id integer, + user_id integer NOT NULL, + CONSTRAINT django_admin_log_action_flag_check CHECK ((action_flag >= 0)) + ); + + + + + CREATE SEQUENCE issue4081.django_admin_log_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.django_admin_log_id_seq OWNED BY issue4081.django_admin_log.id; + + + + CREATE TABLE issue4081.django_content_type ( + id integer NOT NULL, + app_label character varying(100) NOT NULL, + model character varying(100) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.django_content_type_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.django_content_type_id_seq OWNED BY issue4081.django_content_type.id; + + + + CREATE TABLE issue4081.django_migrations ( + id integer NOT NULL, + app character varying(255) NOT NULL, + name character varying(255) NOT NULL, + applied timestamp with time zone NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.django_migrations_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.django_migrations_id_seq OWNED BY issue4081.django_migrations.id; + + + + CREATE TABLE issue4081.django_session ( + session_key character varying(40) NOT NULL, + session_data text NOT NULL, + expire_date timestamp with time zone NOT NULL + ); + + + + + CREATE TABLE issue4081.django_site ( + id integer NOT NULL, + domain character varying(100) NOT NULL, + name character varying(50) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.django_site_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.django_site_id_seq OWNED BY issue4081.django_site.id; + + + + CREATE TABLE issue4081.events_event ( + id integer NOT NULL, + date date NOT NULL, + "time" time without time zone NOT NULL, + picture character varying(100) NOT NULL, + name character varying(300) NOT NULL, + hosted_by character varying(300) NOT NULL, + topic character varying(600) NOT NULL, + location_name character varying(300) NOT NULL, + address_line_one character varying(300) NOT NULL, + address_line_two character varying(300) NOT NULL, + visible boolean NOT NULL, + map_url character varying(200) NOT NULL, + country character varying(300) NOT NULL, + city character varying(300) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.events_event_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.events_event_id_seq OWNED BY issue4081.events_event.id; + + + + CREATE TABLE issue4081.events_eventspage ( + id integer NOT NULL, + heading_image character varying(100) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.events_eventpage_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.events_eventpage_id_seq OWNED BY issue4081.events_eventspage.id; + + + + CREATE TABLE issue4081.help_helppage ( + id integer NOT NULL, + heading character varying(300) NOT NULL, + heading_image character varying(100) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.help_helppage_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.help_helppage_id_seq OWNED BY issue4081.help_helppage.id; + + + + CREATE TABLE issue4081.help_helpquestion ( + id integer NOT NULL, + title character varying(300) NOT NULL, + answer text NOT NULL, + section_id integer NOT NULL, + thumbnail character varying(100) NOT NULL, + "order" integer, + CONSTRAINT help_helpquestion_order_check CHECK (("order" >= 0)) + ); + + + + + CREATE SEQUENCE issue4081.help_helpquestion_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.help_helpquestion_id_seq OWNED BY issue4081.help_helpquestion.id; + + + + CREATE TABLE issue4081.help_helpsection ( + id integer NOT NULL, + title character varying(300) NOT NULL, + "order" integer NOT NULL, + page_id integer NOT NULL, + CONSTRAINT help_helpsection_order_check CHECK (("order" >= 0)) + ); + + + + + CREATE SEQUENCE issue4081.help_helpsection_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.help_helpsection_id_seq OWNED BY issue4081.help_helpsection.id; + + + + CREATE TABLE issue4081.jobs_job ( + id integer NOT NULL, + picture character varying(100) NOT NULL, + position_name character varying(300) NOT NULL, + position_location character varying(300) NOT NULL, + "order" integer, + visible boolean NOT NULL, + position_description text NOT NULL, + apply_page_title character varying(300) NOT NULL, + apply_page_form_title character varying(300) NOT NULL, + page_id integer NOT NULL, + CONSTRAINT jobs_job_order_check CHECK (("order" >= 0)) + ); + + + + + CREATE SEQUENCE issue4081.jobs_job_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.jobs_job_id_seq OWNED BY issue4081.jobs_job.id; + + + + CREATE TABLE issue4081.jobs_jobspage ( + id integer NOT NULL, + heading character varying(300) NOT NULL, + text text NOT NULL, + heading_image character varying(100) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.jobs_jobspage_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.jobs_jobspage_id_seq OWNED BY issue4081.jobs_jobspage.id; + + + + CREATE TABLE issue4081.jobs_nojobspage ( + id integer NOT NULL, + heading character varying(300) NOT NULL, + title character varying(300) NOT NULL, + text text NOT NULL, + video_thumbnail character varying(100) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.jobs_nojobspage_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.jobs_nojobspage_id_seq OWNED BY issue4081.jobs_nojobspage.id; + + + + CREATE TABLE issue4081.jobs_thankyoupage ( + id integer NOT NULL, + heading character varying(300) NOT NULL, + title character varying(300) NOT NULL, + text text NOT NULL, + button_link character varying(200) NOT NULL, + button_text character varying(10) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.jobs_thankyoupage_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.jobs_thankyoupage_id_seq OWNED BY issue4081.jobs_thankyoupage.id; + + + + CREATE TABLE issue4081.landing_landingpage ( + id integer NOT NULL, + section_one_title character varying(300) NOT NULL, + section_two_title character varying(300) NOT NULL, + section_two_text text NOT NULL, + section_three_title character varying(300) NOT NULL, + section_three_text text NOT NULL, + section_four_title character varying(300) NOT NULL, + section_four_text text NOT NULL, + section_one_image character varying(100) NOT NULL, + section_three_image character varying(100) NOT NULL, + section_two_image character varying(100) NOT NULL, + section_four_image character varying(100) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.landing_landingpage_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.landing_landingpage_id_seq OWNED BY issue4081.landing_landingpage.id; + + + + CREATE TABLE issue4081.languages_language ( + id integer NOT NULL, + language character varying(300) NOT NULL, + language_code character varying(7) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.languages_language_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.languages_language_id_seq OWNED BY issue4081.languages_language.id; + + + + CREATE TABLE issue4081.media_artist ( + id integer NOT NULL, + artist character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.media_artist_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.media_artist_id_seq OWNED BY issue4081.media_artist.id; + + + + CREATE TABLE issue4081.media_category ( + id integer NOT NULL, + category character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.media_category_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.media_category_id_seq OWNED BY issue4081.media_category.id; + + + + CREATE TABLE issue4081.media_genre ( + id integer NOT NULL, + genre character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.media_genre_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.media_genre_id_seq OWNED BY issue4081.media_genre.id; + + + + CREATE TABLE issue4081.media_media ( + id integer NOT NULL, + approved boolean NOT NULL, + title character varying(300) NOT NULL, + note text NOT NULL, + created_date timestamp with time zone, + modified_date timestamp with time zone, + artist_id integer NOT NULL, + created_by_id integer, + genre_id integer, + media_language_id integer NOT NULL, + region_id integer, + translation_language_id integer NOT NULL, + updated_by_id integer + ); + + + + + CREATE TABLE issue4081.media_media_categories ( + id integer NOT NULL, + media_id integer NOT NULL, + category_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.media_media_categories_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.media_media_categories_id_seq OWNED BY issue4081.media_media_categories.id; + + + + CREATE SEQUENCE issue4081.media_media_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.media_media_id_seq OWNED BY issue4081.media_media.id; + + + + CREATE TABLE issue4081.media_mediasentence ( + id integer NOT NULL, + repeated_in_line character varying(140) NOT NULL, + place_in_line integer NOT NULL, + word_count integer NOT NULL, + lyrics_sentence character varying(280) NOT NULL, + lyrics_translation character varying(280) NOT NULL, + note character varying(1000) NOT NULL, + approved boolean NOT NULL, + created_date timestamp with time zone, + modified_date timestamp with time zone, + created_by_id integer, + learning_sentence_id integer NOT NULL, + updated_by_id integer, + media_id integer NOT NULL, + "order" integer NOT NULL, + line integer NOT NULL, + repeat boolean NOT NULL, + repeat_of_line integer NOT NULL, + lyrics_translation_chunks character varying(280) NOT NULL, + multiple boolean NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.media_mediasentence_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.media_mediasentence_id_seq OWNED BY issue4081.media_mediasentence.id; + + + + CREATE TABLE issue4081.media_region ( + id integer NOT NULL, + region character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.media_region_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.media_region_id_seq OWNED BY issue4081.media_region.id; + + + + CREATE TABLE issue4081.sentences_customhint ( + id integer NOT NULL, + hint text NOT NULL, + hint_type_id integer NOT NULL, + sentence_id integer, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_customhint_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_customhint_id_seq OWNED BY issue4081.sentences_customhint.id; + + + + CREATE TABLE issue4081.sentences_ecast ( + id integer NOT NULL, + description text NOT NULL, + action_id integer NOT NULL, + character_id integer NOT NULL, + emotion_id integer NOT NULL, + sentence_id integer NOT NULL, + setting_id integer NOT NULL, + theme_id integer, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_ecast_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_ecast_id_seq OWNED BY issue4081.sentences_ecast.id; + + + + CREATE TABLE issue4081.sentences_ecastaction ( + id integer NOT NULL, + action character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_ecastaction_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_ecastaction_id_seq OWNED BY issue4081.sentences_ecastaction.id; + + + + CREATE TABLE issue4081.sentences_ecastcharacter ( + id integer NOT NULL, + "character" character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_ecastcharacter_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_ecastcharacter_id_seq OWNED BY issue4081.sentences_ecastcharacter.id; + + + + CREATE TABLE issue4081.sentences_ecastemotion ( + id integer NOT NULL, + emotion character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_ecastemotion_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_ecastemotion_id_seq OWNED BY issue4081.sentences_ecastemotion.id; + + + + CREATE TABLE issue4081.sentences_ecastsetting ( + id integer NOT NULL, + setting character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_ecastsetting_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_ecastsetting_id_seq OWNED BY issue4081.sentences_ecastsetting.id; + + + + CREATE TABLE issue4081.sentences_ecasttheme ( + id integer NOT NULL, + theme character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_ecasttheme_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_ecasttheme_id_seq OWNED BY issue4081.sentences_ecasttheme.id; + + + + CREATE TABLE issue4081.sentences_hint ( + id integer NOT NULL, + hint text NOT NULL, + hint_type_id integer NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_hint_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_hint_id_seq OWNED BY issue4081.sentences_hint.id; + + + + CREATE TABLE issue4081.sentences_hinttype ( + id integer NOT NULL, + type_name character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_hinttype_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_hinttype_id_seq OWNED BY issue4081.sentences_hinttype.id; + + + + CREATE TABLE issue4081.sentences_imagecategory ( + id integer NOT NULL, + category character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_imagecategory_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_imagecategory_id_seq OWNED BY issue4081.sentences_imagecategory.id; + + + + CREATE TABLE issue4081.sentences_imagetype ( + id integer NOT NULL, + name character varying(300) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.sentences_imagetype_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_imagetype_id_seq OWNED BY issue4081.sentences_imagetype.id; + + + + CREATE TABLE issue4081.sentences_posttranslationword ( + id integer NOT NULL, + post character varying(140) NOT NULL, + language_id integer, + note text NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_posttranslationword_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_posttranslationword_id_seq OWNED BY issue4081.sentences_posttranslationword.id; + + + + CREATE TABLE issue4081.sentences_pretranslationword ( + id integer NOT NULL, + pre character varying(140) NOT NULL, + language_id integer, + note text NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_pretranslationword_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_pretranslationword_id_seq OWNED BY issue4081.sentences_pretranslationword.id; + + + + CREATE TABLE issue4081.sentences_sentence ( + id integer NOT NULL, + to_check character varying(300), + note character varying(300), + chunk_id integer, + fifth_chunk_id integer, + first_chunk_id integer, + fourth_chunk_id integer, + learning_language_id integer, + second_chunk_id integer, + sentence_category_id integer, + sentence_audio_id integer, + seventh_chunk_id integer, + sixth_chunk_id integer, + third_chunk_id integer, + translation_language_id integer, + approved boolean NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + updated_by_id integer, + english_sentence_punctuation_end character varying(140) NOT NULL, + english_sentence_punctuation_start character varying(140) NOT NULL, + learning_sentence_punctuation_end character varying(140) NOT NULL, + learning_sentence_punctuation_start character varying(140) NOT NULL, + translation_sentence_punctuation_end character varying(140) NOT NULL, + translation_sentence_punctuation_start character varying(140) NOT NULL, + first_post_id integer, + first_pre_id integer, + first_underlined boolean NOT NULL, + first_order boolean NOT NULL, + fifth_order boolean NOT NULL, + fifth_post_id integer, + fifth_pre_id integer, + fifth_underlined boolean NOT NULL, + fourth_order boolean NOT NULL, + fourth_post_id integer, + fourth_pre_id integer, + fourth_underlined boolean NOT NULL, + second_order boolean NOT NULL, + second_post_id integer, + second_pre_id integer, + second_underlined boolean NOT NULL, + seventh_order boolean NOT NULL, + seventh_post_id integer, + seventh_pre_id integer, + seventh_underlined boolean NOT NULL, + sixth_order boolean NOT NULL, + sixth_post_id integer, + sixth_pre_id integer, + sixth_underlined boolean NOT NULL, + third_order boolean NOT NULL, + third_post_id integer, + third_pre_id integer, + third_underlined boolean NOT NULL + ); + + + + + CREATE TABLE issue4081.sentences_sentence_hints ( + id integer NOT NULL, + sentence_id integer NOT NULL, + hint_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.sentences_sentence_hints_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_sentence_hints_id_seq OWNED BY issue4081.sentences_sentence_hints.id; + + + + CREATE SEQUENCE issue4081.sentences_sentence_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_sentence_id_seq OWNED BY issue4081.sentences_sentence.id; + + + + CREATE TABLE issue4081.sentences_sentenceaudio ( + id integer NOT NULL, + sentence_in_audio character varying(300) NOT NULL, + audio_file character varying(100), + audio_language_id integer NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_sentenceaudio_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_sentenceaudio_id_seq OWNED BY issue4081.sentences_sentenceaudio.id; + + + + CREATE TABLE issue4081.sentences_sentencecategory ( + id integer NOT NULL, + category_type character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_sentencecategory_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_sentencecategory_id_seq OWNED BY issue4081.sentences_sentencecategory.id; + + + + CREATE TABLE issue4081.sentences_sentencegluer ( + id integer NOT NULL, + learning_sentence_id integer NOT NULL, + translation_sentence_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.sentences_sentencegluer_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_sentencegluer_id_seq OWNED BY issue4081.sentences_sentencegluer.id; + + + + CREATE TABLE issue4081.sentences_sentenceimage ( + id integer NOT NULL, + file character varying(100), + description text NOT NULL, + image_category_id integer NOT NULL, + image_text_language_id integer, + sentence_id integer, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_sentenceimage_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_sentenceimage_id_seq OWNED BY issue4081.sentences_sentenceimage.id; + + + + CREATE TABLE issue4081.sentences_sentencevideo ( + id integer NOT NULL, + file character varying(100), + description text NOT NULL, + audio_language_id integer NOT NULL, + sentence_id integer, + video_category_id integer NOT NULL, + video_thumbnail character varying(100), + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_sentencevideo_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_sentencevideo_id_seq OWNED BY issue4081.sentences_sentencevideo.id; + + + + CREATE TABLE issue4081.sentences_videocategory ( + id integer NOT NULL, + category character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.sentences_videocategory_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.sentences_videocategory_id_seq OWNED BY issue4081.sentences_videocategory.id; + + + + CREATE TABLE issue4081.socialaccount_socialaccount ( + id integer NOT NULL, + provider character varying(30) NOT NULL, + uid character varying(191) NOT NULL, + last_login timestamp with time zone NOT NULL, + date_joined timestamp with time zone NOT NULL, + extra_data text NOT NULL, + user_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.socialaccount_socialaccount_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.socialaccount_socialaccount_id_seq OWNED BY issue4081.socialaccount_socialaccount.id; + + + + CREATE TABLE issue4081.socialaccount_socialapp ( + id integer NOT NULL, + provider character varying(30) NOT NULL, + name character varying(40) NOT NULL, + client_id character varying(191) NOT NULL, + secret character varying(191) NOT NULL, + key character varying(191) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.socialaccount_socialapp_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.socialaccount_socialapp_id_seq OWNED BY issue4081.socialaccount_socialapp.id; + + + + CREATE TABLE issue4081.socialaccount_socialapp_sites ( + id integer NOT NULL, + socialapp_id integer NOT NULL, + site_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.socialaccount_socialapp_sites_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.socialaccount_socialapp_sites_id_seq OWNED BY issue4081.socialaccount_socialapp_sites.id; + + + + CREATE TABLE issue4081.socialaccount_socialtoken ( + id integer NOT NULL, + token text NOT NULL, + token_secret text NOT NULL, + expires_at timestamp with time zone, + account_id integer NOT NULL, + app_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.socialaccount_socialtoken_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.socialaccount_socialtoken_id_seq OWNED BY issue4081.socialaccount_socialtoken.id; + + + + CREATE TABLE issue4081.users_emailnotifications ( + id integer NOT NULL, + events boolean NOT NULL, + promotions boolean NOT NULL, + new_features boolean NOT NULL, + news boolean NOT NULL, + progress boolean NOT NULL, + user_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.users_emailnotifications_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.users_emailnotifications_id_seq OWNED BY issue4081.users_emailnotifications.id; + + + + CREATE TABLE issue4081.users_profile ( + id integer NOT NULL, + profile_picture character varying(100) NOT NULL, + censored_content boolean NOT NULL, + date_of_birth date, + gender character varying(10) NOT NULL, + country character varying(2) NOT NULL, + learning_language_id integer, + speaking_language_id integer + ); + + + + + CREATE SEQUENCE issue4081.users_profile_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.users_profile_id_seq OWNED BY issue4081.users_profile.id; + + + + CREATE TABLE issue4081.users_spotssettings ( + id integer NOT NULL, + autoplay_word_audio boolean NOT NULL, + autoplay_sentence_audio boolean NOT NULL, + play_videos boolean NOT NULL, + show_main_images boolean NOT NULL, + show_submitted_images boolean NOT NULL, + reveal_seconds integer NOT NULL, + next_word_seconds integer NOT NULL, + show_keyboard_shortcuts boolean NOT NULL, + user_id integer NOT NULL, + spots_page_auto boolean NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.users_spotssettings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.users_spotssettings_id_seq OWNED BY issue4081.users_spotssettings.id; + + + + CREATE TABLE issue4081.users_subscription ( + id integer NOT NULL, + expiration_date date NOT NULL, + expired boolean NOT NULL, + subscription_type_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.users_subscription_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.users_subscription_id_seq OWNED BY issue4081.users_subscription.id; + + + + CREATE TABLE issue4081.users_subscriptiontype ( + id integer NOT NULL, + price numeric(6,2) NOT NULL, + name character varying(140) NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.users_subscriptiontype_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.users_subscriptiontype_id_seq OWNED BY issue4081.users_subscriptiontype.id; + + + + CREATE TABLE issue4081.users_user ( + id integer NOT NULL, + password character varying(128) NOT NULL, + last_login timestamp with time zone, + is_superuser boolean NOT NULL, + username character varying(150) NOT NULL, + first_name character varying(30) NOT NULL, + last_name character varying(30) NOT NULL, + email character varying(254) NOT NULL, + is_staff boolean NOT NULL, + is_active boolean NOT NULL, + date_joined timestamp with time zone NOT NULL, + free_account boolean NOT NULL, + first_time boolean NOT NULL, + profile_id integer + ); + + + + + CREATE TABLE issue4081.users_user_groups ( + id integer NOT NULL, + user_id integer NOT NULL, + group_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.users_user_groups_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.users_user_groups_id_seq OWNED BY issue4081.users_user_groups.id; + + + + CREATE SEQUENCE issue4081.users_user_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.users_user_id_seq OWNED BY issue4081.users_user.id; + + + + CREATE TABLE issue4081.users_user_user_permissions ( + id integer NOT NULL, + user_id integer NOT NULL, + permission_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.users_user_user_permissions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.users_user_user_permissions_id_seq OWNED BY issue4081.users_user_user_permissions.id; + + + + CREATE TABLE issue4081.users_vocabularysettings ( + id integer NOT NULL, + word_audio_play_click boolean NOT NULL, + sentence_audio_play_click boolean NOT NULL, + show_keyboard_shortcuts boolean NOT NULL, + user_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.users_vocabularysettings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.users_vocabularysettings_id_seq OWNED BY issue4081.users_vocabularysettings.id; + + + + CREATE TABLE issue4081.words_chunk ( + id integer NOT NULL, + learning_word_id integer NOT NULL, + translation_word_id integer NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer, + vocab boolean NOT NULL, + chunk_type_id integer + ); + + + + + CREATE SEQUENCE issue4081.words_chunk_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.words_chunk_id_seq OWNED BY issue4081.words_chunk.id; + + + + CREATE TABLE issue4081.words_chunktype ( + id integer NOT NULL, + chunk_type character varying(140), + note text NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.words_chunktype_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.words_chunktype_id_seq OWNED BY issue4081.words_chunktype.id; + + + + CREATE TABLE issue4081.words_englishword ( + id integer NOT NULL, + word_in_english character varying(300) NOT NULL, + definition text NOT NULL, + grammar_id integer NOT NULL, + example_sentence text NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + english_word_note text NOT NULL, + modified_date timestamp with time zone, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.words_englishword_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.words_englishword_id_seq OWNED BY issue4081.words_englishword.id; + + + + CREATE TABLE issue4081.words_gender ( + id integer NOT NULL, + gender character varying(140) NOT NULL, + note text NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.words_gender_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.words_gender_id_seq OWNED BY issue4081.words_gender.id; + + + + CREATE TABLE issue4081.words_grammar ( + id integer NOT NULL, + grammar_note text NOT NULL, + name character varying(300) NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + updated_by_id integer, + examples text NOT NULL, + syntax character varying(300) NOT NULL, + grammar_group character varying(300) NOT NULL, + user_grammar character varying(300) NOT NULL, + tense_type_id integer + ); + + + + + CREATE SEQUENCE issue4081.words_grammar_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.words_grammar_id_seq OWNED BY issue4081.words_grammar.id; + + + + CREATE TABLE issue4081.words_knownword ( + id integer NOT NULL, + created_at timestamp with time zone NOT NULL, + user_id integer NOT NULL, + word_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.words_knownword_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.words_knownword_id_seq OWNED BY issue4081.words_knownword.id; + + + + CREATE TABLE issue4081.words_masteredword ( + id integer NOT NULL, + created_at timestamp with time zone NOT NULL, + user_id integer NOT NULL, + word_id integer NOT NULL + ); + + + + + CREATE SEQUENCE issue4081.words_masteredword_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.words_masteredword_id_seq OWNED BY issue4081.words_masteredword.id; + + + + CREATE TABLE issue4081.words_person ( + id integer NOT NULL, + person character varying(140) NOT NULL, + note text NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.words_person_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.words_person_id_seq OWNED BY issue4081.words_person.id; + + + + CREATE TABLE issue4081.words_tensetype ( + id integer NOT NULL, + tense_type character varying(140) NOT NULL, + note text NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.words_tensetype_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.words_tensetype_id_seq OWNED BY issue4081.words_tensetype.id; + + + + CREATE TABLE issue4081.words_word ( + id integer NOT NULL, + word character varying(300) NOT NULL, + word_language_id integer NOT NULL, + word_in_english_id integer NOT NULL, + word_audio_id integer, + word_grammar_id integer, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer, + gender_id integer, + person_id integer, + number character varying(100) + ); + + + + + CREATE SEQUENCE issue4081.words_word_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.words_word_id_seq OWNED BY issue4081.words_word.id; + + + + CREATE TABLE issue4081.words_wordaudio ( + id integer NOT NULL, + word_in_audio character varying(300) NOT NULL, + ipa character varying(600) NOT NULL, + example_sentence text NOT NULL, + audio_file character varying(100), + audio_language_id integer NOT NULL, + created_by_id integer, + created_date timestamp with time zone, + modified_date timestamp with time zone, + note text NOT NULL, + updated_by_id integer + ); + + + + + CREATE SEQUENCE issue4081.words_wordaudio_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + + + + ALTER SEQUENCE issue4081.words_wordaudio_id_seq OWNED BY issue4081.words_wordaudio.id; + + + + ALTER TABLE ONLY issue4081.about_aboutpage ALTER COLUMN id SET DEFAULT nextval('issue4081.about_aboutpage_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.about_teammember ALTER COLUMN id SET DEFAULT nextval('issue4081.about_teammember_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.account_emailaddress ALTER COLUMN id SET DEFAULT nextval('issue4081.account_emailaddress_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.account_emailconfirmation ALTER COLUMN id SET DEFAULT nextval('issue4081.account_emailconfirmation_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.auth_group ALTER COLUMN id SET DEFAULT nextval('issue4081.auth_group_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.auth_group_permissions ALTER COLUMN id SET DEFAULT nextval('issue4081.auth_group_permissions_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.auth_permission ALTER COLUMN id SET DEFAULT nextval('issue4081.auth_permission_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.contact_contactformcontent ALTER COLUMN id SET DEFAULT nextval('issue4081.contact_contactform_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.contact_contactpage ALTER COLUMN id SET DEFAULT nextval('issue4081.contact_contactpage_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.contact_thankyoupage ALTER COLUMN id SET DEFAULT nextval('issue4081.contact_thankyoupage_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.django_admin_log ALTER COLUMN id SET DEFAULT nextval('issue4081.django_admin_log_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.django_content_type ALTER COLUMN id SET DEFAULT nextval('issue4081.django_content_type_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.django_migrations ALTER COLUMN id SET DEFAULT nextval('issue4081.django_migrations_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.django_site ALTER COLUMN id SET DEFAULT nextval('issue4081.django_site_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.events_event ALTER COLUMN id SET DEFAULT nextval('issue4081.events_event_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.events_eventspage ALTER COLUMN id SET DEFAULT nextval('issue4081.events_eventpage_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.help_helppage ALTER COLUMN id SET DEFAULT nextval('issue4081.help_helppage_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.help_helpquestion ALTER COLUMN id SET DEFAULT nextval('issue4081.help_helpquestion_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.help_helpsection ALTER COLUMN id SET DEFAULT nextval('issue4081.help_helpsection_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.jobs_job ALTER COLUMN id SET DEFAULT nextval('issue4081.jobs_job_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.jobs_jobspage ALTER COLUMN id SET DEFAULT nextval('issue4081.jobs_jobspage_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.jobs_nojobspage ALTER COLUMN id SET DEFAULT nextval('issue4081.jobs_nojobspage_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.jobs_thankyoupage ALTER COLUMN id SET DEFAULT nextval('issue4081.jobs_thankyoupage_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.landing_landingpage ALTER COLUMN id SET DEFAULT nextval('issue4081.landing_landingpage_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.languages_language ALTER COLUMN id SET DEFAULT nextval('issue4081.languages_language_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.media_artist ALTER COLUMN id SET DEFAULT nextval('issue4081.media_artist_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.media_category ALTER COLUMN id SET DEFAULT nextval('issue4081.media_category_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.media_genre ALTER COLUMN id SET DEFAULT nextval('issue4081.media_genre_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.media_media ALTER COLUMN id SET DEFAULT nextval('issue4081.media_media_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.media_media_categories ALTER COLUMN id SET DEFAULT nextval('issue4081.media_media_categories_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.media_mediasentence ALTER COLUMN id SET DEFAULT nextval('issue4081.media_mediasentence_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.media_region ALTER COLUMN id SET DEFAULT nextval('issue4081.media_region_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_customhint ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_customhint_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_ecast ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_ecast_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_ecastaction ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_ecastaction_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_ecastcharacter ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_ecastcharacter_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_ecastemotion ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_ecastemotion_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_ecastsetting ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_ecastsetting_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_ecasttheme ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_ecasttheme_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_hint ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_hint_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_hinttype ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_hinttype_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_imagecategory ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_imagecategory_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_imagetype ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_imagetype_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_posttranslationword ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_posttranslationword_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_pretranslationword ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_pretranslationword_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_sentence ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_sentence_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_sentence_hints ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_sentence_hints_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_sentenceaudio ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_sentenceaudio_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_sentencecategory ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_sentencecategory_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_sentencegluer ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_sentencegluer_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_sentenceimage ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_sentenceimage_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_sentencevideo ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_sentencevideo_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.sentences_videocategory ALTER COLUMN id SET DEFAULT nextval('issue4081.sentences_videocategory_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.socialaccount_socialaccount ALTER COLUMN id SET DEFAULT nextval('issue4081.socialaccount_socialaccount_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.socialaccount_socialapp ALTER COLUMN id SET DEFAULT nextval('issue4081.socialaccount_socialapp_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.socialaccount_socialapp_sites ALTER COLUMN id SET DEFAULT nextval('issue4081.socialaccount_socialapp_sites_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.socialaccount_socialtoken ALTER COLUMN id SET DEFAULT nextval('issue4081.socialaccount_socialtoken_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.users_emailnotifications ALTER COLUMN id SET DEFAULT nextval('issue4081.users_emailnotifications_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.users_profile ALTER COLUMN id SET DEFAULT nextval('issue4081.users_profile_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.users_spotssettings ALTER COLUMN id SET DEFAULT nextval('issue4081.users_spotssettings_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.users_subscription ALTER COLUMN id SET DEFAULT nextval('issue4081.users_subscription_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.users_subscriptiontype ALTER COLUMN id SET DEFAULT nextval('issue4081.users_subscriptiontype_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.users_user ALTER COLUMN id SET DEFAULT nextval('issue4081.users_user_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.users_user_groups ALTER COLUMN id SET DEFAULT nextval('issue4081.users_user_groups_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.users_user_user_permissions ALTER COLUMN id SET DEFAULT nextval('issue4081.users_user_user_permissions_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.users_vocabularysettings ALTER COLUMN id SET DEFAULT nextval('issue4081.users_vocabularysettings_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.words_chunk ALTER COLUMN id SET DEFAULT nextval('issue4081.words_chunk_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.words_chunktype ALTER COLUMN id SET DEFAULT nextval('issue4081.words_chunktype_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.words_englishword ALTER COLUMN id SET DEFAULT nextval('issue4081.words_englishword_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.words_gender ALTER COLUMN id SET DEFAULT nextval('issue4081.words_gender_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.words_grammar ALTER COLUMN id SET DEFAULT nextval('issue4081.words_grammar_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.words_knownword ALTER COLUMN id SET DEFAULT nextval('issue4081.words_knownword_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.words_masteredword ALTER COLUMN id SET DEFAULT nextval('issue4081.words_masteredword_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.words_person ALTER COLUMN id SET DEFAULT nextval('issue4081.words_person_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.words_tensetype ALTER COLUMN id SET DEFAULT nextval('issue4081.words_tensetype_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.words_word ALTER COLUMN id SET DEFAULT nextval('issue4081.words_word_id_seq'::regclass); + + + + ALTER TABLE ONLY issue4081.words_wordaudio ALTER COLUMN id SET DEFAULT nextval('issue4081.words_wordaudio_id_seq'::regclass);`, + 'issue4081') + }) +}) diff --git a/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/issue-4095.ts b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/issue-4095.ts new file mode 100644 index 0000000000..e5b055dd75 --- /dev/null +++ b/cli/packages/prisma-db-introspection/src/__tests__/postgres/blackbox/withoutExistingSchema/issue-4095.ts @@ -0,0 +1,231 @@ +import testSchema from '../common' + +describe('Introspector', () => { + test('issue4095 relations', async () => { + await testSchema(`-- ------------------------------------------------------------- + -- TablePlus 1.5(190) + -- + -- https://tableplus.com/ + -- + -- Database: issue4019 + -- Generation Time: 2019-02-08 19:22:20.9670 + -- ------------------------------------------------------------- + + + DROP TABLE IF EXISTS "billing"."BillingAccount"; + -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. + + -- Sequence and defined type + CREATE SEQUENCE IF NOT EXISTS billing."BillingAccount_id_seq"; + + -- Table Definition + CREATE TABLE "billing"."BillingAccount" ( + "id" int8 NOT NULL DEFAULT nextval('billing."BillingAccount_id_seq"'::regclass), + "name" varchar(50) NOT NULL, + "createdAt" timestamptz NOT NULL, + "updatedAt" timestamptz NOT NULL, + "customerId" int8 NOT NULL, + PRIMARY KEY ("id") + ); + + DROP TABLE IF EXISTS "billing"."Condition"; + -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. + + -- Sequence and defined type + CREATE SEQUENCE IF NOT EXISTS billing."Condition_id_seq"; + + -- Table Definition + CREATE TABLE "billing"."Condition" ( + "id" int8 NOT NULL DEFAULT nextval('billing."Condition_id_seq"'::regclass), + "startDate" timestamptz NOT NULL, + "endDate" timestamptz NOT NULL, + "createdAt" timestamptz NOT NULL, + "updatedAt" timestamptz NOT NULL, + "productId" int8 NOT NULL, + PRIMARY KEY ("id") + ); + + DROP TABLE IF EXISTS "billing"."Contract"; + -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. + + -- Sequence and defined type + CREATE SEQUENCE IF NOT EXISTS billing."Contract_id_seq"; + + -- Table Definition + CREATE TABLE "billing"."Contract" ( + "id" int8 NOT NULL DEFAULT nextval('billing."Contract_id_seq"'::regclass), + "code" varchar(50) NOT NULL, + "enabled" bool DEFAULT false, + "activationDateTime" timestamptz, + "minimumFee" numeric NOT NULL, + "lookToBook" numeric NOT NULL, + "customerId" int8 NOT NULL, + "createdAt" timestamptz NOT NULL, + "updatedAt" timestamptz NOT NULL, + PRIMARY KEY ("id") + ); + + DROP TABLE IF EXISTS "billing"."Cost"; + -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. + + -- Sequence and defined type + CREATE SEQUENCE IF NOT EXISTS billing."Cost_id_seq"; + + -- Table Definition + CREATE TABLE "billing"."Cost" ( + "id" int8 NOT NULL DEFAULT nextval('billing."Cost_id_seq"'::regclass), + "type" varchar(50) NOT NULL, + "min" int4, + "max" int4, + "amount" numeric, + "createdAt" timestamptz NOT NULL, + "updatedAt" timestamptz NOT NULL, + "contractId" int8, + "itemId" int8, + PRIMARY KEY ("id") + ); + + DROP TABLE IF EXISTS "billing"."Customer"; + -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. + + -- Sequence and defined type + CREATE SEQUENCE IF NOT EXISTS billing."Customer_id_seq"; + + -- Table Definition + CREATE TABLE "billing"."Customer" ( + "id" int8 NOT NULL DEFAULT nextval('billing."Customer_id_seq"'::regclass), + "code" varchar(50) NOT NULL, + "name" varchar(100) NOT NULL, + "createdAt" timestamptz NOT NULL, + "updatedAt" timestamptz NOT NULL, + PRIMARY KEY ("id") + ); + + DROP TABLE IF EXISTS "billing"."GlobalCondition"; + -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. + + -- Sequence and defined type + CREATE SEQUENCE IF NOT EXISTS billing."GlobalCondition_id_seq"; + + -- Table Definition + CREATE TABLE "billing"."GlobalCondition" ( + "id" int8 NOT NULL DEFAULT nextval('billing."GlobalCondition_id_seq"'::regclass), + "type" varchar(50) NOT NULL, + "numConnections" int4, + "millionSearches" int4, + "amount" numeric, + "globalStartFreeTrialPeriod" timestamptz, + "globalEndFreeTrialPeriod" timestamptz, + "defaultFreeTrialDaysForItem" int4, + "createdAt" timestamptz NOT NULL, + "updatedAt" timestamptz NOT NULL, + "contractId" int8 NOT NULL, + PRIMARY KEY ("id") + ); + + DROP TABLE IF EXISTS "billing"."Invoice"; + -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. + + -- Sequence and defined type + CREATE SEQUENCE IF NOT EXISTS billing."Invoice_id_seq"; + + -- Table Definition + CREATE TABLE "billing"."Invoice" ( + "id" int8 NOT NULL DEFAULT nextval('billing."Invoice_id_seq"'::regclass), + "startDate" timestamptz NOT NULL, + "endDate" timestamptz NOT NULL, + "paymentMethod" varchar(100) NOT NULL, + "paymentStatus" varchar(100) NOT NULL, + "amount" numeric NOT NULL, + "amountPaid" numeric NOT NULL, + "createdAt" timestamptz NOT NULL, + "updatedAt" timestamptz NOT NULL, + "billingAccountId" int8 NOT NULL, + PRIMARY KEY ("id") + ); + + DROP TABLE IF EXISTS "billing"."InvoiceBreakdown"; + -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. + + -- Sequence and defined type + CREATE SEQUENCE IF NOT EXISTS billing."InvoiceBreakdown_id_seq"; + + -- Table Definition + CREATE TABLE "billing"."InvoiceBreakdown" ( + "id" int8 NOT NULL DEFAULT nextval('billing."InvoiceBreakdown_id_seq"'::regclass), + "startDate" timestamptz NOT NULL, + "endDate" timestamptz NOT NULL, + "amount" numeric NOT NULL, + "createdAt" timestamptz NOT NULL, + "updatedAt" timestamptz NOT NULL, + "itemBillingAccountId" int8 NOT NULL, + "invoiceId" int8 NOT NULL, + PRIMARY KEY ("id") + ); + + DROP TABLE IF EXISTS "billing"."Item"; + -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. + + -- Sequence and defined type + CREATE SEQUENCE IF NOT EXISTS billing."Item_id_seq"; + + -- Table Definition + CREATE TABLE "billing"."Item" ( + "id" int8 NOT NULL DEFAULT nextval('billing."Item_id_seq"'::regclass), + "enabled" bool DEFAULT false, + "specificFreeTrialDays" int4, + "createdAt" timestamptz NOT NULL, + "updatedAt" timestamptz NOT NULL, + "contractId" int8 NOT NULL, + PRIMARY KEY ("id") + ); + + DROP TABLE IF EXISTS "billing"."Item_BillingAccount"; + -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. + + -- Sequence and defined type + CREATE SEQUENCE IF NOT EXISTS billing."Item_BillingAccount_id_seq"; + + -- Table Definition + CREATE TABLE "billing"."Item_BillingAccount" ( + "id" int8 NOT NULL DEFAULT nextval('billing."Item_BillingAccount_id_seq"'::regclass), + "createdAt" timestamptz NOT NULL, + "updatedAt" timestamptz NOT NULL, + "billingAccountId" int8 NOT NULL, + "itemId" int8 NOT NULL, + PRIMARY KEY ("id") + ); + + DROP TABLE IF EXISTS "billing"."Product"; + -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. + + -- Sequence and defined type + CREATE SEQUENCE IF NOT EXISTS billing."Product_id_seq"; + + -- Table Definition + CREATE TABLE "billing"."Product" ( + "id" int8 NOT NULL DEFAULT nextval('billing."Product_id_seq"'::regclass), + "type" varchar(50) NOT NULL, + "system" varchar(200) NOT NULL, + "createdAt" timestamptz NOT NULL, + "updatedAt" timestamptz NOT NULL, + "itemId" int8 NOT NULL, + PRIMARY KEY ("id") + ); + + ALTER TABLE "billing"."BillingAccount" ADD FOREIGN KEY ("customerId") REFERENCES "billing"."Customer"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ALTER TABLE "billing"."Condition" ADD FOREIGN KEY ("productId") REFERENCES "billing"."Product"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ALTER TABLE "billing"."Contract" ADD FOREIGN KEY ("customerId") REFERENCES "billing"."Customer"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ALTER TABLE "billing"."Cost" ADD FOREIGN KEY ("contractId") REFERENCES "billing"."Contract"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ALTER TABLE "billing"."Cost" ADD FOREIGN KEY ("itemId") REFERENCES "billing"."Item"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ALTER TABLE "billing"."GlobalCondition" ADD FOREIGN KEY ("contractId") REFERENCES "billing"."Contract"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ALTER TABLE "billing"."Invoice" ADD FOREIGN KEY ("billingAccountId") REFERENCES "billing"."BillingAccount"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ALTER TABLE "billing"."InvoiceBreakdown" ADD FOREIGN KEY ("invoiceId") REFERENCES "billing"."Invoice"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ALTER TABLE "billing"."InvoiceBreakdown" ADD FOREIGN KEY ("itemBillingAccountId") REFERENCES "billing"."Item_BillingAccount"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ALTER TABLE "billing"."Item" ADD FOREIGN KEY ("contractId") REFERENCES "billing"."Contract"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ALTER TABLE "billing"."Item_BillingAccount" ADD FOREIGN KEY ("billingAccountId") REFERENCES "billing"."BillingAccount"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ALTER TABLE "billing"."Item_BillingAccount" ADD FOREIGN KEY ("itemId") REFERENCES "billing"."Item"("id") ON DELETE CASCADE ON UPDATE CASCADE; + ALTER TABLE "billing"."Product" ADD FOREIGN KEY ("itemId") REFERENCES "billing"."Item"("id") ON DELETE CASCADE ON UPDATE CASCADE;`, + 'billing') + }) +}) diff --git a/cli/packages/prisma-db-introspection/src/common/introspectionResult.ts b/cli/packages/prisma-db-introspection/src/common/introspectionResult.ts index cac3b3c811..6a02f682a9 100644 --- a/cli/packages/prisma-db-introspection/src/common/introspectionResult.ts +++ b/cli/packages/prisma-db-introspection/src/common/introspectionResult.ts @@ -10,6 +10,9 @@ export abstract class IntrospectionResult { this.databaseType = databaseType } + /** + * @deprecated This returns an unnormalized datamodel and might get removed in the near future. + */ public abstract getDatamodel(): ISDL public renderToDatamodelString(): string { diff --git a/cli/packages/prisma-db-introspection/src/common/normalization/modelNameNormalizer.ts b/cli/packages/prisma-db-introspection/src/common/normalization/modelNameNormalizer.ts index 355f3b26be..7fa110179c 100644 --- a/cli/packages/prisma-db-introspection/src/common/normalization/modelNameNormalizer.ts +++ b/cli/packages/prisma-db-introspection/src/common/normalization/modelNameNormalizer.ts @@ -6,8 +6,11 @@ import { capitalize, plural, toposort, + isTypeIdentifier, } from 'prisma-datamodel' import { INormalizer } from './normalizer' +import * as uppercamelcase from 'uppercamelcase' +import { groupBy, uniqBy } from 'lodash' export default class ModelNameNormalizer implements INormalizer { public normalize(model: ISDL) { @@ -17,6 +20,8 @@ export default class ModelNameNormalizer implements INormalizer { for (const type of toposort(model.types)) { this.normalizeType(type, model) } + + this.fixConflicts(model) } protected assignName(obj: IGQLType | IGQLField, newName: string) { @@ -30,17 +35,63 @@ export default class ModelNameNormalizer implements INormalizer { } } + protected getNormalizedName(name: string, model: ISDL) { + if (name.toUpperCase() === name) { + return name + } + + const normalizedName = uppercamelcase(singular(name)) + + // if there is a naming conflict with a known scalar type, use the default name + if (isTypeIdentifier(normalizedName) || isTypeIdentifier(singular(name))) { + return name + } + + // if there is already a table in the database with the exact name we're generating - let's just not do it + if (model.types.some(t => (t.databaseName || t.name) === normalizedName)) { + return name + } + + return normalizedName + } + protected normalizeType( type: IGQLType, - parentModel: ISDL, + model: ISDL, forceNoRename: boolean = false, ) { if (!forceNoRename) { - this.assignName(type, capitalize(singular(type.name))) + this.assignName(type, this.getNormalizedName(type.name, model)) } for (const field of type.fields) { - this.normalizeField(field, type, parentModel) + this.normalizeField(field, type, model) + } + } + + protected fixConflicts(model: ISDL) { + const groupedTypesByName = groupBy(model.types, t => t.name) + + for (const types of Object.values(groupedTypesByName)) { + if (types.length > 1) { + for (const type of types) { + if (type.databaseName) { + type.name = uppercamelcase(type.databaseName) + } + } + + const uniqueTypes = uniqBy(types, t => t.name) + + // if there still are duplicates, default to the database name + if (uniqueTypes.length < types.length) { + for (const type of types) { + if (type.databaseName) { + type.name = type.databaseName + type.databaseName = null + } + } + } + } } } diff --git a/cli/packages/prisma-db-introspection/src/databases/document/documentIntrospectionResult.ts b/cli/packages/prisma-db-introspection/src/databases/document/documentIntrospectionResult.ts index b7997911c0..b493826b15 100644 --- a/cli/packages/prisma-db-introspection/src/databases/document/documentIntrospectionResult.ts +++ b/cli/packages/prisma-db-introspection/src/databases/document/documentIntrospectionResult.ts @@ -14,6 +14,9 @@ export class DocumentIntrospectionResult extends IntrospectionResult { this.model = model } + /** + * @deprecated This returns an unnormalized datamodel and might get removed in the near future. + */ public getDatamodel(): ISDL { // Return a copy - object is muteable. return cloneSchema(this.model) diff --git a/cli/packages/prisma-db-introspection/src/databases/relational/postgres/postgresIntrospectionResult.ts b/cli/packages/prisma-db-introspection/src/databases/relational/postgres/postgresIntrospectionResult.ts index 74fd72c53b..90a3269587 100644 --- a/cli/packages/prisma-db-introspection/src/databases/relational/postgres/postgresIntrospectionResult.ts +++ b/cli/packages/prisma-db-introspection/src/databases/relational/postgres/postgresIntrospectionResult.ts @@ -123,10 +123,14 @@ export class PostgresIntrospectionResult extends RelationalIntrospectionResult { if (match === null) { continue } + let [dummy, seqName] = match - const [dummy, seqName] = match + // Trim quotes. + if(seqName.startsWith('"')) { + seqName = seqName.substring(1, seqName.length - 1) + } - const seq = sequences.find(seq => seq.name === seqName) + const seq = sequences.find(x => x.name === seqName) if (seq === undefined) { field.comments.push({ diff --git a/cli/packages/prisma-db-introspection/src/databases/relational/relationalIntrospectionResult.ts b/cli/packages/prisma-db-introspection/src/databases/relational/relationalIntrospectionResult.ts index ff8b7ef7b0..e4a3a99b8b 100644 --- a/cli/packages/prisma-db-introspection/src/databases/relational/relationalIntrospectionResult.ts +++ b/cli/packages/prisma-db-introspection/src/databases/relational/relationalIntrospectionResult.ts @@ -45,6 +45,9 @@ export abstract class RelationalIntrospectionResult extends IntrospectionResult this.sequences = sequences } + /** + * @deprecated This returns an unnormalized datamodel and might get removed in the near future. + */ public getDatamodel(): ISDL { return this.infer(this.model, this.enums, this.relations, this.sequences) } diff --git a/cli/packages/prisma-generate-schema/README.md b/cli/packages/prisma-generate-schema/README.md index 54bd336da0..0aa46473fc 100644 --- a/cli/packages/prisma-generate-schema/README.md +++ b/cli/packages/prisma-generate-schema/README.md @@ -25,7 +25,7 @@ const openCRUDSchema = generateCRUDSchemaString(modelInSDL) This section is intended for maintainers. -First, a datamodel is parsed using the [`DatamodelParser`](src/datamodel/parser.ts). Then, an OpenCRUD schema is created using the [`SchemaGenerator`](src/generator/default/schemaGenerator.ts). +First, a datamodel is parsed using the `Parser` class from `prisma-datamodel`, Then, an OpenCRUD schema is created using the [`SchemaGenerator`](src/generator/default/schemaGenerator.ts). The schema generator utilizes several other generators to generate the `mutation`, `query` and `subscription` objects and all corresponding types. A [`Generator`](src/generator/generator.ts) is usually responsible for a single type only but will access other generators to recursively build the schema. All generators which return object types implement lazy evaluation and caching of generated types via their base class. The generators can be configured using dependency injection, if needed, to switch out the implementation for certain types in the schema. diff --git a/cli/packages/prisma-generate-schema/scripts/regenerateSchema.ts b/cli/packages/prisma-generate-schema/scripts/regenerateSchema.ts index e982e5bfcf..bcd7ef2c61 100644 --- a/cli/packages/prisma-generate-schema/scripts/regenerateSchema.ts +++ b/cli/packages/prisma-generate-schema/scripts/regenerateSchema.ts @@ -6,8 +6,6 @@ const testNames = fs.readdirSync( path.join(__dirname, '../__tests__/blackbox/cases'), ) -console.log({ testNames }) - async function deployModel(service, stage, datamodel) { await fetch(`http://localhost:4466/management`, { method: 'POST', diff --git a/cli/packages/prisma-yml/src/PrismaDefinition.ts b/cli/packages/prisma-yml/src/PrismaDefinition.ts index 98a73fc1c2..22edea4020 100644 --- a/cli/packages/prisma-yml/src/PrismaDefinition.ts +++ b/cli/packages/prisma-yml/src/PrismaDefinition.ts @@ -47,11 +47,26 @@ export class PrismaDefinitionClass { this.envVars = envVars } async load(args: Args, envPath?: string, graceful?: boolean) { + if (args.project) { + const flagPath = path.resolve(args.project as string) + + if (!fs.pathExistsSync(flagPath)) { + throw new Error(`Prisma definition path specified by --project '${flagPath}' does not exist`) + } + + this.definitionPath = flagPath + this.definitionDir = path.dirname(flagPath) + await this.loadDefinition(args, graceful) + + this.validate() + return; + } + if (envPath) { if (!fs.pathExistsSync(envPath)) { envPath = path.join(process.cwd(), envPath) } - + if (!fs.pathExistsSync(envPath)) { throw new Error(`--env-file path '${envPath}' does not exist`) } diff --git a/server/Makefile b/server/Makefile index 050b56d65a..82d9de8ac5 100644 --- a/server/Makefile +++ b/server/Makefile @@ -17,14 +17,12 @@ dev-mongo: cp ./docker-compose/mongo/prisma.yml ./prisma.yml dev-sqlite: - docker-compose -f docker-compose/sqlite/dev-sqlite.yml up -d --remove-orphans cp ./docker-compose/sqlite/prisma.yml ./prisma.yml dev-down: docker-compose -f docker-compose/mysql/dev-mysql.yml down -v --remove-orphans docker-compose -f docker-compose/postgres/dev-postgres.yml down -v --remove-orphans docker-compose -f docker-compose/mongo/dev-mongo.yml down -v --remove-orphans - docker-compose -f docker-compose/sqlite/dev-sqlite.yml down -v --remove-orphans local-image: docker run -e "BRANCH=local" -e "COMMIT_SHA=local" -e "CLUSTER_VERSION=local" -v $(shell pwd):/root/build -w /root/build -v ~/.ivy2:/root/.ivy2 -v ~/.coursier:/root/.coursier -v /var/run/docker.sock:/var/run/docker.sock prismagraphql/build-image:debian sbt "project prisma-local" docker diff --git a/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/database/BuilderBase.scala b/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/database/BuilderBase.scala index 7c69376c1a..593cbcb2b2 100644 --- a/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/database/BuilderBase.scala +++ b/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/database/BuilderBase.scala @@ -19,6 +19,7 @@ trait BuilderBase extends JooqExtensions with JdbcExtensions with SlickExtension val isMySql = slickDatabase.isMySql val isPostgres = slickDatabase.isPostgres + val isSQLite = slickDatabase.isSQLite val sql = DSL.using(slickDatabase.dialect, new Settings().withRenderFormatted(true)) private val relayIdTableName = "_RelayId" diff --git a/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/database/ImportActions.scala b/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/database/ImportActions.scala index 29094c582a..8cf4c4929e 100644 --- a/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/database/ImportActions.scala +++ b/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/database/ImportActions.scala @@ -59,7 +59,7 @@ trait ImportActions extends BuilderBase with SharedJdbcExtensions { .toVector case e: Exception => - Vector(e.getCause.toString) + Vector(s"Failure inserting ${model.dbName}. Cause: ${e.getCause.toString}") } val relayResult: Vector[String] = try { @@ -89,7 +89,7 @@ trait ImportActions extends BuilderBase with SharedJdbcExtensions { .toVector case e: Exception => - Vector(e.getMessage) + Vector(s"Failure inserting RelayRow. Cause: ${e.getMessage}") } val res = nodeResult ++ relayResult @@ -165,7 +165,7 @@ trait ImportActions extends BuilderBase with SharedJdbcExtensions { .toVector case e: Exception => - Vector(e.getMessage) + Vector(s"Failure inserting into relationtable ${relation.relationTableName}. Cause: ${e.getMessage}") } if (res.nonEmpty) throw new Exception(res.mkString("-@-")) @@ -250,7 +250,7 @@ trait ImportActions extends BuilderBase with SharedJdbcExtensions { .toVector case e: Exception => - Vector(e.getMessage) + Vector(s"Failure inserting into listTable ${field.model.dbName}_${field.dbName}: Cause:${e.getMessage}") } if (res.nonEmpty) throw new Exception(res.mkString("-@-")) diff --git a/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/database/MiscActions.scala b/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/database/MiscActions.scala index 8c1ab1b993..20c7772c4f 100644 --- a/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/database/MiscActions.scala +++ b/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/database/MiscActions.scala @@ -10,28 +10,23 @@ trait MiscActions extends BuilderBase { val modelTables = project.models.map(modelTable) val listTables = project.models.flatMap(model => model.scalarListFields.map(scalarListTable)) - val actions = (relationTables ++ listTables ++ Vector(relayTable) ++ modelTables).map { table => - if (isMySql) { - truncateToDBIO(sql.truncate(table)) - } else { - truncateToDBIO(sql.truncate(table).cascade()) - } + val actions = (relationTables ++ listTables ++ Vector(relayTable) ++ modelTables).map { + case table if isMySql => truncateToDBIO(sql.truncate(table)) + case table => truncateToDBIO(sql.truncate(table).cascade()) } - val truncatesAction = DBIO.sequence(actions) - def disableForeignKeyChecks = SimpleDBIO { ctx => + lazy val disableForeignKeyChecks = SimpleDBIO { ctx => val ps = ctx.connection.prepareStatement("SET FOREIGN_KEY_CHECKS=0") ps.executeUpdate() } - def enableForeignKeyChecks = SimpleDBIO { ctx => + lazy val enableForeignKeyChecks = SimpleDBIO { ctx => val ps = ctx.connection.prepareStatement("SET FOREIGN_KEY_CHECKS=1") ps.executeUpdate() } - if (isMySql) { - DBIO.seq(disableForeignKeyChecks, truncatesAction, enableForeignKeyChecks) - } else { - truncatesAction - } + val truncatesAction = DBIO.sequence(actions) + + if (isMySql) DBIO.seq(disableForeignKeyChecks, truncatesAction, enableForeignKeyChecks) else truncatesAction } + } diff --git a/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/impl/JdbcDatabaseMutactionExecutor.scala b/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/impl/JdbcDatabaseMutactionExecutor.scala index 8377d0bfbd..336bb8646a 100644 --- a/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/impl/JdbcDatabaseMutactionExecutor.scala +++ b/server/connectors/api-connector-jdbc/src/main/scala/com/prisma/api/connector/jdbc/impl/JdbcDatabaseMutactionExecutor.scala @@ -7,6 +7,7 @@ import com.prisma.connector.shared.jdbc.SlickDatabase import com.prisma.gc_values.IdGCValue import com.prisma.shared.models.Project import play.api.libs.json.JsValue +import slick.dbio.DBIO._ import slick.jdbc.TransactionIsolation import scala.concurrent.{ExecutionContext, Future} @@ -20,7 +21,7 @@ case class JdbcDatabaseMutactionExecutor( override def executeRaw(project: Project, query: String): Future[JsValue] = { val action = JdbcActionsBuilder(project, slickDatabase).executeRaw(query) - slickDatabase.database.run(action) + runAttached(project, action) } override def executeTransactionally(mutaction: TopLevelDatabaseMutaction) = execute(mutaction, transactionally = true) @@ -35,31 +36,9 @@ case class JdbcDatabaseMutactionExecutor( case false => executeTopLevelMutaction(mutaction, actionsBuilder) } - if (slickDatabase.isMySql) { - slickDatabase.database.run(singleAction.withTransactionIsolation(TransactionIsolation.ReadCommitted)) - } else if (slickDatabase.isPostgres) { - slickDatabase.database.run(singleAction) - } else if (slickDatabase.isSQLite) { - import slickDatabase.profile.api._ - val list = sql"""PRAGMA database_list;""".as[(String, String, String)] - val path = s"""'db/${mutaction.project.dbName}'""" - val attach = sqlu"ATTACH DATABASE #${path} AS #${mutaction.project.dbName};" - val activateForeignKey = sqlu"""PRAGMA foreign_keys = ON;""" + val finalAction = if (slickDatabase.isMySql) singleAction.withTransactionIsolation(TransactionIsolation.ReadCommitted) else singleAction - val attachIfNecessary = for { - attachedDbs <- list - _ <- attachedDbs.map(_._2).contains(mutaction.project.dbName) match { - case true => DBIO.successful(()) - case false => attach - } - _ <- activateForeignKey - result <- singleAction - } yield result - - slickDatabase.database.run(attachIfNecessary.withPinnedSession) - } else { - sys.error("No valid database profile given.") - } + runAttached(mutaction.project, finalAction) } def executeTopLevelMutaction( @@ -145,4 +124,29 @@ case class JdbcDatabaseMutactionExecutor( case m: NestedUpdateNodes => NestedUpdateNodesInterpreter(m) case m: NestedDeleteNodes => NestedDeleteNodesInterpreter(m, shouldDeleteRelayIds = manageRelayIds) } + + private def runAttached[T](project: Project, query: DBIO[T]) = { + if (slickDatabase.isSQLite) { + import slickDatabase.profile.api._ + + val list = sql"""PRAGMA database_list;""".as[(String, String, String)] + val path = s"""'db/${project.dbName}'""" + val attach = sqlu"ATTACH DATABASE #${path} AS #${project.dbName};" + val activateForeignKey = sqlu"""PRAGMA foreign_keys = ON;""" + + val attachIfNecessary = for { + attachedDbs <- list + _ <- attachedDbs.map(_._2).contains(project.dbName) match { + case true => slick.dbio.DBIO.successful(()) + case false => attach + } + _ <- activateForeignKey + result <- query + } yield result + + slickDatabase.database.run(attachIfNecessary.withPinnedSession) + } else { + slickDatabase.database.run(query) + } + } } diff --git a/server/connectors/api-connector-sqlite/src/main/scala/com/prisma/api/connector/sqlite/SQLiteApiConnector.scala b/server/connectors/api-connector-sqlite/src/main/scala/com/prisma/api/connector/sqlite/SQLiteApiConnector.scala index 6f506957f8..c70d7d8b0c 100644 --- a/server/connectors/api-connector-sqlite/src/main/scala/com/prisma/api/connector/sqlite/SQLiteApiConnector.scala +++ b/server/connectors/api-connector-sqlite/src/main/scala/com/prisma/api/connector/sqlite/SQLiteApiConnector.scala @@ -1,5 +1,7 @@ package com.prisma.api.connector.sqlite +import java.sql.Driver + import com.prisma.api.connector.jdbc.impl.{JdbcDataResolver, JdbcDatabaseMutactionExecutor} import com.prisma.api.connector.{ApiConnector, DatabaseMutactionExecutor} import com.prisma.config.DatabaseConfig @@ -7,8 +9,8 @@ import com.prisma.shared.models.{ConnectorCapabilities, ConnectorCapability, Pro import scala.concurrent.{ExecutionContext, Future} -case class SQLiteApiConnector(config: DatabaseConfig, isPrototype: Boolean)(implicit ec: ExecutionContext) extends ApiConnector { - lazy val databases = SQLiteDatabasesFactory.initialize(config) +case class SQLiteApiConnector(config: DatabaseConfig, driver: Driver, isPrototype: Boolean)(implicit ec: ExecutionContext) extends ApiConnector { + lazy val databases = SQLiteDatabasesFactory.initialize(config, driver) override def initialize() = { databases diff --git a/server/connectors/api-connector-sqlite/src/main/scala/com/prisma/api/connector/sqlite/SQLiteDatabasesFactory.scala b/server/connectors/api-connector-sqlite/src/main/scala/com/prisma/api/connector/sqlite/SQLiteDatabasesFactory.scala index bbd0066f82..7302857a92 100644 --- a/server/connectors/api-connector-sqlite/src/main/scala/com/prisma/api/connector/sqlite/SQLiteDatabasesFactory.scala +++ b/server/connectors/api-connector-sqlite/src/main/scala/com/prisma/api/connector/sqlite/SQLiteDatabasesFactory.scala @@ -1,5 +1,7 @@ package com.prisma.api.connector.sqlite +import java.sql.Driver + import com.prisma.config.DatabaseConfig import com.prisma.connector.shared.jdbc.{Databases, SlickDatabase} import com.typesafe.config.{Config, ConfigFactory} @@ -7,31 +9,11 @@ import slick.jdbc.SQLiteProfile import slick.jdbc.SQLiteProfile.api._ object SQLiteDatabasesFactory { - private lazy val dbDriver = new org.sqlite.JDBC - def initialize(dbConfig: DatabaseConfig): Databases = { -// val config = typeSafeConfigFromDatabaseConfig(dbConfig) - val masterDb = Database.forURL("jdbc:sqlite:database.db", driver = "org.sqlite.JDBC") + def initialize(dbConfig: DatabaseConfig, driver: Driver): Databases = { + val masterDb = Database.forDriver(driver, "jdbc:sqlite:database.db") val slickDatabase: SlickDatabase = SlickDatabase(SQLiteProfile, masterDb) Databases(primary = slickDatabase, replica = slickDatabase) } - -// def typeSafeConfigFromDatabaseConfig(dbConfig: DatabaseConfig): Config = { -// ConfigFactory -// .parseString(s""" -// |database { -// | connectionInitSql="set names utf8mb4;" -// | dataSourceClass = "slick.jdbc.DriverDataSource" -// | properties { -// | url = "jdbc:mysql://${dbConfig.host}:${dbConfig.port}/?autoReconnect=true&useSSL=${dbConfig.ssl}&requireSSL=false&serverTimeZone=UTC&useUnicode=true&characterEncoding=UTF-8&socketTimeout=60000&usePipelineAuth=false&cachePrepStmts=true" -// | user = "${dbConfig.user}" -// | password = "${dbConfig.password.getOrElse("")}" -// | } -// | numThreads = ${dbConfig.connectionLimit.getOrElse(10) - 1} // we subtract 1 because one connection is consumed already by deploy -// | connectionTimeout = 5000 -// |} -// """.stripMargin) -// .resolve -// } } diff --git a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/DatabaseInspectorBase.scala b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/DatabaseInspectorBase.scala index 1115255411..48c92c68db 100644 --- a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/DatabaseInspectorBase.scala +++ b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/DatabaseInspectorBase.scala @@ -1,9 +1,9 @@ package com.prisma.deploy.connector.jdbc -import com.prisma.connector.shared.jdbc.{MySqlDialect, PostgresDialect, SlickDatabase, SqliteDialect} +import com.prisma.connector.shared.jdbc.{MySqlDialect, PostgresDialect, SlickDatabase} import com.prisma.deploy.connector._ -import com.prisma.shared.models.TypeIdentifier -import com.prisma.shared.models.TypeIdentifier.{ScalarTypeIdentifier, TypeIdentifier} +import com.prisma.deploy.connector.jdbc.DatabaseInspectorBase.{IntrospectedColumn, IntrospectedForeignKey, IntrospectedSequence} +import com.prisma.shared.models.TypeIdentifier.ScalarTypeIdentifier import slick.dbio.DBIO import slick.jdbc.GetResult @@ -12,14 +12,8 @@ import scala.concurrent.{ExecutionContext, Future} trait DatabaseInspectorBase extends DatabaseInspector { val db: SlickDatabase implicit def ec: ExecutionContext - import db.profile.api.actionBasedSQLInterpolation - // intermediate helper classes - case class IntrospectedColumn(name: String, udtName: String, default: String, isNullable: Boolean) - case class IntrospectedForeignKey(name: String, table: String, column: String, referencedTable: String, referencedColumn: String) - case class IntrospectedSequence(column: String, name: String, current: Int) - override def inspect(schema: String): Future[DatabaseSchema] = db.database.run(action(schema)) def action(schema: String): DBIO[DatabaseSchema] = { @@ -111,6 +105,23 @@ trait DatabaseInspectorBase extends DatabaseInspector { ) } + /** + * Other Helpers + */ + private val dataTypeColumn: String = db.prismaDialect match { + case PostgresDialect => "udt_name" + case MySqlDialect => "DATA_TYPE" + case x => sys.error(s"$x is not implemented yet.") + } +} + +object DatabaseInspectorBase { + + // intermediate helper classes + case class IntrospectedColumn(name: String, udtName: String, default: String, isNullable: Boolean) + case class IntrospectedForeignKey(name: String, table: String, column: String, referencedTable: String, referencedColumn: String) + case class IntrospectedSequence(column: String, name: String, current: Int) + implicit val introspectedForeignKeyGetResult: GetResult[IntrospectedForeignKey] = GetResult { pr => IntrospectedForeignKey( name = pr.rs.getString("fkConstraintName"), @@ -130,12 +141,4 @@ trait DatabaseInspectorBase extends DatabaseInspector { ) } - /** - * Other Helpers - */ - private val dataTypeColumn = db.prismaDialect match { - case PostgresDialect => "udt_name" - case MySqlDialect => "DATA_TYPE" - case x => sys.error(s"$x is not implemented yet.") - } } diff --git a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/MySqlDatabaseInspector.scala b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/MySqlDatabaseInspector.scala index 4a59f5f6c8..833f2d29dc 100644 --- a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/MySqlDatabaseInspector.scala +++ b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/MySqlDatabaseInspector.scala @@ -1,6 +1,7 @@ package com.prisma.deploy.connector.jdbc import com.prisma.connector.shared.jdbc.SlickDatabase import com.prisma.deploy.connector.Index +import com.prisma.deploy.connector.jdbc.DatabaseInspectorBase._ import com.prisma.shared.models.TypeIdentifier import slick.dbio.DBIO diff --git a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/PostgresDatabaseInspector.scala b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/PostgresDatabaseInspector.scala index d76621f9c6..8f4c1d5a65 100644 --- a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/PostgresDatabaseInspector.scala +++ b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/PostgresDatabaseInspector.scala @@ -1,6 +1,7 @@ package com.prisma.deploy.connector.jdbc import com.prisma.connector.shared.jdbc.SlickDatabase import com.prisma.deploy.connector.Index +import com.prisma.deploy.connector.jdbc.DatabaseInspectorBase._ import com.prisma.shared.models.TypeIdentifier import slick.dbio.DBIO diff --git a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/SQLiteDatabaseInspector.scala b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/SQLiteDatabaseInspector.scala new file mode 100644 index 0000000000..f38e4e37cf --- /dev/null +++ b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/SQLiteDatabaseInspector.scala @@ -0,0 +1,202 @@ +package com.prisma.deploy.connector.jdbc + +import com.prisma.connector.shared.jdbc.{MySqlDialect, PostgresDialect, SlickDatabase, SqliteDialect} +import com.prisma.deploy.connector._ +import com.prisma.deploy.connector.jdbc.DatabaseInspectorBase.{IntrospectedColumn, IntrospectedForeignKey} +import com.prisma.shared.models.TypeIdentifier +import slick.dbio.DBIO +import slick.jdbc.GetResult + +import scala.concurrent.{ExecutionContext, Future} + +case class SQLiteDatabaseInspector(db: SlickDatabase)(implicit val ec: ExecutionContext) extends DatabaseInspector { + import db.profile.api.actionBasedSQLInterpolation + + //attach here + + override def inspect(schema: String): Future[DatabaseSchema] = { + val list = sql"""PRAGMA database_list;""".as[(String, String, String)] + val path = s"""'db/$schema'""" + val attach = sqlu"ATTACH DATABASE #${path} AS #${schema};" + + val attachIfNecessary = for { + attachedDbs <- list + _ <- attachedDbs.map(_._2).contains(schema) match { + case true => slick.dbio.DBIO.successful(()) + case false => attach + } + result <- action(schema) + } yield result + db.database.run(attachIfNecessary.withPinnedSession) + } + + def action(schema: String): DBIO[DatabaseSchema] = { + for { + tableNames <- getTableNames(schema) + tables <- DBIO.sequence(tableNames.map(name => getTable(schema, name))) + } yield { + DatabaseSchema(tables) + } + } + + private def getTableNames(schema: String): DBIO[Vector[String]] = { + sql""" + |SELECT + | name + |FROM + | #${schema}.sqlite_master + |WHERE + | type='table' + """.stripMargin.as[String] + } + + private def getTable(schema: String, table: String): DBIO[Table] = { + for { + introspectedColumns <- getColumns(schema, table) + introspectedForeignKeys <- foreignKeyConstraints(schema, table) +// introspectedIndexes <- indexes(schema, table) +// sequences <- getSequences(schema, table) + } yield { + val columns = introspectedColumns.map { col => + // this needs to be extended further in the future if we support arbitrary SQL types + val typeIdentifier = typeIdentifierForTypeName(col.udtName).getOrElse { + sys.error(s"Encountered unknown SQL type ${col.udtName} with column ${col.name}. $col") + } + val fk: Option[ForeignKey] = introspectedForeignKeys.find(fk => fk.column == col.name).map { fk => + ForeignKey(fk.referencedTable, fk.referencedColumn) + } + val sequence: Option[Sequence] = None + +//sequences.find(_.column == col.name).map { mseq => +// Sequence(mseq.name, mseq.current) +// } + + Column( + name = col.name, + tpe = col.udtName, + typeIdentifier = typeIdentifier, + isRequired = col.isNullable, + foreignKey = fk, + sequence = sequence + )(_) + } + Table(table, columns, indexes = Vector.empty) // introspectedIndexes) + } + } + + private def getColumns(schema: String, table: String): DBIO[Vector[IntrospectedColumn]] = { + sql"""Pragma "#$schema".table_info ("#$table")""".stripMargin.as[IntrospectedColumn] + } + + /** + * RESULT CONVERTERS + */ + implicit lazy val introspectedColumnGetResult: GetResult[IntrospectedColumn] = GetResult { ps => + IntrospectedColumn( + name = ps.rs.getString("name"), + udtName = ps.rs.getString(dataTypeColumn), + default = ps.rs.getString("dflt_value"), + isNullable = ps.rs.getBoolean("notnull") + ) + } + + /** + * Other Helpers + */ + private val dataTypeColumn = db.prismaDialect match { + case PostgresDialect => "udt_name" + case MySqlDialect => "DATA_TYPE" + case SqliteDialect => "type" + case x => sys.error(s"$x is not implemented yet.") + } + + private def typeIdentifierForTypeName(typeName: String): Option[TypeIdentifier.ScalarTypeIdentifier] = { + typeName match { + case "boolean" => Some(TypeIdentifier.Boolean) + case "bool" => Some(TypeIdentifier.Boolean) + case _ if typeName.contains("char") => Some(TypeIdentifier.String) + case _ if typeName.contains("CHAR") => Some(TypeIdentifier.String) + case _ if typeName.contains("text") => Some(TypeIdentifier.String) + case "int" | "INT" | "INT(4)" | "INTEGER" => Some(TypeIdentifier.Int) + case _ if typeName.contains("datetime") => Some(TypeIdentifier.DateTime) + case "decimal" | "numeric" | "float" | "double" | "Decimal(65,30)" => Some(TypeIdentifier.Float) + case _ => None + } + } + +// private def getSequences(schema: String, table: String): DBIO[Vector[IntrospectedSequence]] = { +// val sequencesForTable = +// sql""" +// |SELECT +// | column_name +// |FROM +// | information_schema.COLUMNS +// |WHERE +// | extra = 'auto_increment' +// | AND table_name = $table +// | AND table_schema = $schema; +// """.stripMargin.as[String] +// +// val currentValueForSequence = +// sql""" +// |SELECT +// | AUTO_INCREMENT +// |FROM +// | information_schema.TABLES +// |WHERE +// | table_name = $table +// | AND table_schema = $schema; +// """.stripMargin.as[Int] +// +// for { +// sequences <- sequencesForTable +// currentValues <- currentValueForSequence +// } yield { +// val x = for { +// column <- sequences.headOption +// currentValue <- currentValues.headOption +// } yield IntrospectedSequence(column = column, name = "sequences_are_not_named_in_mysql", current = currentValue) +// x.toVector +// } +// } +// + private def foreignKeyConstraints(schema: String, table: String): DBIO[Vector[IntrospectedForeignKey]] = { + implicit val introspectedForeignKeyGetResult: GetResult[IntrospectedForeignKey] = GetResult { pr => + IntrospectedForeignKey( + name = table ++ "_" ++ pr.rs.getString("from") ++ "_fk", + table = table, + column = pr.rs.getString("from"), + referencedTable = pr.rs.getString("table"), + referencedColumn = pr.rs.getString("to") + ) + } + + sql"""Pragma "#$schema".foreign_key_list("#$table");""".stripMargin.as[IntrospectedForeignKey] + } +// +// private def indexes(schema: String, table: String): DBIO[Vector[Index]] = { +// sql""" +// |SELECT +// | table_name, +// | index_name, +// | GROUP_CONCAT(DISTINCT column_name SEPARATOR ', ') AS column_names, +// | NOT non_unique AS is_unique, +// | index_name = 'PRIMARY' AS is_primary_key +// |FROM +// | information_schema.statistics +// |WHERE +// | table_schema = $schema +// | AND table_name = $table +// |GROUP BY +// | table_name, index_name, non_unique +// """.stripMargin.as[(String, String, String, Boolean, Boolean)].map { rows => +// rows.map { row => +// Index( +// name = row._2, +// columns = row._3.split(',').map(_.trim).toVector, +// unique = row._4 +// ) +// } +// } +// } +} diff --git a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/ColumnMutactionInterpreters.scala b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/ColumnMutactionInterpreters.scala index cde0ebbebf..1149be3966 100644 --- a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/ColumnMutactionInterpreters.scala +++ b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/ColumnMutactionInterpreters.scala @@ -1,51 +1,33 @@ package com.prisma.deploy.connector.jdbc.database import com.prisma.deploy.connector._ -import slick.jdbc.PostgresProfile.api._ +import com.prisma.shared.models.TypeIdentifier.ScalarTypeIdentifier +import com.prisma.shared.models.{Project, ScalarField} import com.prisma.utils.boolean.BooleanUtils._ +import slick.jdbc.PostgresProfile.api._ case class CreateColumnInterpreter(builder: JdbcDeployDatabaseMutationBuilder) extends SqlMutactionInterpreter[CreateColumn] { - // todo: that does not consider unique constraints yet override def execute(mutaction: CreateColumn, schemaBeforeMigration: DatabaseSchema) = { schemaBeforeMigration.table(mutaction.model.dbName).flatMap(_.column(mutaction.field.dbName)) match { case None => - builder.createColumn( - mutaction.project, - tableName = mutaction.model.dbName, - columnName = mutaction.field.dbName, - isRequired = mutaction.field.isRequired, - isUnique = mutaction.field.isUnique, - isList = mutaction.field.isList, - typeIdentifier = mutaction.field.typeIdentifier - ) - + CreateColumnHelper.withIndexIfNecessary(builder, mutaction.project, mutaction.field) // This can only happen if an inline relation field has been converted into a scalar field. The foreign key indicates it is a relation column. // Our step order ensures that this relation column already has been deleted. So we just create the scalar column. case Some(c) if c.foreignKey.isDefined => - builder.createColumn( - mutaction.project, - tableName = mutaction.model.dbName, - columnName = mutaction.field.dbName, - isRequired = mutaction.field.isRequired, - isUnique = mutaction.field.isUnique, - isList = mutaction.field.isList, - typeIdentifier = mutaction.field.typeIdentifier - ) + CreateColumnHelper.withIndexIfNecessary(builder, mutaction.project, mutaction.field) case Some(c) => val updateColumn = mustUpdateColumn(c, mutaction).toOption { builder.updateColumn( mutaction.project, - model = mutaction.model, + mutaction.field, + oldTableName = c.table.name, oldColumnName = mutaction.field.dbName, - newColumnName = mutaction.field.dbName, - newIsRequired = mutaction.field.isRequired, - newIsList = mutaction.field.isList, - newTypeIdentifier = mutaction.field.typeIdentifier + oldTypeIdentifier = c.typeIdentifier.asInstanceOf[ScalarTypeIdentifier] ) } val addUniqueConstraint = mustAddUniqueConstraint(c, mutaction).toOption { - builder.addUniqueConstraint(mutaction.project, mutaction.model.dbName, mutaction.field.dbName, mutaction.field.typeIdentifier) + builder.addUniqueConstraint(mutaction.project, mutaction.field) } val removeUniqueConstraint = mustRemoveUniqueConstraint(c, mutaction).toOption { val index = c.table.indexByColumns_!(c.name) @@ -59,7 +41,7 @@ case class CreateColumnInterpreter(builder: JdbcDeployDatabaseMutationBuilder) e private def mustUpdateColumn(column: Column, mutaction: CreateColumn) = { column.typeIdentifier != mutaction.field.typeIdentifier || - column.isRequired == mutaction.field.isRequired + column.isRequired != mutaction.field.isRequired } private def mustAddUniqueConstraint(column: Column, mutaction: CreateColumn) = { @@ -74,46 +56,24 @@ case class CreateColumnInterpreter(builder: JdbcDeployDatabaseMutationBuilder) e override def rollback(mutaction: CreateColumn, schemaBeforeMigration: DatabaseSchema) = { schemaBeforeMigration.table(mutaction.model.dbName).flatMap(_.column(mutaction.field.dbName)) match { - case None => - builder.deleteColumn( - mutaction.project, - tableName = mutaction.model.dbName, - columnName = mutaction.field.dbName - ) - case Some(_) => - DBIO.successful(()) + case None => builder.deleteColumn(mutaction.project, mutaction.model.dbName, mutaction.field.dbName, Some(mutaction.model)) //Fixme + case Some(_) => DBIO.successful(()) } } } case class DeleteColumnInterpreter(builder: JdbcDeployDatabaseMutationBuilder) extends SqlMutactionInterpreter[DeleteColumn] { override def execute(mutaction: DeleteColumn, schemaBeforeMigration: DatabaseSchema) = { - schemaBeforeMigration.table(mutaction.model.dbName).flatMap(_.column(mutaction.field.dbName)) match { - case Some(_) => - builder.deleteColumn( - mutaction.project, - tableName = mutaction.model.dbName, - columnName = mutaction.field.dbName - ) - case None => - DBIO.successful(()) + schemaBeforeMigration.table(mutaction.oldModel.dbName).flatMap(_.column(mutaction.field.dbName)) match { + case Some(_) => builder.deleteColumn(mutaction.project, mutaction.oldModel.dbName, mutaction.field.dbName, Some(mutaction.oldModel)) + case None => DBIO.successful(()) } } override def rollback(mutaction: DeleteColumn, schemaBeforeMigration: DatabaseSchema) = { - schemaBeforeMigration.table(mutaction.model.dbName).flatMap(_.column(mutaction.field.dbName)) match { - case Some(_) => - builder.createColumn( - mutaction.project, - tableName = mutaction.model.dbName, - columnName = mutaction.field.dbName, - isRequired = mutaction.field.isRequired, - isUnique = mutaction.field.isUnique, - isList = mutaction.field.isList, - typeIdentifier = mutaction.field.typeIdentifier - ) - case None => - DBIO.successful(()) + schemaBeforeMigration.table(mutaction.oldModel.dbName).flatMap(_.column(mutaction.field.dbName)) match { + case Some(_) => CreateColumnHelper.withIndexIfNecessary(builder, mutaction.project, mutaction.field) + case None => DBIO.successful(()) } } } @@ -150,29 +110,17 @@ case class UpdateColumnInterpreter(builder: JdbcDeployDatabaseMutationBuilder) e } private def updateFromBeforeStateToAfterState(mutaction: UpdateColumn, schemaBeforeMigration: DatabaseSchema): DBIO[_] = { - val before = mutaction.oldField - val after = mutaction.newField - val indexMustBeRecreated = before.isRequired != after.isRequired || before.dbName != after.dbName || before.typeIdentifier != after.typeIdentifier - - def updateColumn = builder.updateColumn( - mutaction.project, - model = mutaction.model, - oldColumnName = before.dbName, - newColumnName = after.dbName, - newIsRequired = after.isRequired, - newIsList = after.isList, - newTypeIdentifier = after.typeIdentifier - ) - - def createColumn = builder.createColumn( - mutaction.project, - tableName = mutaction.model.dbName, - columnName = after.dbName, - isRequired = after.isRequired, - isUnique = after.isUnique, - isList = after.isList, - typeIdentifier = after.typeIdentifier - ) + val before = mutaction.oldField + val after = mutaction.newField + val typeChanges = before.typeIdentifier != after.typeIdentifier + val requiredChanges = before.isRequired != after.isRequired + + def updateColumn = + builder.updateColumn(mutaction.project, + after, + oldTableName = before.model.dbName, + oldColumnName = before.dbName, + oldTypeIdentifier = before.typeIdentifier) def removeUniqueConstraint = builder.removeIndex( mutaction.project, @@ -180,24 +128,33 @@ case class UpdateColumnInterpreter(builder: JdbcDeployDatabaseMutationBuilder) e indexName = schemaBeforeMigration.table_!(mutaction.model.dbName).indexByColumns_!(before.dbName).name ) - def addUniqueConstraint = builder.addUniqueConstraint( - mutaction.project, - tableName = mutaction.model.dbName, - columnName = after.dbName, - typeIdentifier = after.typeIdentifier - ) - - def updateColumnActions = (before.isUnique, indexMustBeRecreated, after.isUnique) match { - case (true, true, true) => Vector(removeUniqueConstraint, updateColumn, addUniqueConstraint) - case (true, _, false) => Vector(removeUniqueConstraint, updateColumn) - case (true, false, true) => Vector(updateColumn) - case (false, _, false) => Vector(updateColumn) - case (false, _, true) => Vector(updateColumn, addUniqueConstraint) + def addUniqueConstraint = builder.addUniqueConstraint(mutaction.project, after) + + def updateColumnActions = (before.isUnique, typeChanges, requiredChanges, after.isUnique) match { + // type changes, after unique + case (_, true, _, true) => Vector(updateColumn, addUniqueConstraint) + //type changes, after not unique + case (_, true, _, false) => Vector(updateColumn) + // type does not change, after is unique + case (true, false, true, true) => Vector(removeUniqueConstraint, updateColumn, addUniqueConstraint) + case (false, false, _, true) => Vector(updateColumn, addUniqueConstraint) + case (true, false, false, true) => Vector(updateColumn) + // type does not change, after is not unique + case (true, false, _, false) => Vector(removeUniqueConstraint, updateColumn) + case (false, false, _, false) => Vector(updateColumn) } schemaBeforeMigration.table(mutaction.model.dbName).flatMap(_.column(before.dbName)) match { case Some(_) => DBIO.seq(updateColumnActions: _*) - case None => createColumn + case None => CreateColumnHelper.withIndexIfNecessary(builder, mutaction.project, mutaction.newField) } } + +} +object CreateColumnHelper { + def withIndexIfNecessary(builder: JdbcDeployDatabaseMutationBuilder, project: Project, field: ScalarField) = { + val createColumn = builder.createColumn(project, field) + val addUniqueConstraint = if (field.isUnique) builder.addUniqueConstraint(project, field) else DBIO.successful(()) + DBIO.seq(createColumn, addUniqueConstraint) + } } diff --git a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/JdbcDeployDatabaseMutationBuilder.scala b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/JdbcDeployDatabaseMutationBuilder.scala index ed7a223283..3983c97d96 100644 --- a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/JdbcDeployDatabaseMutationBuilder.scala +++ b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/JdbcDeployDatabaseMutationBuilder.scala @@ -3,8 +3,8 @@ package com.prisma.deploy.connector.jdbc.database import com.prisma.connector.shared.jdbc.SlickDatabase import com.prisma.deploy.connector.DatabaseSchema import com.prisma.deploy.connector.jdbc.JdbcBase -import com.prisma.shared.models.TypeIdentifier.ScalarTypeIdentifier -import com.prisma.shared.models.{Model, Project, Relation, TypeIdentifier} +import com.prisma.shared.models.TypeIdentifier.{ScalarTypeIdentifier, TypeIdentifier} +import com.prisma.shared.models._ import org.jooq.impl.DSL._ import org.jooq.impl.SQLDataType import slick.dbio.DBIO @@ -23,46 +23,32 @@ trait JdbcDeployDatabaseMutationBuilder extends JdbcBase { def createSchema(projectId: String): DBIO[_] def truncateProjectTables(project: Project): DBIO[_] def deleteProjectDatabase(projectId: String): DBIO[_] + //UpdateModelTable -> for PostGres and MySQL this is just rename + // -> for SQLite this does everything??? + def renameTable(project: Project, currentName: String, newName: String): DBIO[_] - def addUniqueConstraint(project: Project, tableName: String, columnName: String, typeIdentifier: ScalarTypeIdentifier): DBIO[_] + def addUniqueConstraint(project: Project, field: Field): DBIO[_] def removeIndex(project: Project, tableName: String, indexName: String): DBIO[_] - def createModelTable(project: Project, model: Model): DBIO[_] def createScalarListTable(project: Project, model: Model, fieldName: String, typeIdentifier: ScalarTypeIdentifier): DBIO[_] def createRelationTable(project: Project, relation: Relation): DBIO[_] def createRelationColumn(project: Project, model: Model, references: Model, column: String): DBIO[_] - def createColumn( - project: Project, - tableName: String, - columnName: String, - isRequired: Boolean, - isUnique: Boolean, - isList: Boolean, - typeIdentifier: TypeIdentifier.ScalarTypeIdentifier - ): DBIO[_] - - def updateColumn( - project: Project, - model: Model, - oldColumnName: String, - newColumnName: String, - newIsRequired: Boolean, - newIsList: Boolean, - newTypeIdentifier: ScalarTypeIdentifier - ): DBIO[_] - + def createColumn(project: Project, field: ScalarField): DBIO[_] + def updateColumn(project: Project, field: ScalarField, oldTableName: String, oldColumnName: String, oldTypeIdentifier: ScalarTypeIdentifier): DBIO[_] def deleteRelationColumn(project: Project, model: Model, references: Model, column: String): DBIO[_] def deleteColumn(project: Project, tableName: String, columnName: String, model: Option[Model] = None): DBIO[_] - def renameColumn(project: Project, tableName: String, oldColumnName: String, newColumnName: String): DBIO[_] + def renameColumn(project: Project, tableName: String, oldColumnName: String, newColumnName: String, typeIdentifier: TypeIdentifier): DBIO[_] /* * Connector-agnostic functions */ def updateRelationTable(project: Project, previousRelation: Relation, nextRelation: Relation) = { + val modelAIdType = nextRelation.modelA.idField_!.typeIdentifier + val modelBIdType = nextRelation.modelB.idField_!.typeIdentifier val addOrRemoveId = addOrRemoveIdColumn(project, previousRelation, nextRelation) - val renameModelAColumn = renameColumn(project, previousRelation.relationTableName, previousRelation.modelAColumn, nextRelation.modelAColumn) - val renameModelBColumn = renameColumn(project, previousRelation.relationTableName, previousRelation.modelBColumn, nextRelation.modelBColumn) + val renameModelAColumn = renameColumn(project, previousRelation.relationTableName, previousRelation.modelAColumn, nextRelation.modelAColumn, modelAIdType) + val renameModelBColumn = renameColumn(project, previousRelation.relationTableName, previousRelation.modelBColumn, nextRelation.modelBColumn, modelBIdType) val renameTableStatement = renameTable(project, previousRelation.relationTableName, nextRelation.relationTableName) val all = Vector(addOrRemoveId, renameModelAColumn, renameModelBColumn, renameTableStatement) diff --git a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/JdbcDeployMutactionExecutor.scala b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/JdbcDeployMutactionExecutor.scala index 2f9f4af66f..2c42217ffe 100644 --- a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/JdbcDeployMutactionExecutor.scala +++ b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/JdbcDeployMutactionExecutor.scala @@ -45,7 +45,7 @@ case class JdbcDeployMutactionExecutor(builder: JdbcDeployDatabaseMutationBuilde case x: UpdateScalarListTable => UpdateScalarListInterpreter(builder).execute(x, schemaBeforeMigration) case x: DeleteScalarListTable => DeleteScalarListInterpreter(builder).execute(x, schemaBeforeMigration) case x: CreateModelTable => CreateModelInterpreter(builder).execute(x, schemaBeforeMigration) - case x: RenameTable => RenameModelInterpreter(builder).execute(x, schemaBeforeMigration) + case x: UpdateModelTable => UpdateModelInterpreter(builder).execute(x, schemaBeforeMigration) case x: DeleteModelTable => DeleteModelInterpreter(builder).execute(x, schemaBeforeMigration) case x: CreateRelationTable => CreateRelationInterpreter(builder).execute(x, schemaBeforeMigration) case x: UpdateRelationTable => UpdateRelationInterpreter(builder).execute(x, schemaBeforeMigration) @@ -68,7 +68,7 @@ case class JdbcDeployMutactionExecutor(builder: JdbcDeployDatabaseMutationBuilde case x: UpdateScalarListTable => UpdateScalarListInterpreter(builder).rollback(x, schemaBeforeMigration) case x: DeleteScalarListTable => DeleteScalarListInterpreter(builder).rollback(x, schemaBeforeMigration) case x: CreateModelTable => CreateModelInterpreter(builder).rollback(x, schemaBeforeMigration) - case x: RenameTable => RenameModelInterpreter(builder).rollback(x, schemaBeforeMigration) + case x: UpdateModelTable => UpdateModelInterpreter(builder).rollback(x, schemaBeforeMigration) case x: DeleteModelTable => DeleteModelInterpreter(builder).rollback(x, schemaBeforeMigration) case x: CreateRelationTable => CreateRelationInterpreter(builder).rollback(x, schemaBeforeMigration) case x: UpdateRelationTable => UpdateRelationInterpreter(builder).rollback(x, schemaBeforeMigration) diff --git a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/ModelMutactionInterpreters.scala b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/ModelMutactionInterpreters.scala index 3e1b549b43..f4575ee9ca 100644 --- a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/ModelMutactionInterpreters.scala +++ b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/ModelMutactionInterpreters.scala @@ -1,6 +1,7 @@ package com.prisma.deploy.connector.jdbc.database -import com.prisma.deploy.connector.{CreateModelTable, DatabaseSchema, DeleteModelTable, RenameTable} +import com.prisma.deploy.connector._ +import com.prisma.shared.models.Model import slick.dbio.{DBIOAction, Effect, NoStream} import slick.jdbc.PostgresProfile.api._ @@ -8,20 +9,16 @@ case class CreateModelInterpreter(builder: JdbcDeployDatabaseMutationBuilder) ex override def execute(mutaction: CreateModelTable, schemaBeforeMigration: DatabaseSchema): DBIOAction[Any, NoStream, Effect.All] = { schemaBeforeMigration.table(mutaction.model.dbName) match { - case None => - builder.createModelTable(mutaction.project, mutaction.model) - case Some(_) => - DBIO.successful(()) + case None => builder.createModelTable(mutaction.project, mutaction.model) + case Some(_) => DBIO.successful(()) } } override def rollback(mutaction: CreateModelTable, schemaBeforeMigration: DatabaseSchema) = { // only drop the table if it was created in the step before schemaBeforeMigration.table(mutaction.model.dbName) match { - case None => - builder.dropTable(mutaction.project, tableName = mutaction.model.dbName) - case Some(_) => - DBIO.successful(()) + case None => builder.dropTable(mutaction.project, tableName = mutaction.model.dbName) + case Some(_) => DBIO.successful(()) } } } @@ -31,10 +28,8 @@ case class DeleteModelInterpreter(builder: JdbcDeployDatabaseMutationBuilder) ex override def execute(mutaction: DeleteModelTable, schemaBeforeMigration: DatabaseSchema) = { val droppingTable = schemaBeforeMigration.table(mutaction.model.dbName) match { - case Some(_) => - builder.dropTable(mutaction.project, tableName = mutaction.model.dbName) - case None => - DBIO.successful(()) + case Some(_) => builder.dropTable(mutaction.project, tableName = mutaction.model.dbName) + case None => DBIO.successful(()) } val dropScalarListFields = mutaction.scalarListFields.map { field => @@ -47,23 +42,21 @@ case class DeleteModelInterpreter(builder: JdbcDeployDatabaseMutationBuilder) ex override def rollback(mutaction: DeleteModelTable, schemaBeforeMigration: DatabaseSchema) = { // only recreate the table if it was actually deleted in the step before val recreatingTable = schemaBeforeMigration.table(mutaction.model.dbName) match { - case Some(_) => - builder.createModelTable(mutaction.project, model = mutaction.model) - case None => - DBIO.successful(()) + case Some(_) => builder.createModelTable(mutaction.project, model = mutaction.model) + case None => DBIO.successful(()) } recreatingTable } } -case class RenameModelInterpreter(builder: JdbcDeployDatabaseMutationBuilder) extends SqlMutactionInterpreter[RenameTable] { - override def execute(mutaction: RenameTable, schemaBeforeMigration: DatabaseSchema) = setName(mutaction, mutaction.previousName, mutaction.nextName) - override def rollback(mutaction: RenameTable, schemaBeforeMigration: DatabaseSchema) = setName(mutaction, mutaction.nextName, mutaction.previousName) +case class UpdateModelInterpreter(builder: JdbcDeployDatabaseMutationBuilder) extends SqlMutactionInterpreter[UpdateModelTable] { + override def execute(mutaction: UpdateModelTable, schemaBeforeMigration: DatabaseSchema) = setName(mutaction, mutaction.oldModel, mutaction.newModel) + override def rollback(mutaction: UpdateModelTable, schemaBeforeMigration: DatabaseSchema) = setName(mutaction, mutaction.newModel, mutaction.oldModel) - private def setName(mutaction: RenameTable, previousName: String, nextName: String): DBIOAction[Any, NoStream, Effect.All] = { - val changeModelTableName = builder.renameTable(mutaction.project, currentName = previousName, newName = nextName) - val changeScalarListFieldTableNames = mutaction.scalarListFieldsNames.map { fieldName => - builder.renameScalarListTable(mutaction.project, previousName, fieldName, nextName, fieldName) + private def setName(mutaction: UpdateModelTable, oldModel: Model, newModel: Model): DBIOAction[Any, NoStream, Effect.All] = { + val changeModelTableName = builder.renameTable(mutaction.project, currentName = oldModel.dbName, newName = newModel.dbName) + val changeScalarListFieldTableNames = mutaction.newModel.scalarListFields.map { field => + builder.renameScalarListTable(mutaction.project, oldModel.dbName, field.name, oldModel.dbName, field.name) } DBIO.seq(changeScalarListFieldTableNames :+ changeModelTableName: _*) diff --git a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/RelationMutactionInterpreters.scala b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/RelationMutactionInterpreters.scala index 40b2bbfcb1..de5e28a347 100644 --- a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/RelationMutactionInterpreters.scala +++ b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/RelationMutactionInterpreters.scala @@ -65,7 +65,8 @@ case class UpdateInlineRelationInterpreter(builder: JdbcDeployDatabaseMutationBu project = mutaction.project, tableName = mutaction.previous.relationTableName, oldColumnName = mutaction.previous.inlineManifestation.get.referencingColumn, - newColumnName = mutaction.next.inlineManifestation.get.referencingColumn + newColumnName = mutaction.next.inlineManifestation.get.referencingColumn, + typeIdentifier = mutaction.next.schema.getModelByName_!(mutaction.next.inlineManifestation.get.inTableOfModelName).idField_!.typeIdentifier ) } diff --git a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/TypeMapper.scala b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/TypeMapper.scala index c70f36f5f2..2af11de8cf 100644 --- a/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/TypeMapper.scala +++ b/server/connectors/deploy-connector-jdbc/src/main/scala/com/prisma/deploy/connector/jdbc/database/TypeMapper.scala @@ -1,6 +1,5 @@ package com.prisma.deploy.connector.jdbc.database -import com.prisma.gc_values.GCValue import com.prisma.shared.models.ScalarField import com.prisma.shared.models.TypeIdentifier.TypeIdentifier import org.jooq.DSLContext @@ -11,23 +10,19 @@ trait TypeMapper { rawSQLFromParts( field.dbName, field.isRequired, - field.isList, field.typeIdentifier, - field.isAutoGeneratedByDb, - field.defaultValue + field.isAutoGeneratedByDb ) } def rawSQLFromParts( name: String, isRequired: Boolean, - isList: Boolean, typeIdentifier: TypeIdentifier, isAutoGenerated: Boolean = false, - defaultValue: Option[GCValue] = None )(implicit dsl: DSLContext): String - def rawSqlTypeForScalarTypeIdentifier(isList: Boolean, t: TypeIdentifier): String + def rawSqlTypeForScalarTypeIdentifier(t: TypeIdentifier): String def esc(n: String)(implicit dsl: DSLContext): String = dsl.render(name(n)) } diff --git a/server/connectors/deploy-connector-mongo/src/main/scala/com/prisma/deploy/connector/mongo/impl/mutactions/ColumnMutactionInterpreters.scala b/server/connectors/deploy-connector-mongo/src/main/scala/com/prisma/deploy/connector/mongo/impl/mutactions/ColumnMutactionInterpreters.scala index 75d449288c..ab3e8161f1 100644 --- a/server/connectors/deploy-connector-mongo/src/main/scala/com/prisma/deploy/connector/mongo/impl/mutactions/ColumnMutactionInterpreters.scala +++ b/server/connectors/deploy-connector-mongo/src/main/scala/com/prisma/deploy/connector/mongo/impl/mutactions/ColumnMutactionInterpreters.scala @@ -17,12 +17,12 @@ object CreateColumnInterpreter extends MongoMutactionInterpreter[CreateColumn] { object DeleteColumnInterpreter extends MongoMutactionInterpreter[DeleteColumn] { override def execute(mutaction: DeleteColumn) = mutaction.field.isUnique && !mutaction.field.isId match { - case true => MongoDeployDatabaseMutationBuilder.deleteField(mutaction.model, mutaction.field.dbName) + case true => MongoDeployDatabaseMutationBuilder.deleteField(mutaction.oldModel, mutaction.field.dbName) case false => NoAction.unit } override def rollback(mutaction: DeleteColumn) = mutaction.field.isUnique && !mutaction.field.isId match { - case true => MongoDeployDatabaseMutationBuilder.createField(mutaction.model, mutaction.field.dbName) + case true => MongoDeployDatabaseMutationBuilder.createField(mutaction.oldModel, mutaction.field.dbName) case false => NoAction.unit } } diff --git a/server/connectors/deploy-connector-mongo/src/main/scala/com/prisma/deploy/connector/mongo/impl/mutactions/ModelMutactionInterpreters.scala b/server/connectors/deploy-connector-mongo/src/main/scala/com/prisma/deploy/connector/mongo/impl/mutactions/ModelMutactionInterpreters.scala index 1aee10304a..1d45eeb359 100644 --- a/server/connectors/deploy-connector-mongo/src/main/scala/com/prisma/deploy/connector/mongo/impl/mutactions/ModelMutactionInterpreters.scala +++ b/server/connectors/deploy-connector-mongo/src/main/scala/com/prisma/deploy/connector/mongo/impl/mutactions/ModelMutactionInterpreters.scala @@ -1,7 +1,7 @@ package com.prisma.deploy.connector.mongo.impl.mutactions import com.prisma.deploy.connector.mongo.database.{MongoDeployDatabaseMutationBuilder, NoAction} -import com.prisma.deploy.connector.{CreateModelTable, DeleteModelTable, RenameTable} +import com.prisma.deploy.connector.{CreateModelTable, DeleteModelTable, UpdateModelTable} object CreateModelInterpreter extends MongoMutactionInterpreter[CreateModelTable] { override def execute(mutaction: CreateModelTable) = mutaction.model.isEmbedded match { @@ -27,12 +27,11 @@ object DeleteModelInterpreter extends MongoMutactionInterpreter[DeleteModelTable } } -object RenameModelInterpreter extends MongoMutactionInterpreter[RenameTable] { - override def execute(mutaction: RenameTable) = NoAction.unit //setName(mutaction, mutaction.previousName, mutaction.nextName) - - override def rollback(mutaction: RenameTable) = NoAction.unit //setName(mutaction, mutaction.nextName, mutaction.previousName) - - private def setName(mutaction: RenameTable, previousName: String, nextName: String) = { - MongoDeployDatabaseMutationBuilder.renameCollection(mutaction.project, collectionName = previousName, newName = nextName) - } +object UpdateModelInterpreter extends MongoMutactionInterpreter[UpdateModelTable] { + override def execute(mutaction: UpdateModelTable) = NoAction.unit //setName(mutaction, mutaction.previousName, mutaction.nextName) + override def rollback(mutaction: UpdateModelTable) = NoAction.unit //setName(mutaction, mutaction.nextName, mutaction.previousName) +// +// private def setName(mutaction: UpdateModelTable, previousName: String, nextName: String) = { +// MongoDeployDatabaseMutationBuilder.renameCollection(mutaction.project, collectionName = previousName, newName = nextName) +// } } diff --git a/server/connectors/deploy-connector-mongo/src/main/scala/com/prisma/deploy/connector/mongo/impl/mutactions/MongoAnyMutactionInterpreter.scala b/server/connectors/deploy-connector-mongo/src/main/scala/com/prisma/deploy/connector/mongo/impl/mutactions/MongoAnyMutactionInterpreter.scala index 50f7efebef..70db5fcffb 100644 --- a/server/connectors/deploy-connector-mongo/src/main/scala/com/prisma/deploy/connector/mongo/impl/mutactions/MongoAnyMutactionInterpreter.scala +++ b/server/connectors/deploy-connector-mongo/src/main/scala/com/prisma/deploy/connector/mongo/impl/mutactions/MongoAnyMutactionInterpreter.scala @@ -14,7 +14,7 @@ object MongoAnyMutactionInterpreter extends MongoMutactionInterpreter[DeployMuta case x: UpdateScalarListTable => NoAction.unit case x: DeleteScalarListTable => NoAction.unit case x: CreateModelTable => CreateModelInterpreter.execute(x) - case x: RenameTable => RenameModelInterpreter.execute(x) + case x: UpdateModelTable => UpdateModelInterpreter.execute(x) case x: DeleteModelTable => DeleteModelInterpreter.execute(x) case x: CreateRelationTable => CreateRelationInterpreter.execute(x) case x: UpdateRelationTable => NoAction.unit @@ -35,7 +35,7 @@ object MongoAnyMutactionInterpreter extends MongoMutactionInterpreter[DeployMuta case x: UpdateScalarListTable => NoAction.unit case x: DeleteScalarListTable => NoAction.unit case x: CreateModelTable => CreateModelInterpreter.rollback(x) - case x: RenameTable => RenameModelInterpreter.rollback(x) + case x: UpdateModelTable => UpdateModelInterpreter.rollback(x) case x: DeleteModelTable => DeleteModelInterpreter.rollback(x) case x: CreateRelationTable => CreateRelationInterpreter.execute(x) case x: UpdateRelationTable => NoAction.unit diff --git a/server/connectors/deploy-connector-mysql/src/main/scala/com/prisma/deploy/connector/mysql/MySqlDeployConnector.scala b/server/connectors/deploy-connector-mysql/src/main/scala/com/prisma/deploy/connector/mysql/MySqlDeployConnector.scala index dce1518279..2d1e0b56aa 100644 --- a/server/connectors/deploy-connector-mysql/src/main/scala/com/prisma/deploy/connector/mysql/MySqlDeployConnector.scala +++ b/server/connectors/deploy-connector-mysql/src/main/scala/com/prisma/deploy/connector/mysql/MySqlDeployConnector.scala @@ -38,13 +38,7 @@ case class MySqlDeployConnector(config: DatabaseConfig, driver: Driver, isProtot override val deployMutactionExecutor: DeployMutactionExecutor = JdbcDeployMutactionExecutor(mutationBuilder) override def databaseInspector: DatabaseInspector = MySqlDatabaseInspector(managementDatabase) - override def capabilities = { - if (isPrototype) { - ConnectorCapabilities.mysqlPrototype - } else { - ConnectorCapabilities.mysql - } - } + override def capabilities = if (isPrototype) ConnectorCapabilities.mysqlPrototype else ConnectorCapabilities.mysql override def createProjectDatabase(id: String): Future[Unit] = { val action = mutationBuilder.createDatabaseForProject(id = id) @@ -82,9 +76,7 @@ case class MySqlDeployConnector(config: DatabaseConfig, driver: Driver, isProtot override def reset(): Future[Unit] = truncateTablesInDatabase(managementDatabase.database) - override def shutdown() = { - databases.shutdown - } + override def shutdown() = databases.shutdown override def databaseIntrospectionInferrer(projectId: String) = EmptyDatabaseIntrospectionInferrer @@ -109,10 +101,6 @@ case class MySqlDeployConnector(config: DatabaseConfig, driver: Driver, isProtot } private def dangerouslyTruncateTables(tableNames: Vector[String]): DBIOAction[Unit, NoStream, Effect] = { - DBIO.seq( - List(sqlu"""SET FOREIGN_KEY_CHECKS=0""") ++ - tableNames.map(name => sqlu"TRUNCATE TABLE #$name") ++ - List(sqlu"""SET FOREIGN_KEY_CHECKS=1"""): _* - ) + DBIO.seq(List(sqlu"""SET FOREIGN_KEY_CHECKS=0""") ++ tableNames.map(name => sqlu"TRUNCATE TABLE #$name") ++ List(sqlu"""SET FOREIGN_KEY_CHECKS=1"""): _*) } } diff --git a/server/connectors/deploy-connector-mysql/src/main/scala/com/prisma/deploy/connector/mysql/database/MysqlJdbcDeployDatabaseMutationBuilder.scala b/server/connectors/deploy-connector-mysql/src/main/scala/com/prisma/deploy/connector/mysql/database/MysqlJdbcDeployDatabaseMutationBuilder.scala index 8b710ffdb5..723a6ea483 100644 --- a/server/connectors/deploy-connector-mysql/src/main/scala/com/prisma/deploy/connector/mysql/database/MysqlJdbcDeployDatabaseMutationBuilder.scala +++ b/server/connectors/deploy-connector-mysql/src/main/scala/com/prisma/deploy/connector/mysql/database/MysqlJdbcDeployDatabaseMutationBuilder.scala @@ -4,8 +4,8 @@ import com.prisma.connector.shared.jdbc.SlickDatabase import com.prisma.deploy.connector.jdbc.database.{JdbcDeployDatabaseMutationBuilder, TypeMapper} import com.prisma.shared.models.FieldBehaviour.IdBehaviour import com.prisma.shared.models.Manifestations.RelationTable -import com.prisma.shared.models.{Model, Project, Relation, TypeIdentifier} -import com.prisma.shared.models.TypeIdentifier.ScalarTypeIdentifier +import com.prisma.shared.models.TypeIdentifier.{ScalarTypeIdentifier, TypeIdentifier} +import com.prisma.shared.models._ import com.prisma.utils.boolean.BooleanUtils import org.jooq.impl.DSL import slick.dbio.{DBIOAction => DatabaseAction} @@ -55,9 +55,9 @@ case class MySqlJdbcDeployDatabaseMutationBuilder( } override def createScalarListTable(project: Project, model: Model, fieldName: String, typeIdentifier: ScalarTypeIdentifier): DBIO[_] = { - val indexSize = indexSizeForSQLType(typeMapper.rawSqlTypeForScalarTypeIdentifier(isList = false, typeIdentifier)) - val nodeIdSql = typeMapper.rawSQLFromParts("nodeId", isRequired = true, isList = false, TypeIdentifier.Cuid) - val valueSql = typeMapper.rawSQLFromParts("value", isRequired = true, isList = false, typeIdentifier) + val indexSize = indexSizeForSQLType(typeMapper.rawSqlTypeForScalarTypeIdentifier(typeIdentifier)) + val nodeIdSql = typeMapper.rawSQLFromParts("nodeId", isRequired = true, TypeIdentifier.Cuid) + val valueSql = typeMapper.rawSQLFromParts("value", isRequired = true, typeIdentifier) sqlu"""CREATE TABLE #${qualify(project.dbName, s"${model.dbName}_$fieldName")} ( #$nodeIdSql, @@ -75,13 +75,13 @@ case class MySqlJdbcDeployDatabaseMutationBuilder( val modelB = relation.modelB val modelAColumn = relation.modelAColumn val modelBColumn = relation.modelBColumn - val aColSql = typeMapper.rawSQLFromParts(modelAColumn, isRequired = true, isList = false, modelA.idField_!.typeIdentifier) - val bColSql = typeMapper.rawSQLFromParts(modelBColumn, isRequired = true, isList = false, modelB.idField_!.typeIdentifier) + val aColSql = typeMapper.rawSQLFromParts(modelAColumn, isRequired = true, modelA.idField_!.typeIdentifier) + val bColSql = typeMapper.rawSQLFromParts(modelBColumn, isRequired = true, modelB.idField_!.typeIdentifier) // we do not create an index on A because queries for the A column can be satisfied with the combined index as well def legacyTableCreate(idColumn: String) = { - val idSql = typeMapper.rawSQLFromParts(idColumn, isRequired = true, isList = false, TypeIdentifier.Cuid) + val idSql = typeMapper.rawSQLFromParts(idColumn, isRequired = true, TypeIdentifier.Cuid) sqlu""" CREATE TABLE #${qualify(project.dbName, relationTableName)} ( #$idSql, @@ -114,7 +114,7 @@ case class MySqlJdbcDeployDatabaseMutationBuilder( } override def createRelationColumn(project: Project, model: Model, references: Model, column: String): DBIO[_] = { - val colSql = typeMapper.rawSQLFromParts(column, isRequired = false, isList = false, references.idField_!.typeIdentifier) + val colSql = typeMapper.rawSQLFromParts(column, isRequired = false, references.idField_!.typeIdentifier) sqlu"""ALTER TABLE #${qualify(project.dbName, model.dbName)} ADD COLUMN #$colSql, ADD FOREIGN KEY (#${qualify(column)}) REFERENCES #${qualify(project.dbName, references.dbName)}(#${qualify(references.idField_!.dbName)}) ON DELETE CASCADE; @@ -144,23 +144,9 @@ case class MySqlJdbcDeployDatabaseMutationBuilder( } yield result } - override def createColumn(project: Project, - tableName: String, - columnName: String, - isRequired: Boolean, - isUnique: Boolean, - isList: Boolean, - typeIdentifier: ScalarTypeIdentifier): DBIO[_] = { - val newColSql = typeMapper.rawSQLFromParts(columnName, isRequired = isRequired, isList = isList, typeIdentifier) - val uniqueString = - if (isUnique) { - val indexSize = indexSizeForSQLType(typeMapper.rawSqlTypeForScalarTypeIdentifier(isList = isList, typeIdentifier)) - s", ADD UNIQUE INDEX ${qualify(s"${columnName}_UNIQUE")} (${qualify(columnName)}$indexSize ASC)" - } else { - "" - } - - sqlu"""ALTER TABLE #${qualify(project.dbName, tableName)} ADD COLUMN #$newColSql #$uniqueString, ALGORITHM = INPLACE""" + override def createColumn(project: Project, field: ScalarField): DBIO[_] = { + val newColSql = typeMapper.rawSQLForField(field) + sqlu"""ALTER TABLE #${qualify(project.dbName, field.model.dbName)} ADD COLUMN #$newColSql, ALGORITHM = INPLACE""" } override def deleteColumn(project: Project, tableName: String, columnName: String, model: Option[Model]) = { @@ -168,15 +154,17 @@ case class MySqlJdbcDeployDatabaseMutationBuilder( } override def updateColumn(project: Project, - model: Model, + field: ScalarField, + oldTableName: String, oldColumnName: String, - newColumnName: String, - newIsRequired: Boolean, - newIsList: Boolean, - newTypeIdentifier: ScalarTypeIdentifier): DBIO[_] = { - val newColSql = typeMapper.rawSQLFromParts(newColumnName, isRequired = newIsRequired, isList = newIsList, newTypeIdentifier) + oldTypeIdentifier: ScalarTypeIdentifier): DBIO[_] = { + val typeChange = if (oldTypeIdentifier != field.typeIdentifier) { + sqlu"UPDATE #${qualify(project.dbName, oldTableName)} SET #${qualify(oldColumnName)} = null" + } else { DBIO.successful(()) } + + val newColSql = typeMapper.rawSQLForField(field) - sqlu"ALTER TABLE #${qualify(project.dbName, model.dbName)} CHANGE COLUMN #${qualify(oldColumnName)} #$newColSql" + DBIO.seq(typeChange, sqlu"ALTER TABLE #${qualify(project.dbName, oldTableName)} CHANGE COLUMN #${qualify(oldColumnName)} #$newColSql") } def indexSizeForSQLType(sql: String): String = sql match { @@ -184,11 +172,11 @@ case class MySqlJdbcDeployDatabaseMutationBuilder( case _ => "" } - override def addUniqueConstraint(project: Project, tableName: String, columnName: String, typeIdentifier: ScalarTypeIdentifier): DBIO[_] = { - val sqlType = typeMapper.rawSqlTypeForScalarTypeIdentifier(isList = false, typeIdentifier) + override def addUniqueConstraint(project: Project, field: Field): DBIO[_] = { + val sqlType = typeMapper.rawSqlTypeForScalarTypeIdentifier(field.typeIdentifier) val indexSize = indexSizeForSQLType(sqlType) - sqlu"ALTER TABLE #${qualify(project.dbName, tableName)} ADD UNIQUE INDEX #${qualify(s"${columnName}_UNIQUE")}(#${qualify(columnName)}#$indexSize ASC)" + sqlu"ALTER TABLE #${qualify(project.dbName, field.model.dbName)} ADD UNIQUE INDEX #${qualify(s"${field.dbName}_UNIQUE")}(#${qualify(field.dbName)}#$indexSize ASC)" } override def removeIndex(project: Project, tableName: String, indexName: String): DBIO[_] = { @@ -204,10 +192,19 @@ case class MySqlJdbcDeployDatabaseMutationBuilder( } //Here this is only used for relationtables - override def renameColumn(project: Project, tableName: String, oldColumnName: String, newColumnName: String): DBIO[_] = { - if (oldColumnName != newColumnName) { - val newColSql = typeMapper.rawSQLFromParts(newColumnName, isRequired = true, isList = false, project.models.head.idField_!.typeIdentifier) - sqlu"ALTER TABLE #${qualify(project.dbName, tableName)} CHANGE COLUMN #${qualify(oldColumnName)} #$newColSql" + override def renameColumn(project: Project, tableName: String, oldColumnName: String, newColumnName: String, typeIdentifier: TypeIdentifier): DBIO[_] = { + val newColSql = typeMapper.rawSQLFromParts(newColumnName, isRequired = true, typeIdentifier) + sharedRename(project, tableName, oldColumnName, newColumnName, newColSql) + } + + private def renameColumn(project: Project, tableName: String, oldColumnName: String, field: Field): DBIO[_] = { + val newColSql = typeMapper.rawSQLFromParts(field.dbName, isRequired = field.isRequired, field.typeIdentifier) + sharedRename(project, tableName, oldColumnName, field.dbName, newColSql) + } + + private def sharedRename(project: Project, tableName: String, oldName: String, newName: String, typeString: String) = { + if (oldName != newName) { + sqlu"ALTER TABLE #${qualify(project.dbName, tableName)} CHANGE COLUMN #${qualify(oldName)} #$typeString" } else { DatabaseAction.successful(()) } diff --git a/server/connectors/deploy-connector-mysql/src/main/scala/com/prisma/deploy/connector/mysql/database/MysqlTypeMapper.scala b/server/connectors/deploy-connector-mysql/src/main/scala/com/prisma/deploy/connector/mysql/database/MysqlTypeMapper.scala index 1941cb6ec5..8f1498b653 100644 --- a/server/connectors/deploy-connector-mysql/src/main/scala/com/prisma/deploy/connector/mysql/database/MysqlTypeMapper.scala +++ b/server/connectors/deploy-connector-mysql/src/main/scala/com/prisma/deploy/connector/mysql/database/MysqlTypeMapper.scala @@ -10,18 +10,15 @@ case class MySqlTypeMapper() extends TypeMapper { override def rawSQLFromParts( name: String, isRequired: Boolean, - isList: Boolean, typeIdentifier: TypeIdentifier, - isAutoGenerated: Boolean = false, - defaultValue: Option[GCValue] = None + isAutoGenerated: Boolean = false )(implicit dsl: DSLContext): String = { val n = esc(name) val nullable = if (isRequired) "NOT NULL" else "NULL" val generated = if (isAutoGenerated) "AUTO_INCREMENT" else "" - val ty = rawSqlTypeForScalarTypeIdentifier(isList, typeIdentifier) - val default = defaultValue.map(d => s"DEFAULT ${d.value}").getOrElse("") + val ty = rawSqlTypeForScalarTypeIdentifier(typeIdentifier) - s"$n $ty $nullable $default $generated" + s"$n $ty $nullable $generated" } // note: utf8mb4 requires up to 4 bytes per character and includes full utf8 support, including emoticons @@ -30,8 +27,7 @@ case class MySqlTypeMapper() extends TypeMapper { // We limit enums to 191, and create text indexes over the first 191 characters of the string, but // allow the actual content to be much larger. // Key columns are utf8_general_ci as this collation is ~10% faster when sorting and requires less memory - override def rawSqlTypeForScalarTypeIdentifier(isList: Boolean, t: TypeIdentifier.TypeIdentifier): String = t match { - case _ if isList => "mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" + override def rawSqlTypeForScalarTypeIdentifier(t: TypeIdentifier.TypeIdentifier): String = t match { case TypeIdentifier.String => "mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" case TypeIdentifier.Boolean => "boolean" case TypeIdentifier.Int => "int" diff --git a/server/connectors/deploy-connector-postgres/src/main/scala/com/prisma/deploy/connector/postgres/database/PostgresJdbcDeployDatabaseMutationBuilder.scala b/server/connectors/deploy-connector-postgres/src/main/scala/com/prisma/deploy/connector/postgres/database/PostgresJdbcDeployDatabaseMutationBuilder.scala index 99810f8fe3..2cf61fcacf 100644 --- a/server/connectors/deploy-connector-postgres/src/main/scala/com/prisma/deploy/connector/postgres/database/PostgresJdbcDeployDatabaseMutationBuilder.scala +++ b/server/connectors/deploy-connector-postgres/src/main/scala/com/prisma/deploy/connector/postgres/database/PostgresJdbcDeployDatabaseMutationBuilder.scala @@ -5,8 +5,8 @@ import com.prisma.deploy.connector.jdbc.database.{JdbcDeployDatabaseMutationBuil import com.prisma.gc_values.StringGCValue import com.prisma.shared.models.FieldBehaviour.IdBehaviour import com.prisma.shared.models.Manifestations.RelationTable -import com.prisma.shared.models.TypeIdentifier.ScalarTypeIdentifier -import com.prisma.shared.models.{Model, Project, Relation} +import com.prisma.shared.models.TypeIdentifier.{ScalarTypeIdentifier, TypeIdentifier} +import com.prisma.shared.models._ import com.prisma.utils.boolean.BooleanUtils import org.jooq.impl.DSL import slick.dbio.{DBIOAction => DatabaseAction} @@ -52,25 +52,13 @@ case class PostgresJdbcDeployDatabaseMutationBuilder( } val idFieldSQL = sequence match { - case Some(seq) => - typeMapper.rawSQLFromParts( - name = idField.dbName, - isRequired = idField.isRequired, - isList = false, - typeIdentifier = idField.typeIdentifier, - defaultValue = Some(StringGCValue(s"""nextval('"${project.dbName}"."${seq.name}"'::regclass)""")) - ) - case None => - typeMapper.rawSQLForField(idField) + case Some(s) => typeMapper.rawSQLForField(idField) ++ s""" DEFAULT nextval('"${project.dbName}"."${s.name}"'::regclass)""" + case None => typeMapper.rawSQLForField(idField) } val createSequenceIfRequired = sequence match { - case Some(sequence) => - sqlu""" - CREATE SEQUENCE "#${project.dbName}"."#${sequence.name}" START #${sequence.initialValue} - """ - case _ => - DBIO.successful(()) + case Some(sequence) => sqlu"""CREATE SEQUENCE "#${project.dbName}"."#${sequence.name}" START #${sequence.initialValue}""" + case _ => DBIO.successful(()) } val createTable = sqlu""" @@ -84,7 +72,7 @@ case class PostgresJdbcDeployDatabaseMutationBuilder( } override def createScalarListTable(project: Project, model: Model, fieldName: String, typeIdentifier: ScalarTypeIdentifier): DBIO[_] = { - val sqlType = typeMapper.rawSqlTypeForScalarTypeIdentifier(isList = false, typeIdentifier) + val sqlType = typeMapper.rawSqlTypeForScalarTypeIdentifier(typeIdentifier) sqlu""" CREATE TABLE #${qualify(project.dbName, s"${model.dbName}_$fieldName")} ( @@ -105,8 +93,8 @@ case class PostgresJdbcDeployDatabaseMutationBuilder( val modelB = relation.modelB val modelAColumn = relation.modelAColumn val modelBColumn = relation.modelBColumn - val aColSql = typeMapper.rawSQLFromParts(modelAColumn, isRequired = true, isList = false, modelA.idField_!.typeIdentifier) - val bColSql = typeMapper.rawSQLFromParts(modelBColumn, isRequired = true, isList = false, modelB.idField_!.typeIdentifier) + val aColSql = typeMapper.rawSQLFromParts(modelAColumn, isRequired = true, modelA.idField_!.typeIdentifier) + val bColSql = typeMapper.rawSQLFromParts(modelBColumn, isRequired = true, modelB.idField_!.typeIdentifier) def legacyTableCreate(idColumn: String) = sqlu""" CREATE TABLE #${qualify(project.dbName, relationTableName)} ( "#$idColumn" CHAR(25) NOT NULL, @@ -139,7 +127,7 @@ case class PostgresJdbcDeployDatabaseMutationBuilder( } override def createRelationColumn(project: Project, model: Model, references: Model, column: String): DBIO[_] = { - val colSql = typeMapper.rawSQLFromParts(column, isRequired = false, isList = model.idField_!.isList, references.idField_!.typeIdentifier) + val colSql = typeMapper.rawSQLFromParts(column, isRequired = false, references.idField_!.typeIdentifier) sqlu"""ALTER TABLE #${qualify(project.dbName, model.dbName)} ADD COLUMN #$colSql REFERENCES #${qualify(project.dbName, references.dbName)} (#${qualify(references.dbNameOfIdField_!)}) ON DELETE SET NULL;""" @@ -149,50 +137,38 @@ case class PostgresJdbcDeployDatabaseMutationBuilder( deleteColumn(project, model.dbName, column) } - override def createColumn( - project: Project, - tableName: String, - columnName: String, - isRequired: Boolean, - isUnique: Boolean, - isList: Boolean, - typeIdentifier: ScalarTypeIdentifier - ): DBIO[_] = { - val fieldSQL = typeMapper.rawSQLFromParts(columnName, isRequired, isList, typeIdentifier) - val uniqueAction = isUnique match { - case true => addUniqueConstraint(project, tableName, columnName, typeIdentifier) - case false => DatabaseAction.successful(()) - } - - val addColumn = sqlu"""ALTER TABLE #${qualify(project.dbName, tableName)} ADD COLUMN #$fieldSQL""" - DatabaseAction.seq(addColumn, uniqueAction) + override def createColumn(project: Project, field: ScalarField): DBIO[_] = { + val fieldSQL = typeMapper.rawSQLForField(field) + sqlu"""ALTER TABLE #${qualify(project.dbName, field.model.dbName)} ADD COLUMN #$fieldSQL""" } override def updateColumn(project: Project, - model: Model, + field: ScalarField, + oldTableName: String, oldColumnName: String, - newColumnName: String, - newIsRequired: Boolean, - newIsList: Boolean, - newTypeIdentifier: ScalarTypeIdentifier): DBIO[_] = { - val tableName = model.dbName - val nulls = if (newIsRequired) { "SET NOT NULL" } else { "DROP NOT NULL" } - val sqlType = typeMapper.rawSqlTypeForScalarTypeIdentifier(newIsList, newTypeIdentifier) - val renameIfNecessary = renameColumn(project, tableName, oldColumnName, newColumnName) - - DatabaseAction.seq( - sqlu"""ALTER TABLE #${qualify(project.dbName, tableName)} ALTER COLUMN #${qualify(oldColumnName)} TYPE #$sqlType""", - sqlu"""ALTER TABLE #${qualify(project.dbName, tableName)} ALTER COLUMN #${qualify(oldColumnName)} #$nulls""", - renameIfNecessary - ) + oldTypeIdentifier: ScalarTypeIdentifier): DBIO[_] = { + if (oldTypeIdentifier != field.typeIdentifier) { + DatabaseAction.seq(deleteColumn(project, field.model.dbName, oldColumnName), createColumn(project, field)) + } else { + val sqlType = typeMapper.rawSqlTypeForScalarTypeIdentifier(field.typeIdentifier) + val nulls = if (field.isRequired) "SET NOT NULL" else "DROP NOT NULL" + val renameIfNecessary = renameColumn(project, oldTableName, oldColumnName, field.dbName, field.typeIdentifier) + + DatabaseAction.seq( + sqlu"""ALTER TABLE #${qualify(project.dbName, oldTableName)} ALTER COLUMN #${qualify(oldColumnName)} TYPE #$sqlType""", + sqlu"""ALTER TABLE #${qualify(project.dbName, oldTableName)} ALTER COLUMN #${qualify(oldColumnName)} #$nulls""", + renameIfNecessary + ) + } } override def deleteColumn(project: Project, tableName: String, columnName: String, model: Option[Model]) = { sqlu"""ALTER TABLE #${qualify(project.dbName, tableName)} DROP COLUMN #${qualify(columnName)}""" } - override def addUniqueConstraint(project: Project, tableName: String, columnName: String, typeIdentifier: ScalarTypeIdentifier): DBIO[_] = { - sqlu"""CREATE UNIQUE INDEX #${qualify(s"${project.dbName}.$tableName.$columnName._UNIQUE")} ON #${qualify(project.dbName, tableName)}(#${qualify(columnName)} ASC);""" + override def addUniqueConstraint(project: Project, field: Field): DBIO[_] = { + sqlu"""CREATE UNIQUE INDEX #${qualify(s"${project.dbName}.${field.model.dbName}.${field.dbName}._UNIQUE")} ON #${qualify(project.dbName, field.model.dbName)}(#${qualify( + field.dbName)} ASC);""" } override def removeIndex(project: Project, tableName: String, indexName: String): DBIO[_] = { @@ -207,7 +183,7 @@ case class PostgresJdbcDeployDatabaseMutationBuilder( } } - override def renameColumn(project: Project, tableName: String, oldColumnName: String, newColumnName: String) = { + override def renameColumn(project: Project, tableName: String, oldColumnName: String, newColumnName: String, typeIdentifier: TypeIdentifier) = { if (oldColumnName != newColumnName) { sqlu"""ALTER TABLE #${qualify(project.dbName, tableName)} RENAME COLUMN #${qualify(oldColumnName)} TO #${qualify(newColumnName)}""" } else { diff --git a/server/connectors/deploy-connector-postgres/src/main/scala/com/prisma/deploy/connector/postgres/database/PostgresTypeMapper.scala b/server/connectors/deploy-connector-postgres/src/main/scala/com/prisma/deploy/connector/postgres/database/PostgresTypeMapper.scala index 4b12f9b3b5..4e08b67026 100644 --- a/server/connectors/deploy-connector-postgres/src/main/scala/com/prisma/deploy/connector/postgres/database/PostgresTypeMapper.scala +++ b/server/connectors/deploy-connector-postgres/src/main/scala/com/prisma/deploy/connector/postgres/database/PostgresTypeMapper.scala @@ -10,21 +10,17 @@ case class PostgresTypeMapper() extends TypeMapper { override def rawSQLFromParts( name: String, isRequired: Boolean, - isList: Boolean, typeIdentifier: TypeIdentifier, - isAutoGeneratedByDb: Boolean = false, - defaultValue: Option[GCValue] = None + isAutoGeneratedByDb: Boolean = false )(implicit dsl: DSLContext): String = { val n = esc(name) val nullable = if (isRequired) "NOT NULL" else "NULL" - val ty = rawSqlTypeForScalarTypeIdentifier(isList, typeIdentifier) - val default = defaultValue.map(d => s"DEFAULT ${d.value}").getOrElse("") + val ty = rawSqlTypeForScalarTypeIdentifier(typeIdentifier) - s"$n $ty $nullable $default" + s"$n $ty $nullable" } - override def rawSqlTypeForScalarTypeIdentifier(isList: Boolean, t: TypeIdentifier.TypeIdentifier): String = t match { - case _ if isList => "text" + override def rawSqlTypeForScalarTypeIdentifier(t: TypeIdentifier.TypeIdentifier): String = t match { case TypeIdentifier.String => "text" case TypeIdentifier.Boolean => "boolean" case TypeIdentifier.Int => "int" diff --git a/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/SQLiteDeployConnector.scala b/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/SQLiteDeployConnector.scala index 2a62a6dc51..cd8b310ba1 100644 --- a/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/SQLiteDeployConnector.scala +++ b/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/SQLiteDeployConnector.scala @@ -1,7 +1,10 @@ package com.prisma.deploy.connector.sqlite +import java.sql.Driver + import com.prisma.config.DatabaseConfig import com.prisma.deploy.connector._ +import com.prisma.deploy.connector.jdbc.SQLiteDatabaseInspector import com.prisma.deploy.connector.jdbc.database.{JdbcClientDbQueries, JdbcDeployMutactionExecutor} import com.prisma.deploy.connector.jdbc.persistence.{JdbcCloudSecretPersistence, JdbcMigrationPersistence, JdbcProjectPersistence, JdbcTelemetryPersistence} import com.prisma.deploy.connector.persistence.{MigrationPersistence, ProjectPersistence, TelemetryPersistence} @@ -21,11 +24,11 @@ import slick.jdbc.meta.MTable import scala.concurrent.{ExecutionContext, Future} import scala.util.{Failure, Success} -case class SQLiteDeployConnector(config: DatabaseConfig, isPrototype: Boolean)(implicit ec: ExecutionContext) extends DeployConnector { +case class SQLiteDeployConnector(config: DatabaseConfig, driver: Driver, isPrototype: Boolean)(implicit ec: ExecutionContext) extends DeployConnector { override def isActive = true override def fieldRequirements: FieldRequirementsInterface = SQLiteFieldRequirement(isActive) - lazy val internalDatabaseDefs = SQLiteInternalDatabaseDefs(config) + lazy val internalDatabaseDefs = SQLiteInternalDatabaseDefs(config, driver) lazy val setupDatabase = internalDatabaseDefs.setupDatabases lazy val databases = internalDatabaseDefs.managementDatabases lazy val managementDatabase = databases.primary @@ -38,7 +41,7 @@ case class SQLiteDeployConnector(config: DatabaseConfig, isPrototype: Boolean)(i override val cloudSecretPersistence: JdbcCloudSecretPersistence = JdbcCloudSecretPersistence(managementDatabase) override val telemetryPersistence: TelemetryPersistence = JdbcTelemetryPersistence(managementDatabase) override val deployMutactionExecutor: DeployMutactionExecutor = JdbcDeployMutactionExecutor(mutationBuilder) - override def databaseInspector: DatabaseInspector = DatabaseInspector.empty + override def databaseInspector: DatabaseInspector = SQLiteDatabaseInspector(managementDatabase) override def capabilities: ConnectorCapabilities = ConnectorCapabilities.mysql //Fixme override def createProjectDatabase(id: String): Future[Unit] = { diff --git a/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/SQLiteInternalDatabaseDefs.scala b/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/SQLiteInternalDatabaseDefs.scala index 2d150f7800..716fcd9509 100644 --- a/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/SQLiteInternalDatabaseDefs.scala +++ b/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/SQLiteInternalDatabaseDefs.scala @@ -1,10 +1,13 @@ package com.prisma.deploy.connector.sqlite +import java.sql.Driver + import com.prisma.config.DatabaseConfig import com.prisma.connector.shared.jdbc.{Databases, SlickDatabase} +import com.typesafe.config.{Config, ConfigFactory} import slick.jdbc.SQLiteProfile -case class SQLiteInternalDatabaseDefs(dbConfig: DatabaseConfig) { +case class SQLiteInternalDatabaseDefs(dbConfig: DatabaseConfig, driver: Driver) { import slick.jdbc.SQLiteProfile.api._ val managementSchemaName = dbConfig.managementSchema.getOrElse("prisma") @@ -13,33 +16,10 @@ case class SQLiteInternalDatabaseDefs(dbConfig: DatabaseConfig) { lazy val managementDatabases = databases(root = false) def databases(root: Boolean): Databases = { -// val config = typeSafeConfigFromDatabaseConfig(dbConfig, root) - val masterDb = Database.forURL("jdbc:sqlite:management.db", driver = "org.sqlite.JDBC") + val masterDb = Database.forDriver(driver, "jdbc:sqlite:management.db") val slickDatabase = SlickDatabase(SQLiteProfile, masterDb) Databases(primary = slickDatabase, replica = slickDatabase) } - -// def typeSafeConfigFromDatabaseConfig(dbConfig: DatabaseConfig, root: Boolean): Config = { -// val pooled = if (dbConfig.pooled) "" else "connectionPool = disabled" -// val schema = if (root) "" else managementSchemaName -// -// ConfigFactory -// .parseString(s""" -// |database { -// | connectionInitSql="set names utf8mb4" -// | dataSourceClass = "slick.jdbc.DriverDataSource" -// | properties { -// | url = "jdbc:mysql://${dbConfig.host}:${dbConfig.port}/$schema?autoReconnect=true&useSSL=${dbConfig.ssl}&requireSSL=false&serverTimeZone=UTC&useUnicode=true&characterEncoding=UTF-8&socketTimeout=60000&usePipelineAuth=false" -// | user = "${dbConfig.user}" -// | password = "${dbConfig.password.getOrElse("")}" -// | } -// | numThreads = 1 -// | connectionTimeout = 5000 -// | $pooled -// |} -// """.stripMargin) -// .resolve -// } } diff --git a/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/database/SQLiteJdbcDeployDatabaseMutationBuilder.scala b/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/database/SQLiteJdbcDeployDatabaseMutationBuilder.scala index c8e8c4d04b..59e44a8a8d 100644 --- a/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/database/SQLiteJdbcDeployDatabaseMutationBuilder.scala +++ b/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/database/SQLiteJdbcDeployDatabaseMutationBuilder.scala @@ -6,7 +6,7 @@ import com.prisma.connector.shared.jdbc.SlickDatabase import com.prisma.deploy.connector.jdbc.database.{JdbcDeployDatabaseMutationBuilder, TypeMapper} import com.prisma.gc_values.GCValue import com.prisma.shared.models.TypeIdentifier.{ScalarTypeIdentifier, TypeIdentifier} -import com.prisma.shared.models.{Model, Project, Relation, TypeIdentifier} +import com.prisma.shared.models._ import com.prisma.utils.boolean.BooleanUtils import org.jooq.impl.DSL import slick.dbio.{DBIOAction => DatabaseAction} @@ -78,8 +78,8 @@ case class SQLiteJdbcDeployDatabaseMutationBuilder( } override def createScalarListTable(project: Project, model: Model, fieldName: String, typeIdentifier: ScalarTypeIdentifier): DBIO[_] = { - val nodeIdSql = typeMapper.rawSQLFromParts("nodeId", isRequired = true, isList = false, TypeIdentifier.Cuid) - val valueSql = typeMapper.rawSQLFromParts("value", isRequired = true, isList = false, typeIdentifier) + val nodeIdSql = typeMapper.rawSQLFromParts("nodeId", isRequired = true, TypeIdentifier.Cuid) + val valueSql = typeMapper.rawSQLFromParts("value", isRequired = true, typeIdentifier) val create = sqlu"""CREATE TABLE #${qualify(project.dbName, s"${model.dbName}_$fieldName")} ( #$nodeIdSql, @@ -100,8 +100,8 @@ case class SQLiteJdbcDeployDatabaseMutationBuilder( val modelB = relation.modelB val modelAColumn = relation.modelAColumn val modelBColumn = relation.modelBColumn - val aColSql = typeMapper.rawSQLFromParts(modelAColumn, isRequired = true, isList = false, modelA.idField_!.typeIdentifier) - val bColSql = typeMapper.rawSQLFromParts(modelBColumn, isRequired = true, isList = false, modelB.idField_!.typeIdentifier) + val aColSql = typeMapper.rawSQLFromParts(modelAColumn, isRequired = true, modelA.idField_!.typeIdentifier) + val bColSql = typeMapper.rawSQLFromParts(modelBColumn, isRequired = true, modelB.idField_!.typeIdentifier) val tableCreate = sqlu""" CREATE TABLE #${qualify(project.dbName, relationTableName)} ( "id" CHAR(25) NOT NULL, @@ -120,7 +120,7 @@ case class SQLiteJdbcDeployDatabaseMutationBuilder( } override def createRelationColumn(project: Project, model: Model, references: Model, column: String): DBIO[_] = { - val colSql = typeMapper.rawSQLFromParts(column, isRequired = false, isList = model.idField_!.isList, references.idField_!.typeIdentifier) + val colSql = typeMapper.rawSQLFromParts(column, isRequired = false, references.idField_!.typeIdentifier) sqlu"""ALTER TABLE #${qualify(project.dbName, model.dbName)} ADD COLUMN #$colSql, ADD FOREIGN KEY (#${qualify(column)}) REFERENCES #${qualify(project.dbName, references.dbName)}(#${qualify(references.idField_!.dbName)}) ON DELETE CASCADE; @@ -128,7 +128,7 @@ case class SQLiteJdbcDeployDatabaseMutationBuilder( } override def deleteRelationColumn(project: Project, model: Model, references: Model, column: String): DBIO[_] = { - //Fixme see below + sys.error("DEPLOY CHANGES NOT IMPLEMENTED FOR SQLITE") for { namesOfForeignKey <- getNamesOfForeignKeyConstraints(project.dbName, model, column) _ <- sqlu"""ALTER TABLE #${qualify(project.dbName, model.dbName)} DROP FOREIGN KEY `#${namesOfForeignKey.head}`;""" @@ -137,7 +137,7 @@ case class SQLiteJdbcDeployDatabaseMutationBuilder( } private def getNamesOfForeignKeyConstraints(projectId: String, model: Model, column: String): DatabaseAction[Vector[String], NoStream, Effect] = { - //Fixme this probably does not work + sys.error("DEPLOY CHANGES NOT IMPLEMENTED FOR SQLITE") for { result <- sql""" @@ -153,74 +153,85 @@ case class SQLiteJdbcDeployDatabaseMutationBuilder( } yield result } - override def createColumn(project: Project, - tableName: String, - columnName: String, - isRequired: Boolean, - isUnique: Boolean, - isList: Boolean, - typeIdentifier: ScalarTypeIdentifier): DBIO[_] = { - val newColSql = rawSQLFromParts(columnName, isRequired = isRequired, isList = isList, typeIdentifier) - val unique = if (isUnique) addUniqueConstraint(project, tableName, columnName, typeIdentifier) else DBIO.successful(()) - val add = sqlu"""ALTER TABLE #${qualify(project.dbName, tableName)} ADD COLUMN #$newColSql""" - DBIO.seq(add, unique) + override def createColumn(project: Project, field: ScalarField): DBIO[_] = { + val newColSql = rawSQLFromParts(field.dbName, isRequired = field.isRequired, field.typeIdentifier) //Fixme build from field helper + sqlu"""ALTER TABLE #${qualify(project.dbName, field.model.dbName)} ADD COLUMN #$newColSql""" + } + + private def createColumnForAlterTable(project: Project, tableName: String, field: ScalarField): DBIO[_] = { + val newColSql = rawSQLFromParts(field.dbName, isRequired = field.isRequired, field.typeIdentifier) + sqlu"""ALTER TABLE #${qualify(project.dbName, tableName)} ADD COLUMN #$newColSql""" } override def updateColumn(project: Project, - model: Model, + field: ScalarField, + oldTableName: String, oldColumnName: String, - newColumnName: String, - newIsRequired: Boolean, - newIsList: Boolean, - newTypeIdentifier: ScalarTypeIdentifier): DBIO[_] = { - -// https://www.sqlite.org/lang_altertable.html -// If foreign key constraints are enabled, disable them using PRAGMA foreign_keys=OFF. - val foreignKeysOff = sqlu"Pragma foreign_keys=OFF" - -// Start a transaction. - val beginTransaction = sqlu"BEGIN TRANSACTION" -// Remember the format of all indexes and triggers associated with table X. This information will be needed in step 8 below -// One way to do this is to run a query like the following: SELECT type, sql FROM sqlite_master WHERE tbl_name='X'. - -// Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X. -// Make sure that the name "new_X" does not collide with any existing table name, of course. + oldTypeIdentifier: ScalarTypeIdentifier): DBIO[_] = { + sys.error("DEPLOY CHANGES NOT IMPLEMENTED FOR SQLITE") + + alterTable(project, field.model) + } + + private def alterTable(project: Project, model: Model) = { + sys.error("DEPLOY CHANGES NOT IMPLEMENTED FOR SQLITE") + + val tableName = model.dbName + val tempTableName = s"${model.dbName}_TEMP_TABLE" + + // https://www.sqlite.org/lang_altertable.html + // If foreign key constraints are enabled, disable them using PRAGMA foreign_keys=OFF. + val foreignKeysOff = sqlu"Pragma foreign_keys=OFF;" + + // Start a transaction. + val beginTransaction = sqlu"BEGIN TRANSACTION;" + // Remember the format of all indexes and triggers associated with table X. This information will be needed in step 8 below + + // Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X. + // Make sure that the name "new_X" does not collide with any existing table name, of course. val idField = model.idField_! val idFieldSQL = typeMapper.rawSQLForField(idField) - val createNewTable = sqlu"""CREATE TABLE #${qualify(project.dbName, s"${model.dbName}_TEMP_TABLE")} ( + val createNewTable = + sqlu"""CREATE TABLE #${qualify(project.dbName, tempTableName)} ( #$idFieldSQL, PRIMARY KEY (#${qualify(idField.dbName)}) );""" - val addAllScalarNonListFields = DBIO.seq(model.scalarNonListFields.map { field => - createColumn(project, s"${model.dbName}_TEMP_TABLE", field.dbName, field.isRequired, field.isUnique, field.isList, field.typeIdentifier) - }: _*) + //Fixme InlineRelationFields + val addAllScalarNonListFields = DBIO.seq(model.scalarNonListFields.filterNot(_.isId).map(createColumnForAlterTable(project, tempTableName, _)): _*) + + // Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X. + // Fixme -> field could have been renamed + val columnNames = model.scalarNonListFields.map(_.dbName).mkString(",") // needs to be intersection of old and new scalarfields of model -// Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X. - val columnNames = model.scalarNonListFields.map(_.dbName).mkString(",") val transferContent = - sqlu"""INSERT INTO #${qualify(project.dbName, s"${model.dbName}_TEMP_TABLE")} (#$columnNames) + sqlu"""INSERT INTO #${qualify(project.dbName, tempTableName)} (#$columnNames) SELECT #$columnNames - FROM #${qualify(project.dbName, s"${model.dbName}")} + FROM #${qualify(project.dbName, tableName)}; """ -// Drop the old table X: DROP TABLE X. - val dropOldTable = sqlu"DROP TABLE #${qualify(project.dbName, model.dbName)} " -// Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X. + // Drop the old table X: DROP TABLE X. + val dropOldTable = sqlu"DROP TABLE #${qualify(project.dbName, tableName)};" - val renameNewTable = sqlu"ALTER TABLE #${qualify(project.dbName, s"${model.dbName}_TEMP_TABLE")} RENAME TO #${qualify(project.dbName, model.dbName)}" -// Use CREATE INDEX and CREATE TRIGGER to reconstruct indexes and triggers associated with table X. Perhaps use the old format of the triggers and indexes saved from step 3 above as a guide, making changes as appropriate for the alteration. + // Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X. + val renameNewTable = sqlu"ALTER TABLE #${qualify(project.dbName, tempTableName)} RENAME TO #${qualify(tableName)};" + + // Use CREATE INDEX and CREATE TRIGGER to reconstruct indexes and triggers associated with table X. + // Perhaps use the old format of the triggers and indexes saved from step 3 above as a guide, making changes as appropriate for the alteration. + // Fetch original indexes One way to do this is to run a query like the following: SELECT type, sql FROM sqlite_master WHERE tbl_name='X'. val createIndexes = sqlu"" //Fixme -// If any views refer to table X in a way that is affected by the schema change, then drop those views using DROP VIEW and recreate them with whatever changes are necessary to accommodate the schema change using CREATE VIEW. -// If foreign key constraints were originally enabled then run PRAGMA foreign_key_check to verify that the schema change did not break any foreign key constraints. - val foreignKeyCheck = sqlu"Pragma foreign_key_check" -// Commit the transaction started in step 2. + // If any views refer to table X in a way that is affected by the schema change, then drop those views using DROP VIEW + // and recreate them with whatever changes are necessary to accommodate the schema change using CREATE VIEW. + + // If foreign key constraints were originally enabled then run PRAGMA foreign_key_check to verify that the schema change did not break any foreign key constraints. + val foreignKeyCheck = sqlu"Pragma foreign_key_check;" + // Commit the transaction started in step 2. val commit = sqlu"COMMIT" -// If foreign keys constraints were originally enabled, reenable them now. - val foreignKeysOn = sqlu"Pragma foreign_keys=ON" + // If foreign keys constraints were originally enabled, reenable them now. + val foreignKeysOn = sqlu"Pragma foreign_keys=ON;" DBIO.seq( foreignKeysOff, @@ -229,53 +240,52 @@ case class SQLiteJdbcDeployDatabaseMutationBuilder( addAllScalarNonListFields, transferContent, dropOldTable, - renameNewTable, - createIndexes, - foreignKeyCheck, +// renameNewTable, +// createIndexes, +// foreignKeyCheck, commit, foreignKeysOn ) } - override def deleteColumn(project: Project, tableName: String, columnName: String, model: Option[Model]) = { - //if no model is provided, this concerns the relation table + override def deleteColumn(project: Project, tableName: String, columnName: String, model: Option[Model]): DBIO[_] = { + sys.error("DEPLOY CHANGES NOT IMPLEMENTED FOR SQLITE") - sqlu"""ALTER TABLE #${qualify(project.dbName, tableName)} DROP COLUMN #${qualify(columnName)}""" + //if no model is provided, this concerns the relation table + model match { + case Some(m) => alterTable(project, m) + case None => sys.error("implement for relations") //Fixme + } } - override def addUniqueConstraint(project: Project, tableName: String, columnName: String, typeIdentifier: ScalarTypeIdentifier): DBIO[_] = { - sqlu"CREATE UNIQUE INDEX IF NOT EXISTS #${qualify(project.dbName, s"${columnName}_UNIQUE")} ON #${qualify(tableName)} (#${qualify(columnName)} ASC)" + override def addUniqueConstraint(project: Project, field: Field): DBIO[_] = { + sqlu"CREATE UNIQUE INDEX IF NOT EXISTS #${qualify(project.dbName, s"${field.dbName}_UNIQUE")} ON #${qualify(field.model.dbName)} (#${qualify(field.dbName)} ASC);" } override def removeIndex(project: Project, tableName: String, indexName: String): DBIO[_] = { - sqlu"ALTER TABLE #${qualify(project.dbName, tableName)} DROP INDEX #${qualify(indexName)}" + sqlu"ALTER TABLE #${qualify(project.dbName, tableName)} DROP INDEX #${qualify(indexName)};" } override def renameTable(project: Project, oldTableName: String, newTableName: String): DBIO[_] = { if (oldTableName != newTableName) { - sqlu"""ALTER TABLE #${qualify(project.dbName, oldTableName)} RENAME TO #${qualify(newTableName)}""" + sqlu"""ALTER TABLE #${qualify(project.dbName, oldTableName)} RENAME TO #${qualify(newTableName)};""" } else { - DatabaseAction.successful(()) + DBIO.successful(()) } } - override def renameColumn(project: Project, tableName: String, oldColumnName: String, newColumnName: String) = { + override def renameColumn(project: Project, tableName: String, oldColumnName: String, newColumnName: String, typeIdentifier: TypeIdentifier) = { if (oldColumnName != newColumnName) { - sqlu"""ALTER TABLE #${qualify(project.dbName, tableName)} RENAME COLUMN #${qualify(oldColumnName)} TO #${qualify(newColumnName)}""" + sqlu"""ALTER TABLE #${qualify(project.dbName, tableName)} RENAME COLUMN #${qualify(oldColumnName)} TO #${qualify(newColumnName)};""" } else { - DatabaseAction.successful(()) + DBIO.successful(()) } } - def rawSQLFromParts( - name: String, - isRequired: Boolean, - isList: Boolean, - typeIdentifier: TypeIdentifier, - isAutoGenerated: Boolean = false, - defaultValue: Option[GCValue] = None - ): String = { + def rawSQLFromParts(name: String, isRequired: Boolean, typeIdentifier: TypeIdentifier, isAutoGenerated: Boolean = false): String = { + val n = typeMapper.esc(name) + val defaultDefaultValue = typeIdentifier match { case TypeIdentifier.Cuid => "defaultid" case TypeIdentifier.DateTime => "''" @@ -288,15 +298,11 @@ case class SQLiteJdbcDeployDatabaseMutationBuilder( case TypeIdentifier.Json => "''" case TypeIdentifier.Relation => "''" } + val nullable = if (isRequired) "NOT NULL" else "NULL" val generated = if (isAutoGenerated) "AUTO_INCREMENT" else "" - val ty = typeMapper.rawSqlTypeForScalarTypeIdentifier(isList, typeIdentifier) - val default = defaultValue match { - case None if !isRequired => "" - case Some(d) if !isRequired => s"DEFAULT ${d.value}" - case Some(d) if isRequired => s"DEFAULT ${d.value}" - case None if isRequired => s"DEFAULT $defaultDefaultValue" - } + val ty = typeMapper.rawSqlTypeForScalarTypeIdentifier(typeIdentifier) + val default = if (isRequired) s"DEFAULT $defaultDefaultValue" else "" s"$n $ty $nullable $default $generated" } diff --git a/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/database/SQLiteTypeMapper.scala b/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/database/SQLiteTypeMapper.scala index 01bbf41eae..252265eaef 100644 --- a/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/database/SQLiteTypeMapper.scala +++ b/server/connectors/deploy-connector-sqlite/src/main/scala/com/prisma/deploy/connector/sqlite/database/SQLiteTypeMapper.scala @@ -10,18 +10,14 @@ case class SQLiteTypeMapper() extends TypeMapper { override def rawSQLFromParts( name: String, isRequired: Boolean, - isList: Boolean, typeIdentifier: TypeIdentifier, isAutoGenerated: Boolean = false, - defaultValue: Option[GCValue] = None )(implicit dsl: DSLContext): String = { val n = esc(name) val nullable = if (isRequired) "NOT NULL" else "NULL" val generated = if (isAutoGenerated) "AUTO_INCREMENT" else "" - val ty = rawSqlTypeForScalarTypeIdentifier(isList, typeIdentifier) - val default = defaultValue.map(d => s"DEFAULT ${d.value}").getOrElse("") - - s"$n $ty $nullable $default $generated" + val ty = rawSqlTypeForScalarTypeIdentifier(typeIdentifier) + s"$n $ty $nullable $generated" } // note: utf8mb4 requires up to 4 bytes per character and includes full utf8 support, including emoticons @@ -30,8 +26,7 @@ case class SQLiteTypeMapper() extends TypeMapper { // We limit enums to 191, and create text indexes over the first 191 characters of the string, but // allow the actual content to be much larger. // Key columns are utf8_general_ci as this collation is ~10% faster when sorting and requires less memory - override def rawSqlTypeForScalarTypeIdentifier(isList: Boolean, t: TypeIdentifier.TypeIdentifier): String = t match { - case _ if isList => "mediumtext " + override def rawSqlTypeForScalarTypeIdentifier(t: TypeIdentifier.TypeIdentifier): String = t match { case TypeIdentifier.String => "mediumtext " case TypeIdentifier.Boolean => "boolean" case TypeIdentifier.Int => "int" diff --git a/server/connectors/deploy-connector/src/main/scala/com/prisma/deploy/connector/DeployMutaction.scala b/server/connectors/deploy-connector/src/main/scala/com/prisma/deploy/connector/DeployMutaction.scala index 18e32214ec..eb1b781552 100644 --- a/server/connectors/deploy-connector/src/main/scala/com/prisma/deploy/connector/DeployMutaction.scala +++ b/server/connectors/deploy-connector/src/main/scala/com/prisma/deploy/connector/DeployMutaction.scala @@ -9,16 +9,16 @@ sealed trait DeployMutaction { case class TruncateProject(project: Project) extends DeployMutaction { val projectId = project.id } case class CreateColumn(project: Project, model: Model, field: ScalarField) extends DeployMutaction -case class DeleteColumn(project: Project, model: Model, field: ScalarField) extends DeployMutaction +case class DeleteColumn(project: Project, oldModel: Model, field: ScalarField) extends DeployMutaction case class UpdateColumn(project: Project, model: Model, oldField: ScalarField, newField: ScalarField) extends DeployMutaction case class CreateScalarListTable(project: Project, model: Model, field: ScalarField) extends DeployMutaction case class DeleteScalarListTable(project: Project, model: Model, field: ScalarField) extends DeployMutaction case class UpdateScalarListTable(project: Project, oldModel: Model, newModel: Model, oldField: ScalarField, newField: ScalarField) extends DeployMutaction -case class CreateModelTable(project: Project, model: Model) extends DeployMutaction -case class DeleteModelTable(project: Project, model: Model, nameOfIdField: String, scalarListFields: Vector[String]) extends DeployMutaction // delete/truncate collection based on migrations setting in server config -case class RenameTable(project: Project, previousName: String, nextName: String, scalarListFieldsNames: Vector[String]) extends DeployMutaction // rename based on migration setting +case class CreateModelTable(project: Project, model: Model) extends DeployMutaction +case class DeleteModelTable(project: Project, model: Model, nameOfIdField: String, scalarListFields: Vector[String]) extends DeployMutaction // delete/truncate collection based on migrations setting in server config +case class UpdateModelTable(project: Project, oldModel: Model, newModel: Model) extends DeployMutaction // rename based on migration setting case class CreateRelationTable(project: Project, relation: Relation) extends DeployMutaction case class DeleteRelationTable(project: Project, relation: Relation) extends DeployMutaction // based on migration settings; set relation fields to null in document diff --git a/server/connectors/deploy-connector/src/main/scala/com/prisma/deploy/connector/MigrationStepMapperImpl.scala b/server/connectors/deploy-connector/src/main/scala/com/prisma/deploy/connector/MigrationStepMapperImpl.scala index c018c54911..b18a25675c 100644 --- a/server/connectors/deploy-connector/src/main/scala/com/prisma/deploy/connector/MigrationStepMapperImpl.scala +++ b/server/connectors/deploy-connector/src/main/scala/com/prisma/deploy/connector/MigrationStepMapperImpl.scala @@ -15,9 +15,9 @@ case class MigrationStepMapperImpl(project: Project) extends MigrationStepMapper Vector(DeleteModelTable(project, model, model.dbNameOfIdField_!, scalarListFieldNames)) case x: UpdateModel => - val model = nextSchema.getModelByName(x.newName).getOrElse(nextSchema.getModelByName_!(x.newName.substring(2))) - val scalarListFieldNames = model.scalarListFields.map(_.name).toVector - Vector(RenameTable(project = project, previousName = x.name, nextName = x.newName, scalarListFieldsNames = scalarListFieldNames)) + val oldModel = previousSchema.getModelByName_!(x.name) + val newModel = nextSchema.getModelByName_!(x.newName) + Vector(UpdateModelTable(project = project, oldModel = oldModel, newModel = newModel)) case x: CreateField => val model = nextSchema.getModelByName_!(x.model) @@ -41,36 +41,31 @@ case class MigrationStepMapperImpl(project: Project) extends MigrationStepMapper } case x: UpdateField => - val oldModel = previousSchema.getModelByName_!(x.model) - val newModel = nextSchema.getModelByName_!(x.newModel) - val next = nextSchema.getFieldByName_!(x.newModel, x.finalName) - val previous = previousSchema.getFieldByName_!(x.model, x.name) - lazy val temporaryNext = next.asScalarField_!.copy(name = next.name + "_prisma_tmp", manifestation = None) - + val oldModel = previousSchema.getModelByName_!(x.model) + val newModel = nextSchema.getModelByName_!(x.newModel) + val next = nextSchema.getFieldByName_!(x.newModel, x.finalName) + val previous = previousSchema.getFieldByName_!(x.model, x.name) lazy val createColumn = CreateColumn(project, oldModel, next.asScalarField_!) lazy val updateColumn = UpdateColumn(project, oldModel, previous.asScalarField_!, next.asScalarField_!) lazy val deleteColumn = DeleteColumn(project, oldModel, previous.asScalarField_!) lazy val createScalarListTable = CreateScalarListTable(project, oldModel, next.asScalarField_!) lazy val deleteScalarListTable = DeleteScalarListTable(project, oldModel, previous.asScalarField_!) lazy val updateScalarListTable = UpdateScalarListTable(project, oldModel, newModel, previous.asScalarField_!, next.asScalarField_!) - lazy val createTemporaryColumn = createColumn.copy(field = temporaryNext) - lazy val renameTemporaryColumn = UpdateColumn(project, oldModel, temporaryNext, next.asScalarField_!) // TODO: replace that with a pattern match based on the subtypes of `models.Field` () match { - case _ if previous.isRelation && next.isRelation => Vector.empty - case _ if previous.isRelation && next.isScalarNonList => Vector(createColumn) - case _ if previous.isRelation && next.isScalarList => Vector(createScalarListTable) - case _ if previous.isScalarList && next.isScalarNonList => Vector(createColumn, deleteScalarListTable) - case _ if previous.isScalarList && next.isRelation => Vector(deleteScalarListTable) - case _ if previous.isScalarNonList && next.isScalarList => Vector(createScalarListTable, deleteColumn) - case _ if previous.isScalarNonList && next.isRelation => Vector(deleteColumn) - case _ if previous.isScalarNonList && next.isScalarNonList && previous.typeIdentifier == next.typeIdentifier => Vector(updateColumn) - case _ if previous.isScalarList && next.isScalarList && previous.typeIdentifier == next.typeIdentifier => Vector(updateScalarListTable) - case _ if previous.isScalarList && next.isScalarList => Vector(deleteScalarListTable, createScalarListTable) + case _ if previous.isRelation && next.isRelation => Vector.empty + case _ if previous.isRelation && next.isScalarNonList => Vector(createColumn) + case _ if previous.isRelation && next.isScalarList => Vector(createScalarListTable) + case _ if previous.isScalarList && next.isScalarNonList => Vector(createColumn, deleteScalarListTable) + case _ if previous.isScalarList && next.isRelation => Vector(deleteScalarListTable) + case _ if previous.isScalarList && next.isScalarList && previous.typeIdentifier == next.typeIdentifier => Vector(updateScalarListTable) + case _ if previous.isScalarList && next.isScalarList => Vector(deleteScalarListTable, createScalarListTable) + case _ if previous.isScalarNonList && next.isRelation => Vector(deleteColumn) + case _ if previous.isScalarNonList && next.isScalarList => Vector(createScalarListTable, deleteColumn) case _ if previous.isScalarNonList && next.isScalarNonList => + val common = Vector(updateColumn) val isIdTypeChange = previous.asScalarField_!.isId && next.asScalarField_!.isId && previous.asScalarField_!.typeIdentifier != next.asScalarField_!.typeIdentifier - val common = Vector(createTemporaryColumn, deleteColumn, renameTemporaryColumn) // a table might have temporary no columns. MySQL does not allow this. //Fixme this breaks for SQLITE if (isIdTypeChange) { val deleteRelations = previousSchema.relations.filter(_.containsTheModel(previous.model)).map(deleteRelation).toVector val recreateRelations = nextSchema.relations diff --git a/server/connectors/utils/src/main/scala/com/prisma/connectors/utils/ConnectorLoader.scala b/server/connectors/utils/src/main/scala/com/prisma/connectors/utils/ConnectorLoader.scala index a1ed89818a..40e32378de 100644 --- a/server/connectors/utils/src/main/scala/com/prisma/connectors/utils/ConnectorLoader.scala +++ b/server/connectors/utils/src/main/scala/com/prisma/connectors/utils/ConnectorLoader.scala @@ -20,7 +20,7 @@ object ConnectorLoader { case ("mysql", true) => MySqlApiConnector(databaseConfig, drivers(SupportedDrivers.MYSQL), config.isPrototype) case ("mysql", false) => sys.error("There is not passive mysql deploy connector yet!") case ("postgres", isActive) => PostgresApiConnector(databaseConfig, drivers(SupportedDrivers.POSTGRES), isActive, config.isPrototype) - case ("sqlite", true) => SQLiteApiConnector(databaseConfig, isPrototype = config.isPrototype) + case ("sqlite", true) => SQLiteApiConnector(databaseConfig, drivers(SupportedDrivers.SQLITE), isPrototype = config.isPrototype) case ("sqlite", false) => sys.error("There is no passive sqlite deploy connector yet!") case ("mongo", _) => MongoApiConnector(databaseConfig) case (conn, _) => sys.error(s"Unknown connector $conn") @@ -34,7 +34,7 @@ object ConnectorLoader { case ("mysql", false) => sys.error("There is not passive mysql deploy connector yet!") case ("postgres", isActive) => PostgresDeployConnector(databaseConfig, drivers(SupportedDrivers.POSTGRES), isActive, config.isPrototype) case ("mongo", isActive) => MongoDeployConnector(databaseConfig, isActive = true, isTest = isTest) - case ("sqlite", true) => SQLiteDeployConnector(databaseConfig, isPrototype = config.isPrototype) + case ("sqlite", true) => SQLiteDeployConnector(databaseConfig, drivers(SupportedDrivers.SQLITE), isPrototype = config.isPrototype) case ("sqlite", false) => sys.error("There is no passive sqlite deploy connector yet!") case (conn, _) => sys.error(s"Unknown connector $conn") } diff --git a/server/connectors/utils/src/main/scala/com/prisma/connectors/utils/SupportedDrivers.scala b/server/connectors/utils/src/main/scala/com/prisma/connectors/utils/SupportedDrivers.scala index 0cbd34fbd2..7f20f57ef5 100644 --- a/server/connectors/utils/src/main/scala/com/prisma/connectors/utils/SupportedDrivers.scala +++ b/server/connectors/utils/src/main/scala/com/prisma/connectors/utils/SupportedDrivers.scala @@ -1,19 +1,19 @@ package com.prisma.connectors.utils import java.sql.Driver -import com.prisma.connectors.utils.SupportedDrivers.DBFamiliy +import com.prisma.connectors.utils.SupportedDrivers.DBFamily object SupportedDrivers extends Enumeration { - type DBFamiliy = Value - val MYSQL, POSTGRES, MONGO = Value + type DBFamily = Value + val MYSQL, POSTGRES, MONGO, SQLITE = Value - def apply(mapping: (DBFamiliy, Driver)*): SupportedDrivers = new SupportedDrivers(mapping.toMap) + def apply(mapping: (DBFamily, Driver)*): SupportedDrivers = new SupportedDrivers(mapping.toMap) } -case class DBFamiliyNotSupported(familiy: DBFamiliy) - extends Exception(s"DB family ${familiy.toString.toLowerCase().capitalize} is not supported by this Prisma instance.") +case class DBFamilyNotSupported(family: DBFamily) + extends Exception(s"DB family ${family.toString.toLowerCase().capitalize} is not supported by this Prisma instance.") -case class SupportedDrivers(mapping: Map[DBFamiliy, Driver]) { - def apply(familiy: DBFamiliy): Driver = mapping.getOrElse(familiy, throw DBFamiliyNotSupported(familiy)) - def get(familiy: DBFamiliy): Option[Driver] = mapping.get(familiy) +case class SupportedDrivers(mapping: Map[DBFamily, Driver]) { + def apply(family: DBFamily): Driver = mapping.getOrElse(family, throw DBFamilyNotSupported(family)) + def get(family: DBFamily): Option[Driver] = mapping.get(family) } diff --git a/server/docker-compose/sqlite/dev-sqlite.yml b/server/docker-compose/sqlite/dev-sqlite.yml deleted file mode 100644 index 14b4a3938d..0000000000 --- a/server/docker-compose/sqlite/dev-sqlite.yml +++ /dev/null @@ -1,2 +0,0 @@ -# Transient db - will lose it's data once restarted -version: "3" \ No newline at end of file diff --git a/server/docker-compose/sqlite/prisma.yml b/server/docker-compose/sqlite/prisma.yml index 046ab91b0b..c158432c36 100644 --- a/server/docker-compose/sqlite/prisma.yml +++ b/server/docker-compose/sqlite/prisma.yml @@ -1,4 +1,5 @@ port: 4466 +prototype: true databases: default: connector: sqlite @@ -7,4 +8,4 @@ databases: port: 3306 user: root password: prisma - rawAccess: true \ No newline at end of file + rawAccess: true diff --git a/server/images/prisma-local/src/main/scala/com/prisma/local/PrismaLocalDependencies.scala b/server/images/prisma-local/src/main/scala/com/prisma/local/PrismaLocalDependencies.scala index 1748140cf2..9e3993873a 100644 --- a/server/images/prisma-local/src/main/scala/com/prisma/local/PrismaLocalDependencies.scala +++ b/server/images/prisma-local/src/main/scala/com/prisma/local/PrismaLocalDependencies.scala @@ -39,6 +39,7 @@ case class PrismaLocalDependencies()(implicit val system: ActorSystem, val mater implicit val supportedDrivers: SupportedDrivers = SupportedDrivers( SupportedDrivers.MYSQL -> new org.mariadb.jdbc.Driver, SupportedDrivers.POSTGRES -> new org.postgresql.Driver, + SupportedDrivers.SQLITE -> new org.sqlite.JDBC ) override implicit def self = this diff --git a/server/images/prisma-prod/src/main/scala/com/prisma/prod/PrismaProdDependencies.scala b/server/images/prisma-prod/src/main/scala/com/prisma/prod/PrismaProdDependencies.scala index 274d1dd35b..362fd11ba9 100644 --- a/server/images/prisma-prod/src/main/scala/com/prisma/prod/PrismaProdDependencies.scala +++ b/server/images/prisma-prod/src/main/scala/com/prisma/prod/PrismaProdDependencies.scala @@ -41,6 +41,7 @@ case class PrismaProdDependencies()(implicit val system: ActorSystem, val materi implicit val supportedDrivers: SupportedDrivers = SupportedDrivers( SupportedDrivers.MYSQL -> new org.mariadb.jdbc.Driver, SupportedDrivers.POSTGRES -> new org.postgresql.Driver, + SupportedDrivers.SQLITE -> new org.sqlite.JDBC ) override implicit lazy val executionContext: ExecutionContext = system.dispatcher diff --git a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/ChangingFromRelationToScalarOrBackSpec.scala b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/ChangingFromRelationToScalarOrBackSpec.scala index 06d4aaaf71..581442e7b2 100644 --- a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/ChangingFromRelationToScalarOrBackSpec.scala +++ b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/ChangingFromRelationToScalarOrBackSpec.scala @@ -1,10 +1,11 @@ package com.prisma.integration +import com.prisma.IgnoreSQLite import org.scalatest.{FlatSpec, Matchers} class ChangingFromRelationToScalarOrBackSpec extends FlatSpec with Matchers with IntegrationBaseSpec { - "Changing a field from scalar to relation" should "work when there is no data yet" in { + "Changing a field from scalar to relation" should "work when there is no data yet" taggedAs (IgnoreSQLite) in { val schema = """type A { @@ -31,7 +32,7 @@ class ChangingFromRelationToScalarOrBackSpec extends FlatSpec with Matchers with deployServer.deploySchema(project, schema1) } - "Changing a field from scalar to relation" should "work when there is already data and should delete the old column" in { + "Changing a field from scalar to relation" should "work when there is already data and should delete the old column" taggedAs (IgnoreSQLite) in { val schema = """type A { diff --git a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/ChangingModelsOfRelationsSpec.scala b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/ChangingModelsOfRelationsSpec.scala index 3894a0e6c9..7f98b021b2 100644 --- a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/ChangingModelsOfRelationsSpec.scala +++ b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/ChangingModelsOfRelationsSpec.scala @@ -1,5 +1,6 @@ package com.prisma.integration +import com.prisma.IgnoreSQLite import org.scalatest.{FlatSpec, Matchers} class ChangingModelsOfRelationsSpec extends FlatSpec with Matchers with IntegrationBaseSpec { @@ -81,7 +82,7 @@ class ChangingModelsOfRelationsSpec extends FlatSpec with Matchers with Integrat as.toString should be("""{"data":{"as":[{"a":"A","b":null}]}}""") } - "Renaming a model with @rename but keeping its relation" should "work" in { + "Renaming a model with @rename but keeping its relation" should "work" taggedAs (IgnoreSQLite) in { val schema = """type A { diff --git a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/DeployingUniqueConstraintSpec.scala b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/DeployingUniqueConstraintSpec.scala index 424ff3476f..c0209d0390 100644 --- a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/DeployingUniqueConstraintSpec.scala +++ b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/DeployingUniqueConstraintSpec.scala @@ -1,5 +1,6 @@ package com.prisma.integration +import com.prisma.IgnoreSQLite import org.scalatest.{FlatSpec, Matchers} class DeployingUniqueConstraintSpec extends FlatSpec with Matchers with IntegrationBaseSpec { @@ -27,7 +28,7 @@ class DeployingUniqueConstraintSpec extends FlatSpec with Matchers with Integrat """{"data":{"deploy":{"migration":null,"errors":[{"description":"You are making a field unique, but there are already nodes that would violate that constraint."}],"warnings":[]}}}""") } - "Adding a unique constraint without violating data" should "work" in { + "Adding a unique constraint without violating data" should "work" taggedAs (IgnoreSQLite) in { val schema = """type Team { | name: String! @unique @@ -48,7 +49,7 @@ class DeployingUniqueConstraintSpec extends FlatSpec with Matchers with Integrat deployServer.deploySchemaThatMustSucceed(project, schema1, 3) } - "Adding a unique constraint without violating data" should "work even with multiple nulls" in { + "Adding a unique constraint without violating data" should "work even with multiple nulls" taggedAs (IgnoreSQLite) in { val schema = """type Team { | name: String! @unique @@ -137,7 +138,7 @@ class DeployingUniqueConstraintSpec extends FlatSpec with Matchers with Integrat deployServer.deploySchemaThatMustSucceed(project, schema1, 3) } - "Removing a unique constraint" should "work" in { + "Removing a unique constraint" should "work" taggedAs (IgnoreSQLite) in { val schema = """type Team { | name: String! @unique diff --git a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/IntegrationBaseSpec.scala b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/IntegrationBaseSpec.scala index 1fd1971de6..e408afe5ee 100644 --- a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/IntegrationBaseSpec.scala +++ b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/IntegrationBaseSpec.scala @@ -2,11 +2,13 @@ package com.prisma.integration import akka.actor.ActorSystem import akka.stream.ActorMaterializer +import com.prisma.ConnectorAwareTest import com.prisma.api.connector.DataResolver import com.prisma.api.util.StringMatchers import com.prisma.api.{ApiTestServer, TestApiDependenciesImpl} -import com.prisma.deploy.specutils.{TestDeployDependencies, DeployTestServer} -import com.prisma.shared.models.{Migration, Project} +import com.prisma.config.PrismaConfig +import com.prisma.deploy.specutils.{DeployTestServer, TestDeployDependencies} +import com.prisma.shared.models.{ConnectorCapabilities, Migration, Project} import com.prisma.utils.await.AwaitUtils import com.prisma.utils.json.PlayJsonExtensions import cool.graph.cuid.Cuid @@ -15,7 +17,13 @@ import play.api.libs.json.JsString import scala.collection.mutable.ArrayBuffer -trait IntegrationBaseSpec extends BeforeAndAfterEach with BeforeAndAfterAll with PlayJsonExtensions with AwaitUtils with StringMatchers { self: Suite => +trait IntegrationBaseSpec + extends BeforeAndAfterEach + with BeforeAndAfterAll + with PlayJsonExtensions + with AwaitUtils + with StringMatchers + with ConnectorAwareTest { self: Suite => implicit lazy val system = ActorSystem() implicit lazy val materializer = ActorMaterializer() @@ -34,6 +42,9 @@ trait IntegrationBaseSpec extends BeforeAndAfterEach with BeforeAndAfterAll with def dataResolver(project: Project): DataResolver = apiTestDependencies.dataResolver(project) + override def capabilities: ConnectorCapabilities = apiTestDependencies.deployConnector.capabilities + + override def prismaConfig: PrismaConfig = apiTestDependencies.config // DEPLOY implicit lazy val deployTestDependencies: TestDeployDependencies = TestDeployDependencies() diff --git a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/RenamingWithExistingDataSpec.scala b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/RenamingWithExistingDataSpec.scala index 7eae9ef4d6..1a3632656f 100644 --- a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/RenamingWithExistingDataSpec.scala +++ b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/RenamingWithExistingDataSpec.scala @@ -1,10 +1,11 @@ package com.prisma.integration +import com.prisma.IgnoreSQLite import org.scalatest.{FlatSpec, Matchers} class RenamingWithExistingDataSpec extends FlatSpec with Matchers with IntegrationBaseSpec { - "Renaming a model" should "work" in { + "Renaming a model" should "work" taggedAs (IgnoreSQLite) in { val schema = """type A { @@ -26,7 +27,7 @@ class RenamingWithExistingDataSpec extends FlatSpec with Matchers with Integrati bs.toString should be("""{"data":{"bs":[{"a":"A"}]}}""") } - "Renaming a model with a scalar list " should "work" in { + "Renaming a model with a scalar list " should "work" taggedAs (IgnoreSQLite) in { val schema = """type A { @@ -50,7 +51,7 @@ class RenamingWithExistingDataSpec extends FlatSpec with Matchers with Integrati bs.toString should be("""{"data":{"bs":[{"a":"A"}]}}""") } - "Renaming a field" should "work" in { + "Renaming a field" should "work" taggedAs (IgnoreSQLite) in { val schema = """type A { @@ -72,7 +73,7 @@ class RenamingWithExistingDataSpec extends FlatSpec with Matchers with Integrati bs.toString should be("""{"data":{"as":[{"b":"A"}]}}""") } - "Renaming a relation with oldName on both sides" should "work" in { + "Renaming a relation with oldName on both sides" should "work" taggedAs (IgnoreSQLite) in { val schema = """type A { @@ -106,7 +107,7 @@ class RenamingWithExistingDataSpec extends FlatSpec with Matchers with Integrati as.toString should be("""{"data":{"as":[{"b":{"b":"B1"}}]}}""") } - "Renaming a model and field" should "work" in { + "Renaming a model and field" should "work" taggedAs (IgnoreSQLite) in { val schema = """type A { @@ -128,7 +129,7 @@ class RenamingWithExistingDataSpec extends FlatSpec with Matchers with Integrati bs.toString should be("""{"data":{"bs":[{"b":"A"}]}}""") } - "Renaming a model and a relation with oldName on both sides" should "work" in { + "Renaming a model and a relation with oldName on both sides" should "work" taggedAs (IgnoreSQLite) in { val schema = """type A { @@ -162,7 +163,7 @@ class RenamingWithExistingDataSpec extends FlatSpec with Matchers with Integrati as.toString should be("""{"data":{"cs":[{"b":{"b":"B1"}}]}}""") } - "Renaming a field and a relation with oldName on both sides" should "work" in { + "Renaming a field and a relation with oldName on both sides" should "work" taggedAs (IgnoreSQLite) in { val schema = """type A { @@ -196,14 +197,16 @@ class RenamingWithExistingDataSpec extends FlatSpec with Matchers with Integrati as.toString should be("""{"data":{"as":[{"bNew":{"b":"B1"}}]}}""") } - "Renaming models by switching the names of two existing models" should "work even when there is existing data" in { + "Renaming models by switching the names of two existing models" should "error and ask to be split in two parts" in { val schema = """type A { + | id: ID! @unique | a: String! @unique |} | |type B { + | id: ID! @unique | b: String @unique |}""" @@ -214,19 +217,19 @@ class RenamingWithExistingDataSpec extends FlatSpec with Matchers with Integrati val schema1 = """type B @rename(oldName: "A"){ + | id: ID! @unique | a: String! @unique |} | |type A @rename(oldName: "B"){ + | id: ID! @unique | b: String @unique |}""" - val updatedProject = deployServer.deploySchema(project, schema1) + val updatedProject = deployServer.deploySchemaThatMustError(project, schema1) - val as = apiServer.query("""{as{b}}""", updatedProject) - as.toString should be("""{"data":{"as":[{"b":"B"}]}}""") - val bs = apiServer.query("""{bs{a}}""", updatedProject) - bs.toString should be("""{"data":{"bs":[{"a":"A"}]}}""") + updatedProject.toString() should be( + """{"data":{"deploy":{"migration":null,"errors":[{"description":"You renamed type `A` to `B`. But that is the old name of type `A`. Please do this in two steps."},{"description":"You renamed type `B` to `A`. But that is the old name of type `B`. Please do this in two steps."}],"warnings":[]}}}""") } // these will be fixed when we implement a migration workflow diff --git a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/SeveralRelationsBetweenSameModelsIntegrationSpec.scala b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/SeveralRelationsBetweenSameModelsIntegrationSpec.scala index 8cb89d0ae7..fd0ec7d55a 100644 --- a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/SeveralRelationsBetweenSameModelsIntegrationSpec.scala +++ b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/SeveralRelationsBetweenSameModelsIntegrationSpec.scala @@ -1,5 +1,6 @@ package com.prisma.integration +import com.prisma.IgnoreSQLite import org.scalatest.{FlatSpec, Matchers} class SeveralRelationsBetweenSameModelsIntegrationSpec extends FlatSpec with Matchers with IntegrationBaseSpec { @@ -117,7 +118,7 @@ class SeveralRelationsBetweenSameModelsIntegrationSpec extends FlatSpec with Mat updatedProject.schema.relations.head.name should be("""NewName""") } - "DeployMutation" should "be able to handle renaming relations that don't have a name yet" in { + "DeployMutation" should "be able to handle renaming relations that don't have a name yet" taggedAs (IgnoreSQLite) in { val schema = """type A { @@ -162,7 +163,7 @@ class SeveralRelationsBetweenSameModelsIntegrationSpec extends FlatSpec with Mat unchangedRelationContent.toString should be("""{"data":{"as":[{"title":"A1","b1":{"title":"B1"}}]}}""") } - "DeployMutation" should "be able to handle renaming relations that are already named" in { + "DeployMutation" should "be able to handle renaming relations that are already named" taggedAs (IgnoreSQLite) in { val schema = """type A { @@ -290,7 +291,7 @@ class SeveralRelationsBetweenSameModelsIntegrationSpec extends FlatSpec with Mat unchangedRelationContent.toString should be("""{"data":{"as":[{"title":"A1","b":{"title":"B1"}}]}}""") } - "Going from two named relations between the same models to one named one without a backrelation" should "work even when there is a rename" in { + "Going from two named relations between the same models to one named one without a backrelation" should "work even when there is a rename" taggedAs (IgnoreSQLite) in { val schema = """type A { diff --git a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/DeleteEnumDeploySpec.scala b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/DeleteEnumDeploySpec.scala index 875a985a3c..34157a48e7 100644 --- a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/DeleteEnumDeploySpec.scala +++ b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/DeleteEnumDeploySpec.scala @@ -1,11 +1,12 @@ package com.prisma.integration.deploychecks +import com.prisma.IgnoreSQLite import com.prisma.integration.IntegrationBaseSpec import org.scalatest.{FlatSpec, Matchers} class DeleteEnumDeploySpec extends FlatSpec with Matchers with IntegrationBaseSpec { - "Deleting an Enum" should "not throw a warning if there is no data yet" in { + "Deleting an Enum" should "not throw a warning if there is no data yet" taggedAs (IgnoreSQLite) in { val schema = """|type A { @@ -54,7 +55,7 @@ class DeleteEnumDeploySpec extends FlatSpec with Matchers with IntegrationBaseSp """{"data":{"deploy":{"migration":null,"errors":[],"warnings":[{"description":"You already have nodes for this model. This change may result in data loss."}]}}}""") } - "Deleting an Enum" should "throw a warning if there is already data but proceed with -force" in { + "Deleting an Enum" should "throw a warning if there is already data but proceed with -force" taggedAs (IgnoreSQLite) in { val schema = """|type A { diff --git a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/DeleteFieldDeploySpec.scala b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/DeleteFieldDeploySpec.scala index 77c42b05ae..33f2ae8c14 100644 --- a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/DeleteFieldDeploySpec.scala +++ b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/DeleteFieldDeploySpec.scala @@ -1,5 +1,6 @@ package com.prisma.integration.deploychecks +import com.prisma.{ConnectorAwareTest, IgnoreSQLite} import com.prisma.integration.IntegrationBaseSpec import org.scalatest.{FlatSpec, Matchers} @@ -27,7 +28,7 @@ class DeleteFieldDeploySpec extends FlatSpec with Matchers with IntegrationBaseS """{"data":{"deploy":{"migration":null,"errors":[],"warnings":[{"description":"You already have nodes for this model. This change may result in data loss."}]}}}""") } - "Deleting a field" should "throw a warning if nodes are present but proceed with -force flag" in { + "Deleting a field" should "throw a warning if nodes are present but proceed with -force flag" taggedAs (IgnoreSQLite) in { val schema = """type A { @@ -49,7 +50,7 @@ class DeleteFieldDeploySpec extends FlatSpec with Matchers with IntegrationBaseS """{"data":{"deploy":{"migration":{"applied":0,"revision":3},"errors":[],"warnings":[{"description":"You already have nodes for this model. This change may result in data loss."}]}}}""") } - "Deleting a field" should "succeed if no nodes are present" in { + "Deleting a field" should "succeed if no nodes are present" taggedAs (IgnoreSQLite) in { val schema = """type A { diff --git a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/UpdateFieldDeploySpec.scala b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/UpdateFieldDeploySpec.scala index c2c792b2ad..dbca973cbb 100644 --- a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/UpdateFieldDeploySpec.scala +++ b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/UpdateFieldDeploySpec.scala @@ -1,5 +1,6 @@ package com.prisma.integration.deploychecks +import com.prisma.IgnoreSQLite import com.prisma.integration.IntegrationBaseSpec import org.scalatest.{FlatSpec, Matchers} @@ -27,7 +28,7 @@ class UpdateFieldDeploySpec extends FlatSpec with Matchers with IntegrationBaseS """{"data":{"deploy":{"migration":null,"errors":[],"warnings":[{"description":"You already have nodes for this model. This change may result in data loss."}]}}}""") } - "Updating a field from scalar non-list to scalar list" should "throw a warning if there is already data but proceed with -force" in { + "Updating a field from scalar non-list to scalar list" should "throw a warning if there is already data but proceed with -force" taggedAs (IgnoreSQLite) in { val schema = """|type A { @@ -49,7 +50,7 @@ class UpdateFieldDeploySpec extends FlatSpec with Matchers with IntegrationBaseS """{"data":{"deploy":{"migration":{"applied":0,"revision":3},"errors":[],"warnings":[{"description":"You already have nodes for this model. This change may result in data loss."}]}}}""") } - "Updating a field from scalar non-list to scalar list" should "succeed if there is no data yet" in { + "Updating a field from scalar non-list to scalar list" should "succeed if there is no data yet" taggedAs (IgnoreSQLite) in { val schema = """|type A { @@ -132,7 +133,7 @@ class UpdateFieldDeploySpec extends FlatSpec with Matchers with IntegrationBaseS """{"data":{"deploy":{"migration":null,"errors":[],"warnings":[{"description":"You already have nodes for this model. This change may result in data loss."}]}}}""") } - "Updating a field from string to int" should "throw a warning if there is already data but proceed with -force" in { + "Updating a field from string to int" should "throw a warning if there is already data but proceed with -force" taggedAs (IgnoreSQLite) in { val schema = """|type A { @@ -154,7 +155,7 @@ class UpdateFieldDeploySpec extends FlatSpec with Matchers with IntegrationBaseS """{"data":{"deploy":{"migration":{"applied":0,"revision":3},"errors":[],"warnings":[{"description":"You already have nodes for this model. This change may result in data loss."}]}}}""") } - "Updating a field from string to int" should "not throw a warning if there is no data yet" in { + "Updating a field from string to int" should "not throw a warning if there is no data yet" taggedAs (IgnoreSQLite) in { val schema = """|type A { @@ -195,7 +196,7 @@ class UpdateFieldDeploySpec extends FlatSpec with Matchers with IntegrationBaseS """{"data":{"deploy":{"migration":null,"errors":[],"warnings":[{"description":"You already have nodes for this model. This change may result in data loss."}]}}}""") } - "Updating a field from string to a relation" should "throw a warning if there is already data but proceed with -force" in { + "Updating a field from string to a relation" should "throw a warning if there is already data but proceed with -force" taggedAs (IgnoreSQLite) in { val schema = """|type A { @@ -217,7 +218,7 @@ class UpdateFieldDeploySpec extends FlatSpec with Matchers with IntegrationBaseS """{"data":{"deploy":{"migration":{"applied":0,"revision":3},"errors":[],"warnings":[{"description":"You already have nodes for this model. This change may result in data loss."}]}}}""") } - "Updating a field from string to a relation" should "not throw a warning if there is no data yet" in { + "Updating a field from string to a relation" should "not throw a warning if there is no data yet" taggedAs (IgnoreSQLite) in { val schema = """|type A { @@ -236,7 +237,7 @@ class UpdateFieldDeploySpec extends FlatSpec with Matchers with IntegrationBaseS deployServer.deploySchemaThatMustSucceed(project, schema2, 3) } - "Updating a scalar field to required" should "not throw an error if there is no data yet" in { + "Updating a scalar field to required" should "not throw an error if there is no data yet" taggedAs (IgnoreSQLite) in { val schema = """|type A { @@ -255,7 +256,7 @@ class UpdateFieldDeploySpec extends FlatSpec with Matchers with IntegrationBaseS deployServer.deploySchemaThatMustSucceed(project, schema2, 3) } - "Updating a scalar field to required" should "not throw an error if all required fields already have data" in { + "Updating a scalar field to required" should "not throw an error if all required fields already have data" taggedAs (IgnoreSQLite) in { val schema = """|type A { diff --git a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/UpdateModelDeploySpec.scala b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/UpdateModelDeploySpec.scala index 400b4b6193..224a3a936d 100644 --- a/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/UpdateModelDeploySpec.scala +++ b/server/integration-tests/integration-tests-mysql/src/test/scala/com/prisma/integration/deploychecks/UpdateModelDeploySpec.scala @@ -1,10 +1,11 @@ package com.prisma.integration.deploychecks +import com.prisma.IgnoreSQLite import com.prisma.integration.IntegrationBaseSpec import org.scalatest.{FlatSpec, Matchers} class UpdateModelDeploySpec extends FlatSpec with Matchers with IntegrationBaseSpec { - "Updating a model by changing its name" should "succeed even when there are nodes" in { + "Updating a model by changing its name" should "succeed even when there are nodes" taggedAs (IgnoreSQLite) in { val schema = """type A { diff --git a/server/servers/api/src/test/scala/com/prisma/api/TestApiDependencies.scala b/server/servers/api/src/test/scala/com/prisma/api/TestApiDependencies.scala index 9d881bb3d2..ad8acbb79d 100644 --- a/server/servers/api/src/test/scala/com/prisma/api/TestApiDependencies.scala +++ b/server/servers/api/src/test/scala/com/prisma/api/TestApiDependencies.scala @@ -42,7 +42,8 @@ case class TestApiDependenciesImpl()(implicit val system: ActorSystem, val mater CustomJdbcDriver.jna } else { new org.postgresql.Driver - }) + }), + SupportedDrivers.SQLITE -> new org.sqlite.JDBC ) override val cacheFactory: CacheFactory = new CaffeineCacheFactory() diff --git a/server/servers/api/src/test/scala/com/prisma/api/import_export/ListValueImportExportSpec.scala b/server/servers/api/src/test/scala/com/prisma/api/import_export/ListValueImportExportSpec.scala index 6728a62f7e..3466e614b6 100644 --- a/server/servers/api/src/test/scala/com/prisma/api/import_export/ListValueImportExportSpec.scala +++ b/server/servers/api/src/test/scala/com/prisma/api/import_export/ListValueImportExportSpec.scala @@ -60,7 +60,12 @@ class ListValueImportExportSpec extends FlatSpec with Matchers with ApiSpecBase val model = project.schema.getModelByName_!("Model0") val field = model.getFieldByName_!("stringList") - importer.executeImport(lists).await().toString should include(s"Failure inserting into listTable ${model.name}_${field.name} for the id 3 for value ") + val res = importer.executeImport(lists).await().toString + + ifConnectorIsNotSQLite(res should include(s"Failure inserting into listTable ${model.name}_${field.name} for the id 3 for value ")) + ifConnectorIsSQLite(res should include( + s"Failure inserting into listTable ${model.name}_${field.name}: Cause:[SQLITE_CONSTRAINT_FOREIGNKEY] A foreign key constraint failed (FOREIGN KEY constraint failed)")) + } "Exporting nodes" should "work (with filesize limit set to 1000 for test) and preserve the order of items" in { diff --git a/server/servers/api/src/test/scala/com/prisma/api/mutations/CascadingDeleteSpec.scala b/server/servers/api/src/test/scala/com/prisma/api/mutations/CascadingDeleteSpec.scala index 7752f56f07..35e4974daa 100644 --- a/server/servers/api/src/test/scala/com/prisma/api/mutations/CascadingDeleteSpec.scala +++ b/server/servers/api/src/test/scala/com/prisma/api/mutations/CascadingDeleteSpec.scala @@ -1,15 +1,14 @@ package com.prisma.api.mutations import com.prisma.api.ApiSpecBase -import com.prisma.shared.models.ConnectorCapability.JoinRelationLinksCapability +import com.prisma.shared.models.ConnectorCapability._ import com.prisma.shared.models._ import com.prisma.shared.schema_dsl.SchemaDsl import org.scalatest.{FlatSpec, Matchers} class CascadingDeleteSpec extends FlatSpec with Matchers with ApiSpecBase { - override def runOnlyForCapabilities = Set(JoinRelationLinksCapability) - - override def doNotRunForPrototypes: Boolean = true + override def runOnlyForCapabilities = Set(JoinRelationLinksCapability) + override def doNotRunForCapabilities = Set(EmbeddedTypesCapability) //region TOP LEVEL DELETE diff --git a/server/servers/api/src/test/scala/com/prisma/api/mutations/DeadlockSpec.scala b/server/servers/api/src/test/scala/com/prisma/api/mutations/DeadlockSpec.scala index 8a3e177952..c252df1205 100644 --- a/server/servers/api/src/test/scala/com/prisma/api/mutations/DeadlockSpec.scala +++ b/server/servers/api/src/test/scala/com/prisma/api/mutations/DeadlockSpec.scala @@ -10,7 +10,8 @@ import org.scalatest.{FlatSpec, Matchers, Retries} import scala.concurrent.Future class DeadlockSpec extends FlatSpec with Matchers with Retries with ApiSpecBase with AwaitUtils { - override def runOnlyForCapabilities = Set(ScalarListsCapability) + override def doNotRunForPrototypes: Boolean = true + override def runOnlyForCapabilities = Set(ScalarListsCapability) import testDependencies.system.dispatcher diff --git a/server/servers/api/src/test/scala/com/prisma/api/mutations/ExecuteRawSpec.scala b/server/servers/api/src/test/scala/com/prisma/api/mutations/ExecuteRawSpec.scala index 8ebfcd28c7..3f114a5166 100644 --- a/server/servers/api/src/test/scala/com/prisma/api/mutations/ExecuteRawSpec.scala +++ b/server/servers/api/src/test/scala/com/prisma/api/mutations/ExecuteRawSpec.scala @@ -2,21 +2,20 @@ package com.prisma.api.mutations import com.prisma.api.ApiSpecBase import com.prisma.api.connector.jdbc.impl.JdbcDatabaseMutactionExecutor -import com.prisma.shared.models.ConnectorCapability.JoinRelationLinksCapability +import com.prisma.shared.models.ConnectorCapability.{JoinRelationLinksCapability, RawAccessCapability} import com.prisma.shared.models.{ConnectorCapability, Project} import com.prisma.shared.schema_dsl.SchemaDsl import org.jooq.Query import org.jooq.conf.{ParamType, Settings} import org.jooq.impl.DSL import org.jooq.impl.DSL.{field, name, table} -import org.scalatest.{FlatSpec, Matchers, WordSpecLike} +import org.scalatest.{Matchers, WordSpecLike} import play.api.libs.json.{JsString, JsValue} import sangria.util.StringUtil class ExecuteRawSpec extends WordSpecLike with Matchers with ApiSpecBase { - override def doNotRunForPrototypes: Boolean = true - override def runOnlyForCapabilities: Set[ConnectorCapability] = Set(JoinRelationLinksCapability) + override def runOnlyForCapabilities: Set[ConnectorCapability] = Set(JoinRelationLinksCapability, RawAccessCapability) val project: Project = SchemaDsl.fromBuilder { schema => schema.model("Todo").field("title", _.String) @@ -37,6 +36,7 @@ class ExecuteRawSpec extends WordSpecLike with Matchers with ApiSpecBase { lazy val slickDatabase = testDependencies.databaseMutactionExecutor.asInstanceOf[JdbcDatabaseMutactionExecutor].slickDatabase lazy val isMySQL = slickDatabase.isMySql lazy val isPostgres = slickDatabase.isPostgres + lazy val isSQLite = slickDatabase.isSQLite lazy val sql = DSL.using(slickDatabase.dialect, new Settings().withRenderFormatted(true)) lazy val modelTable = table(name(schemaName, model.dbName)) lazy val idColumn = model.idField_!.dbName @@ -127,11 +127,10 @@ class ExecuteRawSpec extends WordSpecLike with Matchers with ApiSpecBase { } "syntactic errors should bubble through to the user" in { - val errorCode = if (isPostgres) 0 else 1064 - val errorContains = if (isPostgres) { - "syntax error at end of input" - } else { - "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near" + val (errorCode, errorContains) = () match { + case _ if isPostgres => (0, "syntax error at end of input") + case _ if isMySQL => (1064, "check the manual that corresponds to your MySQL server version for the right syntax to use near") + case _ if isSQLite => (1, "incomplete input") } server.queryThatMustFail( s"""mutation { @@ -147,13 +146,13 @@ class ExecuteRawSpec extends WordSpecLike with Matchers with ApiSpecBase { } "other errors should also bubble through to the user" in { - val id = createTodo("title") - val errorCode = if (isPostgres) 0 else 1062 - val errorContains = if (isPostgres) { - "duplicate key value violates unique constraint" - } else { - "Duplicate entry" + val id = createTodo("title") + val (errorCode, errorContains) = () match { + case _ if isPostgres => (0, "duplicate key value violates unique constraint") + case _ if isMySQL => (1062, "Duplicate entry") + case _ if isSQLite => (19, "Abort due to constraint violation (UNIQUE constraint failed: Todo.id)") } + executeRawThatMustFail( sql.insertInto(modelTable).columns(field(idColumn), field(titleColumn)).values(id, "irrelevant"), errorCode = errorCode, diff --git a/server/servers/api/src/test/scala/com/prisma/api/mutations/embedded/EmbeddedDeadlockSpec.scala b/server/servers/api/src/test/scala/com/prisma/api/mutations/embedded/EmbeddedDeadlockSpec.scala index 2a0db99826..7c75e1643a 100644 --- a/server/servers/api/src/test/scala/com/prisma/api/mutations/embedded/EmbeddedDeadlockSpec.scala +++ b/server/servers/api/src/test/scala/com/prisma/api/mutations/embedded/EmbeddedDeadlockSpec.scala @@ -1,6 +1,5 @@ package com.prisma.api.mutations.embedded -import com.prisma.IgnoreMongo import com.prisma.api.ApiSpecBase import com.prisma.shared.models.ConnectorCapability.EmbeddedTypesCapability import com.prisma.shared.schema_dsl.SchemaDsl @@ -11,6 +10,8 @@ import org.scalatest.{FlatSpec, Matchers, Retries} import scala.concurrent.Future class EmbeddedDeadlockSpec extends FlatSpec with Matchers with Retries with ApiSpecBase with AwaitUtils { + override def doNotRunForPrototypes: Boolean = true + override def runOnlyForCapabilities = Set(EmbeddedTypesCapability) import testDependencies.system.dispatcher diff --git a/server/servers/api/src/test/scala/com/prisma/api/mutations/nonEmbedded/NonEmbeddedDeadlockSpec.scala b/server/servers/api/src/test/scala/com/prisma/api/mutations/nonEmbedded/NonEmbeddedDeadlockSpec.scala index dbe14c7167..55e9ef0741 100644 --- a/server/servers/api/src/test/scala/com/prisma/api/mutations/nonEmbedded/NonEmbeddedDeadlockSpec.scala +++ b/server/servers/api/src/test/scala/com/prisma/api/mutations/nonEmbedded/NonEmbeddedDeadlockSpec.scala @@ -10,6 +10,8 @@ import org.scalatest.{FlatSpec, Matchers, Retries} import scala.concurrent.Future class NonEmbeddedDeadlockSpec extends FlatSpec with Matchers with Retries with ApiSpecBase with AwaitUtils { + override def doNotRunForPrototypes: Boolean = true + override def runOnlyForCapabilities = Set(JoinRelationLinksCapability, ScalarListsCapability) import testDependencies.system.dispatcher diff --git a/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/DataSchemaAstExtensions.scala b/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/DataSchemaAstExtensions.scala index 3d7904aaa6..64cbc60ebd 100644 --- a/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/DataSchemaAstExtensions.scala +++ b/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/DataSchemaAstExtensions.scala @@ -66,15 +66,14 @@ object DataSchemaAstExtensions { implicit class CoolObjectType(val objectType: ObjectTypeDefinition) extends AnyVal { def hasNoIdField: Boolean = field("id").isEmpty - def previousName: String = { - val nameBeforeRename = for { + def oldName: Option[String] = { + for { directive <- objectType.directive("rename") argument <- directive.arguments.headOption } yield argument.value.asInstanceOf[StringValue].value - - nameBeforeRename.getOrElse(objectType.name) } + def previousName: String = oldName.getOrElse(objectType.name) def isEmbedded: Boolean = objectType.directives.exists(_.name == "embedded") def field_!(name: String): FieldDefinition = field(name).getOrElse(sys.error(s"Could not find the field $name on the type ${objectType.name}")) def field(name: String): Option[FieldDefinition] = objectType.fields.find(_.name == name) diff --git a/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/inference/MigrationStepsInferrer.scala b/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/inference/MigrationStepsInferrer.scala index ad2f9f5bcd..fdbf4a69d4 100644 --- a/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/inference/MigrationStepsInferrer.scala +++ b/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/inference/MigrationStepsInferrer.scala @@ -2,6 +2,7 @@ package com.prisma.deploy.migration.inference import com.prisma.deploy.schema.UpdatedRelationAmbiguous import com.prisma.shared.models.FieldBehaviour._ +import com.prisma.shared.models.Manifestations.ModelManifestation import com.prisma.shared.models._ trait MigrationStepsInferrer { @@ -50,8 +51,7 @@ case class MigrationStepsInferrerImpl(previousSchema: Schema, nextSchema: Schema enumsToDelete ++ enumsToUpdate ++ fieldsToUpdate ++ - modelsToUpdateFirstStep ++ - modelsToUpdateSecondStep ++ + modelsToUpdate ++ relationsToUpdate ++ enumsToCreate ++ modelsToCreate ++ @@ -72,13 +72,10 @@ case class MigrationStepsInferrerImpl(previousSchema: Schema, nextSchema: Schema nextModel <- nextSchema.models.toVector previousModelName = renames.getPreviousModelName(nextModel.name) previousModel <- previousSchema.getModelByName(previousModelName) - if nextModel.name != previousModel.name || nextModel.isEmbedded != previousModel.isEmbedded - } yield UpdateModel(name = previousModelName, newName = nextModel.name) + if nextModel.template != previousModel.template + } yield UpdateModel(name = previousModel.name, newName = nextModel.name) } - lazy val modelsToUpdateFirstStep: Vector[UpdateModel] = modelsToUpdate.map(update => update.copy(newName = "__" + update.newName)) - lazy val modelsToUpdateSecondStep: Vector[UpdateModel] = modelsToUpdate.map(update => update.copy(name = "__" + update.newName)) - /* * Check all previous models if they are present on on the new one, ignore renames (== updated models). * Use the _previous_ model name to check presence, and as updates are ignored this has to yield a result, diff --git a/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/validation/DataModelValidatorImpl.scala b/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/validation/DataModelValidatorImpl.scala index 9a30a6358d..5e5341dd06 100644 --- a/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/validation/DataModelValidatorImpl.scala +++ b/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/validation/DataModelValidatorImpl.scala @@ -157,13 +157,15 @@ case class DataModelValidatorImpl( val fieldDirectiveValidations = tryValidation(validateFieldDirectives()) val typeDirectiveValidations = tryValidation(validateTypeDirectives()) val enumValidations = tryValidation(EnumValidator(doc).validate()) + val validateRenames = tryValidation(validateCrossRenames(doc.objectTypes)) val allValidations = Vector( globalValidations, reservedFieldsValidations, fieldDirectiveValidations, enumValidations, - typeDirectiveValidations + typeDirectiveValidations, + validateRenames ) val validationErrors: Vector[DeployError] = allValidations.collect { case Good(x) => x }.flatten @@ -239,6 +241,20 @@ case class DataModelValidatorImpl( requiredArgErrors ++ optionalArgErrors } + def validateCrossRenames(objectTypes: Seq[ObjectTypeDefinition]): Seq[DeployError] = { + for { + renamedType1 <- objectTypes + oldName <- renamedType1.oldName + allObjectTypesExceptThisOne = objectTypes.filterNot(_ == renamedType1) + renamedTypeThatHadTheNameOfType1 <- allObjectTypesExceptThisOne.find(_.oldName.contains(renamedType1.name)) + } yield { + DeployError( + renamedType1.name, + s"You renamed type `$oldName` to `${renamedType1.name}`. But that is the old name of type `${renamedTypeThatHadTheNameOfType1.name}`. Please do this in two steps." + ) + } + } + def validateDirectiveUniqueness(fieldAndType: FieldAndType): Option[DeployError] = { val directives = fieldAndType.fieldDef.directives val uniqueDirectives = directives.map(_.name).toSet diff --git a/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/validation/DeployResults.scala b/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/validation/DeployResults.scala index ae437a7ad0..af960acbcf 100644 --- a/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/validation/DeployResults.scala +++ b/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/validation/DeployResults.scala @@ -181,6 +181,14 @@ object DeployErrors { ) } + def crossRenamedTypeName(objectTypeDefinition: ObjectTypeDefinition) = { + error( + objectTypeDefinition, + s"The type `${objectTypeDefinition.name}` is being renamed. Another type is also being renamed and formerly had `${objectTypeDefinition.name}` new name." + + s"Please split cases where you do renames like type A -> type B and type B -> type A at the same time into two parts. " + ) + } + def directiveMissesRequiredArgument(fieldAndType: FieldAndType, directive: String, argument: String) = { error( fieldAndType, diff --git a/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/validation/LegacyDataModelValidator.scala b/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/validation/LegacyDataModelValidator.scala index f918ddbb30..e4b2f2eb3d 100644 --- a/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/validation/LegacyDataModelValidator.scala +++ b/server/servers/deploy/src/main/scala/com/prisma/deploy/migration/validation/LegacyDataModelValidator.scala @@ -3,15 +3,11 @@ package com.prisma.deploy.migration.validation import com.prisma.deploy.connector.{FieldRequirement, FieldRequirementsInterface} import com.prisma.deploy.gc_value.GCStringConverter import com.prisma.deploy.validation._ -import com.prisma.shared.models.ConnectorCapability.{MigrationsCapability, ScalarListsCapability} -import com.prisma.shared.models.{ConnectorCapabilities, ConnectorCapability, RelationStrategy, TypeIdentifier} -import com.prisma.utils.or.OrExtensions +import com.prisma.shared.models.{ConnectorCapabilities, TypeIdentifier} import org.scalactic.{Bad, Good, Or} import sangria.ast.{Argument => _, _} import scala.collection.immutable.Seq -import scala.util.{Failure, Success} -import scala.concurrent.Future import scala.util.{Failure, Success, Try} case class DirectiveRequirement(directiveName: String, requiredArguments: Seq[RequiredArg], optionalArguments: Seq[Argument]) @@ -190,6 +186,7 @@ case class LegacyDataModelValidator( val scalarFieldValidations = tryValidation(validateScalarFields(allFieldAndTypes)) val fieldDirectiveValidations = tryValidation(allFieldAndTypes.flatMap(validateFieldDirectives)) val enumValidations = tryValidation(validateEnumTypes) + val crossRenameValidations = tryValidation(validateCrossRenames(doc.objectTypes)) val allValidations = Vector( reservedFieldsValidations, @@ -200,7 +197,8 @@ case class LegacyDataModelValidator( relationFieldValidations, scalarFieldValidations, fieldDirectiveValidations, - enumValidations + enumValidations, + crossRenameValidations ) val validationErrors: Vector[DeployError] = allValidations.collect { case Good(x) => x }.flatten @@ -250,6 +248,20 @@ case class LegacyDataModelValidator( } } + def validateCrossRenames(objectTypes: Seq[ObjectTypeDefinition]): Seq[DeployError] = { + for { + renamedType1 <- objectTypes + oldName <- renamedType1.oldName + allObjectTypesExceptThisOne = objectTypes.filterNot(_ == renamedType1) + renamedTypeThatHadTheNameOfType1 <- allObjectTypesExceptThisOne.find(_.oldName.contains(renamedType1.name)) + } yield { + DeployError( + renamedType1.name, + s"You renamed type `$oldName` to `${renamedType1.name}`. But that is the old name of type `${renamedTypeThatHadTheNameOfType1.name}`. Please do this in two steps." + ) + } + } + def validateDuplicateFields(fieldAndTypes: Seq[FieldAndType]): Seq[DeployError] = { for { objectType <- fieldAndTypes.map(_.objectType).distinct @@ -300,7 +312,7 @@ case class LegacyDataModelValidator( /** * Check that if a relation is not a self-relation and a relation-directive occurs only once that there is no - * opposing field without a relationdirective on the other side. + * opposing field without a relation directive on the other side. */ val allowOnlyOneDirectiveOnlyWhenUnambigous = relationFieldsWithRelationDirective.flatMap { case thisType if !isSelfRelation(thisType) && relationCount(thisType) == 1 => diff --git a/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/DatabaseSchemaValidatorSpec.scala b/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/DatabaseSchemaValidatorSpec.scala index 9ad2869049..73609f535d 100644 --- a/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/DatabaseSchemaValidatorSpec.scala +++ b/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/DatabaseSchemaValidatorSpec.scala @@ -7,8 +7,8 @@ import com.prisma.shared.models.ConnectorCapability._ import org.scalatest.{Matchers, WordSpecLike} class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with PassiveDeploySpecBase { - override def doNotRunForCapabilities: Set[ConnectorCapability] = Set.empty override def runOnlyForCapabilities = Set(MigrationsCapability) + override def doNotRunForCapabilities: Set[ConnectorCapability] = Set.empty "it should error if a table is missing" in { val postgres = @@ -17,6 +17,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | id SERIAL PRIMARY KEY |); """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -24,7 +25,14 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.blog ( + | id INTEGER PRIMARY KEY + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -49,6 +57,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | id SERIAL PRIMARY KEY |); """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -56,7 +65,14 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.blog ( + | id INTEGER PRIMARY KEY NOT NULL + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -82,6 +98,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | title text |); """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -90,7 +107,15 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL, + | title mediumtext + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -117,6 +142,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | title text NOT NULL |); """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -125,7 +151,15 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL, + | title mediumtext NOT NULL + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -152,6 +186,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | title text |); """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -160,7 +195,15 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL, + | title mediumtext + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -186,6 +229,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | id varchar(25) PRIMARY KEY |); """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -193,7 +237,14 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.blog ( + | id char(25) PRIMARY KEY NOT NULL + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -216,6 +267,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | author int references author(id) |); """.stripMargin + val mysql = s""" | CREATE TABLE author ( @@ -228,7 +280,20 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.author ( + | id int PRIMARY KEY NOT NULL + | ); + | + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL, + | author int, + | FOREIGN KEY (author) REFERENCES author(id) + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -256,6 +321,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | author int references author(id) |); """.stripMargin + val mysql = s""" | CREATE TABLE author ( @@ -268,7 +334,19 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.author ( + | id int PRIMARY KEY NOT NULL + | ); + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL, + | author int, + | FOREIGN KEY (author) REFERENCES author(id) + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -302,6 +380,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | author int NOT NULL references author(id) |); """.stripMargin + val mysql = s""" | CREATE TABLE author ( @@ -314,7 +393,20 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.author ( + | id int PRIMARY KEY NOT NULL + | ); + | + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL, + | author int NOT NULL, + | FOREIGN KEY (author) REFERENCES author(id) + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -347,6 +439,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | id SERIAL PRIMARY KEY |); """.stripMargin + val mysql = s""" | CREATE TABLE author( @@ -357,7 +450,17 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.author( + | id int NOT NULL, PRIMARY KEY(id) + | ); + | CREATE TABLE $projectId.blog ( + | id int NOT NULL, PRIMARY KEY(id) + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -393,6 +496,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | author int references wrong_table(id) |); """.stripMargin + val mysql = s""" | CREATE TABLE author ( @@ -407,7 +511,21 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.author ( + | id int PRIMARY KEY NOT NULL + | ); + | CREATE TABLE $projectId.wrong_table ( + | id int PRIMARY KEY NOT NULL + | ); + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL, + | author int, FOREIGN KEY (author) REFERENCES wrong_table(id) + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -442,6 +560,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | author int references author(nick) |); """.stripMargin + val mysql = s""" | CREATE TABLE author( @@ -454,7 +573,19 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.author( + | id int PRIMARY KEY NOT NULL, + | nick int UNIQUE + | ); + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL, + | author int, FOREIGN KEY (author) REFERENCES author(nick) + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -487,6 +618,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | id SERIAL PRIMARY KEY |); """.stripMargin + val mysql = s""" | CREATE TABLE author( @@ -497,7 +629,17 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.author( + | id int NOT NULL, PRIMARY KEY(id) + | ); + | CREATE TABLE $projectId.blog ( + | id int NOT NULL, PRIMARY KEY(id) + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -539,6 +681,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | author int references author(id) | ) """.stripMargin + val mysql = s""" | CREATE TABLE author ( @@ -553,7 +696,25 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.author ( + | id int PRIMARY KEY NOT NULL + | ); + | + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL + | ); + | + | CREATE TABLE $projectId.blog_to_author( + | author int, + | blog int, + | FOREIGN KEY (blog) REFERENCES blog(id), + | FOREIGN KEY (author) REFERENCES author(id) + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -595,6 +756,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | author int references author(id) | ); """.stripMargin + val mysql = s""" | CREATE TABLE author( @@ -609,7 +771,25 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.author( + | id int PRIMARY KEY NOT NULL + | ); + | + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL + | ); + | + | CREATE TABLE $projectId.blog_to_author( + | author int, + | blog int, + | FOREIGN KEY (author) REFERENCES author(id), + | FOREIGN KEY (blog) REFERENCES blog(id) + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -645,6 +825,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | author int | ) """.stripMargin + val mysql = s""" | CREATE TABLE author( @@ -659,7 +840,21 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.author( + | id int PRIMARY KEY NOT NULL + | ); + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL + | ); + | CREATE TABLE $projectId.blog_to_author( + | author int, + | blog int + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -706,6 +901,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | author int references wrong_table(id) | ) """.stripMargin + val mysql = s""" | CREATE TABLE author ( @@ -723,7 +919,29 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ) """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.author ( + | id int PRIMARY KEY NOT NULL + | ); + | + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL + | ); + | + | CREATE TABLE $projectId.wrong_table( + | id int PRIMARY KEY NOT NULL + | ); + | + | CREATE TABLE $projectId.blog_to_author( + | blog int, + | author int, + | FOREIGN KEY (blog) REFERENCES wrong_table(id), + | FOREIGN KEY (author) REFERENCES wrong_table(id) + | ) + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -768,6 +986,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | PRIMARY KEY ("nodeId","position") | ) """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -781,7 +1000,20 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL + | ); + | CREATE TABLE $projectId.blog_tags ( + | `nodeId` char(25) NOT NULL, + | `position` int NOT NULL, + | `value` mediumtext NOT NULL, + | PRIMARY KEY (`nodeId`,`position`) + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -801,6 +1033,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | id SERIAL PRIMARY KEY | ); """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -808,7 +1041,14 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -839,6 +1079,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | PRIMARY KEY ("nodeId","position") | ); """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -852,7 +1093,20 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv | ); """.stripMargin - setup(SQLs(postgres = postgres, mysql = mysql)) + val sqlite = + s""" + | CREATE TABLE $projectId.blog ( + | id int PRIMARY KEY NOT NULL + | ); + | CREATE TABLE $projectId.blog_tags ( + | `nodeId` bool NOT NULL, + | `position` bool NOT NULL, + | `value` bool NOT NULL, + | PRIMARY KEY (`nodeId`,`position`) + | ); + """.stripMargin + + setup(SQLs(postgres = postgres, mysql = mysql, sqlite = sqlite)) val dataModel = s""" @@ -865,7 +1119,7 @@ class DatabaseSchemaValidatorSpec extends WordSpecLike with Matchers with Passiv val errors = deployThatMustError(dataModel, ConnectorCapabilities(IntIdCapability, NonEmbeddedScalarListCapability)) errors should have(size(3)) errors.forall(_.`type` == "Blog") should be(true) - errors.forall(_.field == Some("tags")) should be(true) + errors.forall(_.field.contains("tags")) should be(true) val (error1, error2, error3) = (errors(0), errors(1), errors(2)) error1.description should be( "The column `nodeId` in the underlying table for the scalar list field `tags` has the wrong type. It has the type `Boolean` but it should have the type `String`.") diff --git a/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/ExistingDatabasesSpec.scala b/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/ExistingDatabasesSpec.scala index 139071211f..321ab375ba 100644 --- a/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/ExistingDatabasesSpec.scala +++ b/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/ExistingDatabasesSpec.scala @@ -9,6 +9,7 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo override def doNotRunForCapabilities: Set[ConnectorCapability] = Set.empty override def runOnlyForCapabilities = Set(MigrationsCapability) + override def doNotRunForPrototypes: Boolean = true "adding a type for an existing table should work" in { val postgres = @@ -17,6 +18,7 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo | id SERIAL PRIMARY KEY -- implicit primary key constraint |); """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -24,7 +26,8 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo | PRIMARY KEY(id) | ); """.stripMargin - val initialResult = setup(SQLs(postgres = postgres, mysql = mysql)) + + val initialResult = setup(SQLs(postgres = postgres, mysql = mysql, sqlite = "")) val dataModel = s""" @@ -52,7 +55,8 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo | PRIMARY KEY(id) | ); """.stripMargin - val initialResult = setup(SQLs(postgres = postgres, mysql = mysql)) + + val initialResult = setup(SQLs(postgres = postgres, mysql = mysql, sqlite = "")) val dataModel = s""" @@ -85,7 +89,7 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo // val dropPostTable = "DROP TABLE post;" - val result = executeSql(SQLs(postgres = dropPostTable, mysql = dropPostTable)) + val result = executeSql(SQLs(postgres = dropPostTable, mysql = dropPostTable, sqlite = dropPostTable)) result.table("post").isDefined should be(false) val dataModel = @@ -108,6 +112,7 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo | title text |); """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -116,13 +121,14 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo | PRIMARY KEY(id) | ); """.stripMargin - val initialResult = setup(SQLs(postgres = postgres, mysql = mysql)) + + val initialResult = setup(SQLs(postgres = postgres, mysql = mysql, sqlite = "")) val dataModel = s""" |type Blog @db(name: "blog"){ | id: Int! @id - | title: String! + | title: String |} """.stripMargin @@ -138,6 +144,7 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo | title int |); """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -146,7 +153,8 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo | PRIMARY KEY(id) | ); """.stripMargin - val initialResult = setup(SQLs(postgres = postgres, mysql = mysql)) + + val initialResult = setup(SQLs(postgres = postgres, mysql = mysql, sqlite = "")) val tableBefore = initialResult.table_!("blog") val columnBefore = tableBefore.column_!("title") columnBefore.typeIdentifier should be(TI.Int) @@ -177,6 +185,7 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo | title int |); """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -185,7 +194,8 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo | PRIMARY KEY(id) | ); """.stripMargin - val initialResult = setup(SQLs(postgres = postgres, mysql = mysql)) + + val initialResult = setup(SQLs(postgres = postgres, mysql = mysql, sqlite = "")) val tableBefore = initialResult.table_!("blog") tableBefore.indexByColumns("title") should be(empty) @@ -202,6 +212,41 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo tableAfter.indexByColumns_!("title").unique should be(true) } + "creating a field for an existing column and simultaneously making it required should work" in { + val postgres = + s""" + | CREATE TABLE blog ( + | id SERIAL PRIMARY KEY, + | title int + |); + """.stripMargin + + val mysql = + s""" + | CREATE TABLE blog ( + | id int NOT NULL, + | title int, + | PRIMARY KEY(id) + | ); + """.stripMargin + + val initialResult = setup(SQLs(postgres = postgres, mysql = mysql, sqlite = "")) + val tableBefore = initialResult.table_!("blog") + tableBefore.column_!("title").isRequired should be(false) + + val dataModel = + s""" + |type Blog @db(name: "blog"){ + | id: Int! @id + | title: Int! + |} + """.stripMargin + + val result = deploy(dataModel, ConnectorCapabilities(IntIdCapability)) + val tableAfter = result.table_!("blog") + tableAfter.column_!("title").isRequired should be(true) + } + "creating a field for an existing column and simultaneously removing the unique constraint should work" in { val postgres = s""" @@ -211,6 +256,7 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo |); |CREATE UNIQUE INDEX title_index ON blog(title ASC); """.stripMargin + val mysql = s""" | CREATE TABLE blog ( @@ -220,7 +266,8 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo | UNIQUE INDEX (title ASC) | ); """.stripMargin - val initialResult = setup(SQLs(postgres = postgres, mysql = mysql)) + + val initialResult = setup(SQLs(postgres = postgres, mysql = mysql, sqlite = "")) val tableBefore = initialResult.table_!("blog") tableBefore.indexByColumns_!("title").unique should be(true) @@ -251,8 +298,8 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo val initialResult = deploy(initialDataModel, ConnectorCapabilities(IntIdCapability)) initialResult.table_!("blog").column("title").isDefined should be(true) - val dropPostTable = "ALTER TABLE blog DROP COLUMN title;" - val result = executeSql(SQLs(postgres = dropPostTable, mysql = dropPostTable)) + val dropTitleColumn = "ALTER TABLE blog DROP COLUMN title;" + val result = executeSql(SQLs(postgres = dropTitleColumn, mysql = dropTitleColumn, sqlite = dropTitleColumn)) result.table_!("blog").column("title").isDefined should be(false) val dataModel = @@ -280,8 +327,8 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo deploy(initialDataModel, ConnectorCapabilities(IntIdCapability)) - val dropPostTable = "ALTER TABLE blog DROP COLUMN title;" - executeSql(SQLs(postgres = dropPostTable, mysql = dropPostTable)) + val dropTitleColumn = "ALTER TABLE blog DROP COLUMN title;" + executeSql(SQLs(postgres = dropTitleColumn, mysql = dropTitleColumn, sqlite = dropTitleColumn)) val dataModel = s""" @@ -314,8 +361,8 @@ class ExistingDatabasesSpec extends WordSpecLike with Matchers with PassiveDeplo deploy(initialDataModel, ConnectorCapabilities(IntIdCapability)) - val dropPostTable = "ALTER TABLE blog RENAME COLUMN title TO new_title;" - val result = executeSql(SQLs(postgres = dropPostTable, mysql = dropPostTable)) + val renameTitleColumn = "ALTER TABLE blog RENAME COLUMN title TO new_title;" + val result = executeSql(SQLs(postgres = renameTitleColumn, mysql = renameTitleColumn, sqlite = renameTitleColumn)) result.table_!("blog").column("title") should be(empty) val dataModel = diff --git a/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/MigrationsSpec.scala b/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/MigrationsSpec.scala index b38687083d..aa4af69eb8 100644 --- a/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/MigrationsSpec.scala +++ b/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/MigrationsSpec.scala @@ -14,6 +14,8 @@ class MigrationsSpec extends WordSpecLike with Matchers with DeploySpecBase { override def runOnlyForCapabilities = Set(MigrationsCapability) + override def doNotRunForPrototypes: Boolean = true + val name = this.getClass.getSimpleName val stage = "default" val serviceId = testDependencies.projectIdEncoder.toEncodedString(name, stage) @@ -967,7 +969,9 @@ class MigrationsSpec extends WordSpecLike with Matchers with DeploySpecBase { functions = Vector.empty, noMigration = None ) + val refreshedProject = testDependencies.projectPersistence.load(project.id).await.get + val mutation = DeployMutation( args = input, project = refreshedProject, diff --git a/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/inference/LegacyInfererIntegrationSpec.scala b/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/inference/LegacyInfererIntegrationSpec.scala index 269612ca70..12fd968545 100644 --- a/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/inference/LegacyInfererIntegrationSpec.scala +++ b/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/inference/LegacyInfererIntegrationSpec.scala @@ -26,7 +26,7 @@ class LegacyInfererIntegrationSpec extends FlatSpec with Matchers with DeploySpe val project = inferSchema(schema) val steps = inferSteps(previousSchema = project, next = schema) - steps should be(empty) + steps should be(Vector()) } "they" should "should error if Prisma would shorten two autogenerated relation names to the same name " in { @@ -124,14 +124,11 @@ class LegacyInfererIntegrationSpec extends FlatSpec with Matchers with DeploySpe """.stripMargin val steps = inferSteps(previousSchema = project, next = nextSchema) - steps should have(size(1)) - steps should be( - Vector( - UpdateRelation( - name = "ManualRelationName", - newName = Some("CommentToTodo") - ) - )) + steps should have(size(3)) + steps should contain allOf ( + UpdateRelation(name = "ManualRelationName", newName = Some("CommentToTodo")), + UpdateModel("Todo", "Todo"), UpdateModel("Comment", "Comment") + ) } @@ -160,14 +157,13 @@ class LegacyInfererIntegrationSpec extends FlatSpec with Matchers with DeploySpe """.stripMargin val steps = inferSteps(previousSchema = project, next = nextSchema) - steps should have(size(1)) - steps should be( - Vector( - UpdateRelation( - name = "CommentToTodo", - newName = Some("ManualRelationName") - ) - )) + steps.length should be(3) + + steps should contain allOf ( + UpdateRelation(name = "CommentToTodo", newName = Some("ManualRelationName")), + UpdateModel("Todo", "Todo"), + UpdateModel("Comment", "Comment") + ) } "they" should "not propose an Update and Delete at the same time when renaming a relation" in { @@ -195,8 +191,10 @@ class LegacyInfererIntegrationSpec extends FlatSpec with Matchers with DeploySpe """.stripMargin val steps = inferSteps(previousSchema = project, next = nextSchema) - steps should have(size(2)) + steps should have(size(4)) steps should contain allOf ( + UpdateModel("Todo", "Todo"), + UpdateModel("Comment", "Comment"), DeleteRelation("ManualRelationName1"), CreateRelation("ManualRelationName2") ) @@ -228,8 +226,10 @@ class LegacyInfererIntegrationSpec extends FlatSpec with Matchers with DeploySpe |} """.stripMargin val steps = inferSteps(previousSchema = project, next = nextSchema) - steps should have(size(6)) + steps.length should be(8) steps should contain allOf ( + UpdateModel("Todo", "Todo"), + UpdateModel("Comment", "Comment"), CreateField( model = "Todo", name = "comment1" @@ -282,7 +282,7 @@ class LegacyInfererIntegrationSpec extends FlatSpec with Matchers with DeploySpe |} """.stripMargin val steps = inferSteps(previousSchema = project, next = nextSchema) - steps should have(size(0)) + steps.length should be(0) } def inferSchema(schema: String): Schema = { diff --git a/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/inference/MigrationStepsInferrerSpec.scala b/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/inference/MigrationStepsInferrerSpec.scala index 61729471a6..0b79f078fb 100644 --- a/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/inference/MigrationStepsInferrerSpec.scala +++ b/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/inference/MigrationStepsInferrerSpec.scala @@ -11,6 +11,8 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB /** * Basic tests */ + // due to the way we set up our schemas, the schemainferrer will always create an UpdateModel at the moment. + "No changes" should "create no migration steps" in { val renames = SchemaMapping.empty @@ -24,7 +26,7 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val inferrer = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, renames) val steps = inferrer.evaluate() - steps shouldBe empty + steps shouldBe Vector(UpdateModel("Test", "Test")) } "Creating models" should "create CreateModel and CreateField migration steps" in { @@ -41,8 +43,9 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val proposer = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, renames) val steps = proposer.evaluate() - steps.length shouldBe 4 + steps.length shouldBe 5 steps should contain allOf ( + UpdateModel("Test", "Test"), CreateModel("Test2"), CreateField("Test2", "id"), CreateField("Test2", "c"), @@ -65,8 +68,8 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val proposer = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, renames) val steps = proposer.evaluate() - steps.length shouldBe 1 - steps.last shouldBe DeleteModel("Test2") + steps.length shouldBe 2 + steps should contain allOf (UpdateModel("Test", "Test"), DeleteModel("Test2")) } "Updating models" should "create UpdateModel migration steps" in { @@ -84,8 +87,8 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val proposer = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, renames) val steps = proposer.evaluate() - steps.length shouldBe 2 - steps.last shouldBe UpdateModel("__Test2", "Test2") + steps.length shouldBe 1 + steps.last shouldBe UpdateModel("Test", "Test2") } "Creating fields" should "create CreateField migration steps" in { @@ -101,8 +104,8 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val proposer = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, renames) val steps = proposer.evaluate() - steps.length shouldBe 1 - steps.last shouldBe CreateField("Test", "b") + steps.length shouldBe 2 + steps should contain allOf (UpdateModel("Test", "Test"), CreateField("Test", "b")) } "Deleting fields" should "create DeleteField migration steps" in { @@ -118,8 +121,8 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val proposer = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, renames) val steps = proposer.evaluate() - steps.length shouldBe 1 - steps.last shouldBe DeleteField("Test", "b") + steps.length shouldBe 2 + steps should contain allOf (UpdateModel("Test", "Test"), DeleteField("Test", "b")) } "Updating fields" should "create UpdateField migration steps" in { @@ -156,8 +159,9 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val proposer = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, renames) val steps = proposer.evaluate() - steps.length shouldBe 5 + steps.length shouldBe 6 steps should contain allOf ( + UpdateModel("Test", "Test"), UpdateField("Test", "Test", "a", Some("a2")), UpdateField("Test", "Test", "b", None), UpdateField("Test", "Test", "c", None), @@ -185,9 +189,11 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val proposer = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, SchemaMapping.empty) val steps = proposer.evaluate() - steps.length shouldBe 3 + steps.length shouldBe 5 val relationName = nextProject.relations.head.name steps should contain allOf ( + UpdateModel("Todo", "Todo"), + UpdateModel("Comment", "Comment"), CreateField( model = "Todo", name = "comments" @@ -221,8 +227,10 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val proposer = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, SchemaMapping.empty) val steps = proposer.evaluate() - steps should have(size(3)) + steps.length should be(5) steps should contain allOf ( + UpdateModel("Todo", "Todo"), + UpdateModel("Comment", "Comment"), DeleteField("Todo", "comments"), DeleteField("Comment", "todo"), DeleteRelation(previousProject.relations.head.name) @@ -254,12 +262,10 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val inferrer = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, mappings) val steps = inferrer.evaluate() - steps should have(size(7)) + steps should have(size(5)) steps should contain(UpdateRelation("CommentToTodo", newName = Some("CommentNewToTodoNew"))) - steps should contain(UpdateModel("Comment", newName = "__CommentNew")) - steps should contain(UpdateModel("Todo", newName = "__TodoNew")) - steps should contain(UpdateModel("__CommentNew", newName = "CommentNew")) - steps should contain(UpdateModel("__TodoNew", newName = "TodoNew")) + steps should contain(UpdateModel("Comment", newName = "CommentNew")) + steps should contain(UpdateModel("Todo", newName = "TodoNew")) steps should contain(UpdateField("Comment", "CommentNew", "todo", Some("todoNew"))) steps should contain(UpdateField("Todo", "TodoNew", "comments", Some("commentsNew"))) } @@ -300,8 +306,9 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val proposer = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, SchemaMapping.empty) val steps = proposer.evaluate() - steps should have(size(2)) + steps should have(size(3)) steps should contain allOf ( + UpdateModel("Todo", "Todo"), CreateEnum("TodoStatus"), CreateField( model = "Todo", @@ -329,14 +336,12 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val steps = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, renames).evaluate() - steps should have(size(1)) - steps should be( - Vector( - UpdateEnum( - name = "TodoStatus", - newName = Some("TodoStatusNew") - ) - )) + steps.length should be(2) + steps should contain allOf (UpdateModel("Todo", "Todo"), + UpdateEnum( + name = "TodoStatus", + newName = Some("TodoStatusNew") + )) } "Updating the values of an Enum" should "create one UpdateEnum step" in { @@ -358,9 +363,9 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val steps = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, renames).evaluate() - println(steps) - steps should have(size(1)) - steps should contain( + steps.length should be(2) + steps should contain allOf ( + UpdateModel("Todo", "Todo"), UpdateEnum( name = "TodoStatus", newName = None @@ -390,8 +395,9 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB } val steps = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, renames).evaluate() - steps should have(size(1)) - steps should contain( + steps.length should be(2) + steps should contain allOf ( + UpdateModel("Todo", "Todo"), CreateField( model = "Todo", name = "someField" @@ -413,8 +419,9 @@ class MigrationStepsInferrerSpec extends FlatSpec with Matchers with DeploySpecB val steps = MigrationStepsInferrerImpl(previousProject.schema, nextProject.schema, renames).evaluate() - steps should have(size(1)) - steps should contain( + steps.length should be(2) + steps should contain allOf ( + UpdateModel("Todo", "Todo"), DeleteEnum( name = "TodoStatus" ) diff --git a/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/migrator/MigrationApplierSpec.scala b/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/migrator/MigrationApplierSpec.scala index d88710950e..f477a29aa3 100644 --- a/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/migrator/MigrationApplierSpec.scala +++ b/server/servers/deploy/src/test/scala/com/prisma/deploy/migration/migrator/MigrationApplierSpec.scala @@ -1,5 +1,6 @@ package com.prisma.deploy.migration.migrator +import com.prisma.IgnoreSQLite import com.prisma.deploy.connector._ import com.prisma.deploy.specutils.{ActiveDeploySpecBase, TestProject} import com.prisma.shared.models._ @@ -44,7 +45,7 @@ class MigrationApplierSpec extends FlatSpec with Matchers with ActiveDeploySpecB testDependencies.projectPersistence.create(Project(projectId, 1, emptySchema)).await } - "the applier" should "succeed when all steps succeed" in { + "the applier" should "succeed when all steps succeed" taggedAs (IgnoreSQLite) in { persistence.create(migration).await val executor = mutactionExecutor( execute = { @@ -66,7 +67,7 @@ class MigrationApplierSpec extends FlatSpec with Matchers with ActiveDeploySpecB persisted.finishedAt.isDefined shouldEqual true } - "the applier" should "mark a migration as ROLLBACK_SUCCESS if all steps can be rolled back successfully" in { + "the applier" should "mark a migration as ROLLBACK_SUCCESS if all steps can be rolled back successfully" taggedAs (IgnoreSQLite) in { persistence.create(migration).await val executor = mutactionExecutor( @@ -91,7 +92,7 @@ class MigrationApplierSpec extends FlatSpec with Matchers with ActiveDeploySpecB persisted.rolledBack should be(1) // 1 step was rolled back } - "the applier" should "mark a migration as ROLLBACK_FAILURE if the rollback fails" in { + "the applier" should "mark a migration as ROLLBACK_FAILURE if the rollback fails" taggedAs (IgnoreSQLite) in { persistence.create(migration).await val executor = mutactionExecutor( diff --git a/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/AddProjectMutationSpec.scala b/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/AddProjectMutationSpec.scala index 098cff2271..7a23f10a07 100644 --- a/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/AddProjectMutationSpec.scala +++ b/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/AddProjectMutationSpec.scala @@ -9,7 +9,7 @@ class AddProjectMutationSpec extends FlatSpec with Matchers with DeploySpecBase val projectPersistence = testDependencies.projectPersistence "AddProjectMutation" should "succeed for valid input" in { - val name = s"${Cuid.createCuid()}~test" + val name = s"${Cuid.createCuid()}Xtest" val stage = Cuid.createCuid() val result = server.query(s""" @@ -33,7 +33,7 @@ class AddProjectMutationSpec extends FlatSpec with Matchers with DeploySpecBase } "AddProjectMutation" should "fail if a project already exists" in { - val name = s"${Cuid.createCuid()}~test" + val name = s"${Cuid.createCuid()}Xtest" val stage = Cuid.createCuid() server.query(s""" diff --git a/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/DeployMutationRegressionSpec.scala b/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/DeployMutationRegressionSpec.scala index d522e50590..215f84d9b5 100644 --- a/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/DeployMutationRegressionSpec.scala +++ b/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/DeployMutationRegressionSpec.scala @@ -1,12 +1,14 @@ package com.prisma.deploy.schema.mutations +import com.prisma.IgnoreSQLite import com.prisma.deploy.specutils.ActiveDeploySpecBase -import com.prisma.shared.models.{MigrationId, MigrationStatus, ProjectId} +import com.prisma.shared.models.ConnectorCapability.LegacyDataModelCapability +import com.prisma.shared.models.{ConnectorCapability, MigrationId, MigrationStatus} import org.scalatest.{FlatSpec, Matchers} class DeployMutationRegressionSpec extends FlatSpec with Matchers with ActiveDeploySpecBase { - override def doNotRunForPrototypes: Boolean = true + override def runOnlyForCapabilities: Set[ConnectorCapability] = Set(LegacyDataModelCapability) val projectPersistence = testDependencies.projectPersistence val migrationPersistence = testDependencies.migrationPersistence @@ -111,7 +113,7 @@ class DeployMutationRegressionSpec extends FlatSpec with Matchers with ActiveDep migration.status shouldEqual MigrationStatus.Success } - "DeployMutation" should "succeed for regression #1436" in { + "DeployMutation" should "succeed for regression #1436" taggedAs (IgnoreSQLite) in { val (project, initialMigration) = setupProject(""" |type Post { | id: ID! @unique diff --git a/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/DeployMutationSpec.scala b/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/DeployMutationSpec.scala index 6dd90c9317..33f76e5397 100644 --- a/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/DeployMutationSpec.scala +++ b/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/DeployMutationSpec.scala @@ -1,6 +1,6 @@ package com.prisma.deploy.schema.mutations -import com.prisma.IgnoreMongo +import com.prisma.{IgnoreMongo, IgnoreSQLite} import com.prisma.deploy.specutils.ActiveDeploySpecBase import com.prisma.shared.models._ import org.scalatest.{FlatSpec, Matchers} @@ -495,7 +495,7 @@ class DeployMutationSpec extends FlatSpec with Matchers with ActiveDeploySpecBas migrations should have(size(3)) migrations.exists(x => x.status != MigrationStatus.Success) shouldEqual false migrations.head.revision shouldEqual 3 // order is DESC - migrations.head.steps.head should be(UpdateSecrets(Vector("secret"))) + migrations.head.steps should be(Vector(UpdateSecrets(Vector("secret")))) server.query( s""" @@ -518,7 +518,7 @@ class DeployMutationSpec extends FlatSpec with Matchers with ActiveDeploySpecBas migrations2 should have(size(4)) migrations2.exists(x => x.status != MigrationStatus.Success) shouldEqual false migrations2.head.revision shouldEqual 4 // order is DESC - migrations2.head.steps.head should be(UpdateSecrets(Vector.empty)) + migrations2.head.steps should be(Vector(UpdateSecrets(Vector()))) } "DeployMutation" should "not change secrets if there are errors in the deploy (invalid functions)" in { @@ -667,7 +667,7 @@ class DeployMutationSpec extends FlatSpec with Matchers with ActiveDeploySpecBas ) } - "DeployMutation" should "be able to change a field from scalar non-list to scalar list" in { + "DeployMutation" should "be able to change a field from scalar non-list to scalar list" taggedAs (IgnoreSQLite) in { val (project, _) = setupProject(basicTypesGql) val schema = """ diff --git a/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/SeveralRelationsBetweenSameModelsSpec.scala b/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/SeveralRelationsBetweenSameModelsSpec.scala index b56a8f9767..d97ae52547 100644 --- a/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/SeveralRelationsBetweenSameModelsSpec.scala +++ b/server/servers/deploy/src/test/scala/com/prisma/deploy/schema/mutations/SeveralRelationsBetweenSameModelsSpec.scala @@ -1,11 +1,12 @@ package com.prisma.deploy.schema.mutations +import com.prisma.IgnoreSQLite import com.prisma.deploy.specutils.ActiveDeploySpecBase import org.scalatest.{FlatSpec, Matchers} class SeveralRelationsBetweenSameModelsSpec extends FlatSpec with Matchers with ActiveDeploySpecBase { - "DeployMutation" should "be able to name a relation that previously had no name" in { + "DeployMutation" should "be able to name a relation that previously had no name" taggedAs (IgnoreSQLite) in { val schema = """type A { @@ -115,7 +116,7 @@ class SeveralRelationsBetweenSameModelsSpec extends FlatSpec with Matchers with updatedProject2.schema.relations(2).name should be("""AB2""") } - "DeployMutation" should "be able to handle renaming relations" in { + "DeployMutation" should "be able to handle renaming relations" taggedAs (IgnoreSQLite) in { val schema = """type A { diff --git a/server/servers/deploy/src/test/scala/com/prisma/deploy/specutils/DeploySpecBase.scala b/server/servers/deploy/src/test/scala/com/prisma/deploy/specutils/DeploySpecBase.scala index c00e384ba3..f0e9cab4e8 100644 --- a/server/servers/deploy/src/test/scala/com/prisma/deploy/specutils/DeploySpecBase.scala +++ b/server/servers/deploy/src/test/scala/com/prisma/deploy/specutils/DeploySpecBase.scala @@ -7,6 +7,7 @@ import com.prisma.deploy.connector.jdbc.database.JdbcDeployMutactionExecutor import com.prisma.deploy.connector.mysql.MySqlDeployConnector import com.prisma.deploy.connector.{DatabaseSchema, EmptyDatabaseIntrospectionInferrer, FieldRequirementsInterface} import com.prisma.deploy.connector.postgres.PostgresDeployConnector +import com.prisma.deploy.connector.sqlite.SQLiteDeployConnector import com.prisma.deploy.migration.SchemaMapper import com.prisma.deploy.migration.inference.{MigrationStepsInferrer, SchemaInferrer} import com.prisma.deploy.migration.validation.DeployError @@ -85,7 +86,7 @@ trait PassiveDeploySpecBase extends DeploySpecBase with DataModelV2Base { self: override def doNotRunForCapabilities = Set(MigrationsCapability) lazy val slickDatabase = deployConnector.deployMutactionExecutor.asInstanceOf[JdbcDeployMutactionExecutor].slickDatabase - case class SQLs(postgres: String, mysql: String) + case class SQLs(postgres: String, mysql: String, sqlite: String) def addProject() = { deployConnector.deleteProjectDatabase(projectId).await() @@ -93,72 +94,67 @@ trait PassiveDeploySpecBase extends DeploySpecBase with DataModelV2Base { self: } def setup(sqls: SQLs): DatabaseSchema = { - deployConnector.deleteProjectDatabase(projectId).await() - if (slickDatabase.isMySql) { - setupWithRawSQL(sqls.mysql) - } else if (slickDatabase.isPostgres) { - setupWithRawSQL(sqls.postgres) - } else { - sys.error("This is neither Postgres nor MySQL") + slickDatabase match { + case db if db.isMySql => setupWithRawSQL(sqls.mysql) + case db if db.isPostgres => setupWithRawSQL(sqls.postgres) + case db if db.isSQLite => setupWithRawSQL(sqls.sqlite) + case _ => sys.error("This is neither Postgres nor MySQL nor SQLite") } inspect } - def setupWithRawSQL(sql: String)(implicit suite: Suite): Unit = { - setupProjectDatabaseForProject(projectId, projectName, projectStage, sql) - } + def setupWithRawSQL(sql: String)(implicit suite: Suite): Unit = setupProjectDatabaseForProject(projectId, projectName, projectStage, sql) private def setupProjectDatabaseForProject(schemaName: String, name: String, stage: String, sql: String): Unit = { - val (session, isPostgres) = deployConnector match { - case c: PostgresDeployConnector => (c.managementDatabase.createSession(), true) - case c: MySqlDeployConnector => (c.managementDatabase.database.createSession(), false) - case x => sys.error(s"$x is not supported here") - } - val statement = session.createStatement() - - val dropSchema = if (isPostgres) { - s"""drop schema if exists "$schemaName" cascade;""" - } else { - s"""drop database if exists `$schemaName`;""" + val (session, defaultSchema) = deployConnector match { + case c: PostgresDeployConnector => + val session = c.managementDatabase.createSession() + val default = s"""SET search_path TO "$schemaName";""" + (session, default) + + case c: MySqlDeployConnector => + val session = c.managementDatabase.database.createSession() + val default = s"USE `$schemaName`;" + (session, default) + + case c: SQLiteDeployConnector => + val session = c.managementDatabase.database.createSession() + val path = s"""'db/$projectId'""" + (session, s"ATTACH DATABASE $path AS $projectId;") + + case x => sys.error(s"$x is not supported here") } - statement.execute(dropSchema) + val statement = session.createStatement() addProject() - - val createSchema = if (isPostgres) s"""create schema if not exists "$schemaName";""" else s"create database if not exists `$schemaName`;" - statement.execute(createSchema) - - val setDefaultSchema = if (isPostgres) s"""SET search_path TO "$schemaName";""" else s"USE `$schemaName`;" - statement.execute(setDefaultSchema) + statement.execute(defaultSchema) sql.split(';').foreach { sql => val isNotOnlyWhiteSpace = sql.exists(c => c != '\n' && c != ' ') - if (isNotOnlyWhiteSpace) { - statement.execute(sql) - } + if (isNotOnlyWhiteSpace) statement.execute(sql) } session.close() } def executeSql(sqls: SQLs): DatabaseSchema = { - if (slickDatabase.isMySql) { - executeSql(sqls.mysql) - } else if (slickDatabase.isPostgres) { - executeSql(sqls.postgres) - } else { - sys.error("This is neither Postgres nor MySQL") + slickDatabase match { + case db if db.isMySql => executeSql(sqls.mysql) + case db if db.isPostgres => executeSql(sqls.postgres) + case db if db.isSQLite => executeSql(sqls.sqlite) + case _ => sys.error("This is neither Postgres nor MySQL nor SQLite") } + inspect } private def executeSql(sql: String*): Unit = { - val session = deployConnector match { - case c: PostgresDeployConnector => c.managementDatabase.createSession() - case c: MySqlDeployConnector => c.managementDatabase.database.createSession() + val (session, defaultSchema) = deployConnector match { + case c: PostgresDeployConnector => (c.managementDatabase.createSession(), s"""SET search_path TO "$projectId";""") + case c: MySqlDeployConnector => (c.managementDatabase.database.createSession(), s"USE `$projectId`;") + case c: SQLiteDeployConnector => (c.managementDatabase.database.createSession(), s"USE `$projectId`;") case x => sys.error(s"$x is not supported here") } - val statement = session.createStatement() - val setDefaultSchema = if (slickDatabase.isPostgres) s"""SET search_path TO "$projectId";""" else s"USE `$projectId`;" - statement.execute(setDefaultSchema) + val statement = session.createStatement() + statement.execute(defaultSchema) sql.foreach(statement.execute) session.close() } @@ -233,10 +229,8 @@ trait DataModelV2Base { self: PassiveDeploySpecBase => val result = mutation.execute.await result match { - case MutationSuccess(result) => - result - case MutationError => - sys.error("Deploy returned an unexpected error") + case MutationSuccess(result) => result + case MutationError => sys.error("Deploy returned an unexpected error") } } diff --git a/server/servers/deploy/src/test/scala/com/prisma/deploy/specutils/TestDeployDependencies.scala b/server/servers/deploy/src/test/scala/com/prisma/deploy/specutils/TestDeployDependencies.scala index cf888245b4..10deac0064 100644 --- a/server/servers/deploy/src/test/scala/com/prisma/deploy/specutils/TestDeployDependencies.scala +++ b/server/servers/deploy/src/test/scala/com/prisma/deploy/specutils/TestDeployDependencies.scala @@ -31,7 +31,8 @@ case class TestDeployDependencies()(implicit val system: ActorSystem, val materi CustomJdbcDriver.jna } else { new org.postgresql.Driver - }) + }), + SupportedDrivers.SQLITE -> new org.sqlite.JDBC ) implicit val reporter: ErrorReporter = DummyErrorReporter diff --git a/server/servers/servers-shared/src/test/scala/com/prisma/ConnectorAwareTest.scala b/server/servers/servers-shared/src/test/scala/com/prisma/ConnectorAwareTest.scala index 1f23679345..8d4253c470 100644 --- a/server/servers/servers-shared/src/test/scala/com/prisma/ConnectorAwareTest.scala +++ b/server/servers/servers-shared/src/test/scala/com/prisma/ConnectorAwareTest.scala @@ -39,7 +39,7 @@ trait ConnectorAwareTest extends SuiteMixin { self: Suite => case "postgres" => PostgresConnectorTag case "sqlite" => SQLiteConnectorTag } - private lazy val isPrototype: Boolean = connectorTag == MongoConnectorTag + private lazy val isPrototype: Boolean = prismaConfig.prototype.getOrElse(false) // connectorTag == MongoConnectorTag def capabilities: ConnectorCapabilities def runOnlyForConnectors: Set[ConnectorTag] = ConnectorTag.values.toSet @@ -97,23 +97,11 @@ trait ConnectorAwareTest extends SuiteMixin { self: Suite => } } - def ifConnectorIsNotMongo[T](assertion: => T): Unit = { - if (connectorTag != MongoConnectorTag) { - assertion - } - } - - def ifConnectorIsActive[T](assertion: => T): Unit = { - if (connector.active && connectorTag != MongoConnectorTag) { - assertion - } - } - - def ifConnectorIsPassive[T](assertion: => T): Unit = { - if (!connector.active) { - assertion - } - } + def ifConnectorIsNotSQLite[T](assertion: => T): Unit = if (connectorTag != SQLiteConnectorTag) assertion + def ifConnectorIsSQLite[T](assertion: => T): Unit = if (connectorTag == SQLiteConnectorTag) assertion + def ifConnectorIsNotMongo[T](assertion: => T): Unit = if (connectorTag != MongoConnectorTag) assertion + def ifConnectorIsActive[T](assertion: => T): Unit = if (connector.active && connectorTag != MongoConnectorTag) assertion + def ifConnectorIsPassive[T](assertion: => T): Unit = if (!connector.active) assertion private def ignoredTestsBasedOnIndividualTagging(connector: DatabaseConfig) = { val ignoreConnectorTypes = ignoreConnectorTags.filter(_.name.endsWith(connector.connector)) diff --git a/server/servers/subscriptions/src/test/scala/com/prisma/subscriptions/TestSubscriptionDependencies.scala b/server/servers/subscriptions/src/test/scala/com/prisma/subscriptions/TestSubscriptionDependencies.scala index b7e25c1b6c..7c8cad0597 100644 --- a/server/servers/subscriptions/src/test/scala/com/prisma/subscriptions/TestSubscriptionDependencies.scala +++ b/server/servers/subscriptions/src/test/scala/com/prisma/subscriptions/TestSubscriptionDependencies.scala @@ -34,7 +34,8 @@ class TestSubscriptionDependencies()(implicit val system: ActorSystem, val mater CustomJdbcDriver.jna } else { new org.postgresql.Driver - }) + }), + SupportedDrivers.SQLITE -> new org.sqlite.JDBC ) override val cacheFactory: CacheFactory = new CaffeineCacheFactory() diff --git a/server/shared-models/src/main/scala/com/prisma/shared/models/ConnectorCapabilities.scala b/server/shared-models/src/main/scala/com/prisma/shared/models/ConnectorCapabilities.scala index eda3ffd6b3..de0a137558 100644 --- a/server/shared-models/src/main/scala/com/prisma/shared/models/ConnectorCapabilities.scala +++ b/server/shared-models/src/main/scala/com/prisma/shared/models/ConnectorCapabilities.scala @@ -25,12 +25,12 @@ object ConnectorCapability extends Enumeratum[ConnectorCapability] { object SupportsExistingDatabasesCapability extends ConnectorCapability object MigrationsCapability extends ConnectorCapability object LegacyDataModelCapability extends ConnectorCapability + object RawAccessCapability extends ConnectorCapability object IntrospectionCapability extends ConnectorCapability object JoinRelationLinksCapability extends ConnectorCapability // the ability to join using relation links object MongoJoinRelationLinksCapability extends ConnectorCapability // does not allow NOT/OR and only _some on manyrelations object RelationLinkListCapability extends ConnectorCapability // relation links can be stored inline in a node in a list object RelationLinkTableCapability extends ConnectorCapability // relation links are stored in a table - // RawAccessCapability sealed trait IdCapability extends ConnectorCapability object IntIdCapability extends IdCapability @@ -69,7 +69,8 @@ object ConnectorCapabilities extends BooleanUtils { MigrationsCapability, NonEmbeddedScalarListCapability, NodeQueryCapability, - ImportExportCapability + ImportExportCapability, + RawAccessCapability ) ConnectorCapabilities(capas) } @@ -83,7 +84,8 @@ object ConnectorCapabilities extends BooleanUtils { RelationLinkTableCapability, IntrospectionCapability, IntIdCapability, - UuidIdCapability + UuidIdCapability, + RawAccessCapability ) val capas = if (isActive) { common ++ Set(MigrationsCapability, NonEmbeddedScalarListCapability, NodeQueryCapability, ImportExportCapability) @@ -116,6 +118,8 @@ object ConnectorCapabilities extends BooleanUtils { SupportsExistingDatabasesCapability, IntIdCapability, NonEmbeddedScalarListCapability, + RawAccessCapability, + IdSequenceCapability ) } diff --git a/server/shared-models/src/main/scala/com/prisma/shared/models/Field.scala b/server/shared-models/src/main/scala/com/prisma/shared/models/Field.scala index 636b3271dc..167dd704d1 100644 --- a/server/shared-models/src/main/scala/com/prisma/shared/models/Field.scala +++ b/server/shared-models/src/main/scala/com/prisma/shared/models/Field.scala @@ -1,7 +1,7 @@ package com.prisma.shared.models import com.prisma.gc_values.GCValue -import com.prisma.shared.models.FieldBehaviour.{CreatedAtBehaviour, IdBehaviour, IdStrategy, UpdatedAtBehaviour} +import com.prisma.shared.models.FieldBehaviour.{CreatedAtBehaviour, IdBehaviour, UpdatedAtBehaviour} import com.prisma.shared.models.Manifestations._ import scala.language.implicitConversions @@ -234,7 +234,6 @@ case class ScalarField( template: FieldTemplate, model: Model ) extends Field { - import template._ import ReservedFields._ override def isRelation = false