You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
4.9 KiB

import ConnectionMutable from './ConnectionMutable.ts'
import {EscapedValue, FieldValueObject, QuerySource} from '../types.ts'
import {MalformedSQLGrammarError} from './Select.ts'
import {TableRefBuilder} from './TableRefBuilder.ts'
import {applyMixins} from '../../../../lib/src/support/mixins.ts'
import {escape, FieldSet} from '../types.ts'
import {raw} from '../Builder.ts'
// TODO support DEFAULT VALUES
// TODO support ON CONFLICT
/**
* Query builder base for INSERT queries.
* @extends ConnectionMutable
* @extends TableRefBuilder
*/
export class Insert<T> extends ConnectionMutable<T> {
/**
* The target table to insert into.
* @type QuerySource
*/
protected _target?: QuerySource = undefined
/**
* The columns to insert.
* @type Array<string>
*/
protected _columns: string[] = []
/**
* The row data to insert.
* @type Array<string>
*/
protected _rows: string[] = []
/**
* The fields to insert.
* @type Array<string>
*/
protected _fields: string[] = []
/**
* Return all data?
* @type boolean
*/
protected _return_all = false
sql(level = 0): string {
const indent = Array(level * 2).fill(' ').join('')
if ( typeof this._target === 'undefined' )
throw new MalformedSQLGrammarError('No table reference has been provided.')
if ( this._rows.length < 1 )
throw new MalformedSQLGrammarError('There are no rows to insert.')
const table_ref = this.source_alias_to_table_ref(this._target)
const returning = this._return_all ? this._columns.join(', ') : this._fields.join(', ')
const fields = escape(this._columns.map(x => raw(x)))
return [
`INSERT INTO ${this.serialize_table_ref(table_ref)}`,
` ${fields}`,
'VALUES',
` ${this._rows.join(',\n ')}`,
...(returning.trim() ? [`RETURNING ${returning}`] : [])
].filter(x => String(x).trim()).join(`\n${indent}`)
}
/**
* Set the table to insert into.
* @param {QuerySource} source
* @param {string} [alias]
* @return Insert
*/
into(source: QuerySource, alias?: string) {
if ( !alias ) this._target = source
else this._target = { ref: source, alias }
return this
}
/**
* Set the columns to insert.
* @param {...string} columns
* @return Insert
*/
columns(...columns: string[]) {
this._columns = columns
return this
}
/**
* Add raw row data to insert.
* @param {...EscapedValue} row
* @return Insert
*/
row_raw(...row: EscapedValue[]) {
if ( row.length !== this._columns.length )
throw new MalformedSQLGrammarError(`Cannot insert row with ${row.length} values using a query that has ${this._columns.length} columns specified.`)
this._rows.push(escape(row))
return this
}
/**
* Add a field value object to insert.
* @param {FieldValueObject} row
* @return Insert
*/
row(row: FieldValueObject) {
const columns = []
const row_raw = []
for ( const field in row ) {
if ( !row.hasOwnProperty(field) ) continue
columns.push(field)
row_raw.push(row[field])
}
this.columns(...columns)
this.row_raw(...row_raw)
return this
}
/**
* Add multiple field value objects to insert.
* @param {Array<FieldValueObject>}rows
* @return Insert
*/
rows(rows: FieldValueObject[]) {
const [initial, ...rest] = rows
const columns = []
const initial_raw = []
for ( const field in initial ) {
if ( !initial.hasOwnProperty(field) ) continue
columns.push(field)
initial_raw.push(initial[field])
}
this.columns(...columns)
this.row_raw(...initial_raw)
for ( const row of rest ) {
const values = []
for ( const col of columns ) {
values.push(row[col])
}
this.row_raw(...values)
}
return this
}
/**
* Set the fields to return after insert.
* @param {...FieldSet} fields
* @return Insert
*/
returning(...fields: FieldSet[]) {
for ( const field_set of fields ) {
if ( typeof field_set === 'string' ) {
if ( !this._fields.includes(field_set) )
this._fields.push(field_set)
} else {
for ( const field of field_set ) {
if ( !this._fields.includes(field) )
this._fields.push(field)
}
}
}
this._return_all = this._fields.length === 0
return this
}
}
export interface Insert<T> extends TableRefBuilder {}
applyMixins(Insert, [TableRefBuilder])