Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into nulls-not-distinct
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Vershinin committed Nov 20, 2023
2 parents 3d7cad2 + 31c5258 commit af247ac
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/operation-node/create-table-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface CreateTableNode extends OperationNode {
readonly onCommit?: OnCommitAction
readonly frontModifiers?: ReadonlyArray<OperationNode>
readonly endModifiers?: ReadonlyArray<OperationNode>
readonly selectQuery?: OperationNode
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/operation-node/operation-node-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ export class OperationNodeTransformer {
onCommit: node.onCommit,
frontModifiers: this.transformNodeList(node.frontModifiers),
endModifiers: this.transformNodeList(node.endModifiers),
selectQuery: this.transformNode(node.selectQuery),
})
}

Expand Down
27 changes: 17 additions & 10 deletions src/query-compiler/default-query-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export class DefaultQueryCompiler
this.parentNode !== undefined &&
!ParensNode.is(this.parentNode) &&
!InsertQueryNode.is(this.parentNode) &&
!CreateTableNode.is(this.parentNode) &&
!CreateViewNode.is(this.parentNode) &&
!SetOperationNode.is(this.parentNode)

Expand Down Expand Up @@ -549,18 +550,24 @@ export class DefaultQueryCompiler
}

this.visitNode(node.table)
this.append(' (')
this.compileList([...node.columns, ...(node.constraints ?? [])])
this.append(')')

if (node.onCommit) {
this.append(' on commit ')
this.append(node.onCommit)
}
if (node.selectQuery) {
this.append(' as ')
this.visitNode(node.selectQuery)
} else {
this.append(' (')
this.compileList([...node.columns, ...(node.constraints ?? [])])
this.append(')')

if (node.endModifiers && node.endModifiers.length > 0) {
this.append(' ')
this.compileList(node.endModifiers, ' ')
if (node.onCommit) {
this.append(' on commit ')
this.append(node.onCommit)
}

if (node.endModifiers && node.endModifiers.length > 0) {
this.append(' ')
this.compileList(node.endModifiers, ' ')
}
}
}

Expand Down
29 changes: 29 additions & 0 deletions src/schema/create-table-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
UniqueConstraintNodeBuilder,
UniqueConstraintNodeBuilderCallback,
} from './unique-constraint-builder.js'
import { parseExpression } from '../parser/expression-parser.js'

/**
* This builder can be used to create a `create table` query.
Expand Down Expand Up @@ -375,6 +376,34 @@ export class CreateTableBuilder<TB extends string, C extends string = never>
})
}

/**
* Allows to create table from `select` query.
*
* ### Examples
*
* ```ts
* db.schema.createTable('copy')
* .temporary()
* .as(db.selectFrom('person').select(['first_name', 'last_name']))
* .execute()
* ```
*
* The generated SQL (PostgreSQL):
*
* ```sql
* create temporary table "copy" as
* select "first_name", "last_name" from "person"
* ```
*/
as(expression: Expression<unknown>) {
return new CreateTableBuilder({
...this.#props,
node: CreateTableNode.cloneWith(this.#props.node, {
selectQuery: parseExpression(expression),
}),
})
}

/**
* Calls the given function passing `this` as the only argument.
*
Expand Down
107 changes: 107 additions & 0 deletions test/node/src/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,113 @@ for (const dialect of DIALECTS) {

await builder.execute()
})

it('should create a table with as expression', async () => {
const builder = ctx.db.schema
.createTable('test')
.as(ctx.db.selectFrom('person').select(['first_name', 'last_name']))

testSql(builder, dialect, {
postgres: {
sql: 'create table "test" as select "first_name", "last_name" from "person"',
parameters: [],
},
mysql: {
sql: 'create table `test` as select `first_name`, `last_name` from `person`',
parameters: [],
},
mssql: NOT_SUPPORTED,
sqlite: {
sql: 'create table "test" as select "first_name", "last_name" from "person"',
parameters: [],
},
})

await builder.execute()
})

it('should create a temporary table if not exists with as expression', async () => {
const builder = ctx.db.schema
.createTable('test')
.temporary()
.ifNotExists()
.as(
ctx.db
.selectFrom('person')
.select(['first_name', 'last_name'])
.where('first_name', '=', 'Jennifer')
)

testSql(builder, dialect, {
postgres: {
sql: 'create temporary table if not exists "test" as select "first_name", "last_name" from "person" where "first_name" = $1',
parameters: ['Jennifer'],
},
mysql: {
sql: 'create temporary table if not exists `test` as select `first_name`, `last_name` from `person` where `first_name` = ?',
parameters: ['Jennifer'],
},
mssql: NOT_SUPPORTED,
sqlite: {
sql: 'create temporary table if not exists "test" as select "first_name", "last_name" from "person" where "first_name" = ?',
parameters: ['Jennifer'],
},
})

await builder.execute()
})

it('should create a table with as expression and raw sql', async () => {
let rawSql = sql`select "first_name", "last_name" from "person"`
if (dialect === 'mysql') {
rawSql = sql`select \`first_name\`, \`last_name\` from \`person\``
}

const builder = ctx.db.schema.createTable('test').as(rawSql)

testSql(builder, dialect, {
postgres: {
sql: 'create table "test" as select "first_name", "last_name" from "person"',
parameters: [],
},
mysql: {
sql: 'create table `test` as select `first_name`, `last_name` from `person`',
parameters: [],
},
mssql: NOT_SUPPORTED,
sqlite: {
sql: 'create table "test" as select "first_name", "last_name" from "person"',
parameters: [],
},
})

await builder.execute()
})

it('should create a table with as expression and ignore addColumn', async () => {
const builder = ctx.db.schema
.createTable('test')
.as(ctx.db.selectFrom('person').select(['first_name', 'last_name']))
.addColumn('first_name', 'varchar(20)')

testSql(builder, dialect, {
postgres: {
sql: 'create table "test" as select "first_name", "last_name" from "person"',
parameters: [],
},
mysql: {
sql: 'create table `test` as select `first_name`, `last_name` from `person`',
parameters: [],
},
mssql: NOT_SUPPORTED,
sqlite: {
sql: 'create table "test" as select "first_name", "last_name" from "person"',
parameters: [],
},
})

await builder.execute()
})
}

if (dialect === 'mssql') {
Expand Down
5 changes: 5 additions & 0 deletions test/typings/test-d/create-table.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ async function testCreateTableWithAddUniqueConstraint(db: Kysely<Database>) {
.addUniqueConstraint('a_unique', ['a'], 'wrong option')
)
}

async function testCreateTableWithAsStatement(db: Kysely<Database>) {
expectError(db.schema.createTable('test').as())
expectError(db.schema.createTable('test').as('test'))
}

0 comments on commit af247ac

Please sign in to comment.