Setup eslint and enforce rules
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2021-06-02 22:36:25 -05:00
parent 82e7a1f299
commit 1d5056b753
149 changed files with 6104 additions and 3114 deletions

View File

@@ -1,6 +1,6 @@
import {EscapeValue, QuerySafeValue, raw, SQLDialect} from './SQLDialect';
import {Constraint, isConstraintGroup, isConstraintItem, SpecifiedField} from "../types";
import {AbstractBuilder} from "../builder/AbstractBuilder";
import {EscapeValue, QuerySafeValue, raw, SQLDialect} from './SQLDialect'
import {Constraint, isConstraintGroup, isConstraintItem, SpecifiedField} from '../types'
import {AbstractBuilder} from '../builder/AbstractBuilder'
/**
* An implementation of the SQLDialect specific to PostgreSQL.
@@ -8,8 +8,9 @@ import {AbstractBuilder} from "../builder/AbstractBuilder";
export class PostgreSQLDialect extends SQLDialect {
public escape(value: EscapeValue): QuerySafeValue {
if ( value instanceof QuerySafeValue ) return value
else if ( Array.isArray(value) ) {
if ( value instanceof QuerySafeValue ) {
return value
} else if ( Array.isArray(value) ) {
return new QuerySafeValue(value, `(${value.map(v => this.escape(v)).join(',')})`)
} else if ( String(value).toLowerCase() === 'true' || value === true ) {
return new QuerySafeValue(value, 'TRUE')
@@ -34,7 +35,7 @@ export class PostgreSQLDialect extends SQLDialect {
} else if ( value === null || typeof value === 'undefined' ) {
return new QuerySafeValue(value, 'NULL')
} else {
const escaped = value.replace(/'/g, '\\\'') //.replace(/"/g, '\\"').replace(/`/g, '\\`')
const escaped = value.replace(/'/g, '\\\'') // .replace(/"/g, '\\"').replace(/`/g, '\\`')
return new QuerySafeValue(value, `'${escaped}'`)
}
}
@@ -44,7 +45,7 @@ export class PostgreSQLDialect extends SQLDialect {
'SELECT COUNT(*) AS "extollo_render_count"',
'FROM (',
...query.split('\n').map(x => ` ${x}`),
') AS extollo_target_query'
') AS extollo_target_query',
].join('\n')
}
@@ -54,35 +55,46 @@ export class PostgreSQLDialect extends SQLDialect {
'FROM (',
...query.split('\n').map(x => ` ${x}`),
') AS extollo_target_query',
`OFFSET ${start} LIMIT ${(end - start) + 1}`
`OFFSET ${start} LIMIT ${(end - start) + 1}`,
].join('\n')
}
/** Render the fields from the builder class to PostgreSQL syntax. */
protected renderFields(builder: AbstractBuilder<any>) {
protected renderFields(builder: AbstractBuilder<any>): string[] {
return builder.appliedFields.map((field: SpecifiedField) => {
let columnString: string
if ( typeof field === 'string' ) columnString = field.split('.').map(x => `"${x}"`).join('.')
else if ( field instanceof QuerySafeValue ) columnString = field.toString()
else if ( typeof field.field === 'string' ) columnString = field.field.split('.').map(x => `"${x}"`).join('.')
else columnString = field.field.toString()
if ( typeof field === 'string' ) {
columnString = field.split('.').map(x => `"${x}"`)
.join('.')
} else if ( field instanceof QuerySafeValue ) {
columnString = field.toString()
} else if ( typeof field.field === 'string' ) {
columnString = field.field.split('.').map(x => `"${x}"`)
.join('.')
} else {
columnString = field.field.toString()
}
let aliasString = ''
if ( typeof field !== 'string' && !(field instanceof QuerySafeValue) ) aliasString = ` AS "${field.alias}"`
if ( typeof field !== 'string' && !(field instanceof QuerySafeValue) ) {
aliasString = ` AS "${field.alias}"`
}
return `${columnString}${aliasString}`
})
}
public renderSelect(builder: AbstractBuilder<any>): string {
const indent = (item: string, level = 1) => Array(level + 1).fill('').join(' ') + item
const indent = (item: string, level = 1) => Array(level + 1).fill('')
.join(' ') + item
const queryLines = [
`SELECT${builder.appliedDistinction ? ' DISTINCT' : ''}`
`SELECT${builder.appliedDistinction ? ' DISTINCT' : ''}`,
]
// Add fields
// FIXME error if no fields
const fields = this.renderFields(builder).map(x => indent(x)).join(',\n')
const fields = this.renderFields(builder).map(x => indent(x))
.join(',\n')
queryLines.push(fields)
@@ -91,7 +103,8 @@ export class PostgreSQLDialect extends SQLDialect {
const source = builder.querySource
if ( source ) {
const tableString = typeof source === 'string' ? source : source.table
const table: string = tableString.split('.').map(x => `"${x}"`).join('.')
const table: string = tableString.split('.').map(x => `"${x}"`)
.join('.')
queryLines.push('FROM ' + (typeof source === 'string' ? table : `${table} "${source.alias}"`))
}
@@ -105,7 +118,8 @@ export class PostgreSQLDialect extends SQLDialect {
// Add group by
if ( builder.appliedGroupings?.length ) {
const grouping = builder.appliedGroupings.map(group => {
return indent(group.split('.').map(x => `"${x}"`).join('.'))
return indent(group.split('.').map(x => `"${x}"`)
.join('.'))
}).join(',\n')
queryLines.push('GROUP BY')
@@ -114,7 +128,8 @@ export class PostgreSQLDialect extends SQLDialect {
// Add order by
if ( builder.appliedOrder?.length ) {
const ordering = builder.appliedOrder.map(x => indent(`${x.field.split('.').map(x => '"' + x + '"').join('.')} ${x.direction}`)).join(',\n')
const ordering = builder.appliedOrder.map(x => indent(`${x.field.split('.').map(y => '"' + y + '"')
.join('.')} ${x.direction}`)).join(',\n')
queryLines.push('ORDER BY')
queryLines.push(ordering)
}
@@ -132,14 +147,14 @@ export class PostgreSQLDialect extends SQLDialect {
// TODO support FROM, RETURNING
public renderUpdate(builder: AbstractBuilder<any>, data: {[key: string]: EscapeValue}): string {
const indent = (item: string, level = 1) => Array(level + 1).fill('').join(' ') + item
const queryLines: string[] = []
// Add table source
const source = builder.querySource
if ( source ) {
const tableString = typeof source === 'string' ? source : source.table
const table: string = tableString.split('.').map(x => `"${x}"`).join('.')
const table: string = tableString.split('.').map(x => `"${x}"`)
.join('.')
queryLines.push('UPDATE ' + (typeof source === 'string' ? table : `${table} "${source.alias}"`))
}
@@ -166,17 +181,21 @@ export class PostgreSQLDialect extends SQLDialect {
// FIXME: subquery support here and with select
public renderInsert(builder: AbstractBuilder<any>, data: {[key: string]: EscapeValue}|{[key: string]: EscapeValue}[] = []): string {
const indent = (item: string, level = 1) => Array(level + 1).fill('').join(' ') + item
const indent = (item: string, level = 1) => Array(level + 1).fill('')
.join(' ') + item
const queryLines: string[] = []
if ( !Array.isArray(data) ) data = [data]
if ( !Array.isArray(data) ) {
data = [data]
}
const columns = Object.keys(data[0])
// Add table source
const source = builder.querySource
if ( source ) {
const tableString = typeof source === 'string' ? source : source.table
const table: string = tableString.split('.').map(x => `"${x}"`).join('.')
const table: string = tableString.split('.').map(x => `"${x}"`)
.join('.')
queryLines.push('INSERT INTO ' + (typeof source === 'string' ? table : `${table} AS "${source.alias}"`)
+ (columns.length ? ` (${columns.join(', ')})` : ''))
}
@@ -187,9 +206,9 @@ export class PostgreSQLDialect extends SQLDialect {
queryLines.push('VALUES')
const valueString = data.map(row => {
const values = columns.map(x => this.escape(row[x]))
return indent(`(${values.join(', ')})`)
})
const values = columns.map(x => this.escape(row[x]))
return indent(`(${values.join(', ')})`)
})
.join(',\n')
queryLines.push(valueString)
@@ -198,7 +217,8 @@ export class PostgreSQLDialect extends SQLDialect {
// Add return fields
if ( builder.appliedFields?.length ) {
queryLines.push('RETURNING')
const fields = this.renderFields(builder).map(x => indent(x)).join(',\n')
const fields = this.renderFields(builder).map(x => indent(x))
.join(',\n')
queryLines.push(fields)
}
@@ -207,14 +227,16 @@ export class PostgreSQLDialect extends SQLDialect {
}
public renderDelete(builder: AbstractBuilder<any>): string {
const indent = (item: string, level = 1) => Array(level + 1).fill('').join(' ') + item
const indent = (item: string, level = 1) => Array(level + 1).fill('')
.join(' ') + item
const queryLines: string[] = []
// Add table source
const source = builder.querySource
if ( source ) {
const tableString = typeof source === 'string' ? source : source.table
const table: string = tableString.split('.').map(x => `"${x}"`).join('.')
const table: string = tableString.split('.').map(x => `"${x}"`)
.join('.')
queryLines.push('DELETE FROM ' + (typeof source === 'string' ? table : `${table} "${source.alias}"`))
}
@@ -229,7 +251,8 @@ export class PostgreSQLDialect extends SQLDialect {
if ( builder.appliedFields?.length ) {
queryLines.push('RETURNING')
const fields = this.renderFields(builder).map(x => indent(x)).join(',\n')
const fields = this.renderFields(builder).map(x => indent(x))
.join(',\n')
queryLines.push(fields)
}
@@ -237,16 +260,18 @@ export class PostgreSQLDialect extends SQLDialect {
return queryLines.join('\n')
}
public renderConstraints(constraints: Constraint[]): string {
public renderConstraints(allConstraints: Constraint[]): string {
const constraintsToSql = (constraints: Constraint[], level = 1): string => {
const indent = Array(level * 2).fill(' ').join('')
let statements = []
const indent = Array(level * 2).fill(' ')
.join('')
const statements = []
for ( const constraint of constraints ) {
if ( isConstraintGroup(constraint) ) {
statements.push(`${indent}${statements.length < 1 ? '' : constraint.preop + ' '}(\n${constraintsToSql(constraint.items, level + 1)}\n${indent})`)
} else if ( isConstraintItem(constraint) ) {
const field: string = constraint.field.split('.').map(x => `"${x}"`).join('.')
const field: string = constraint.field.split('.').map(x => `"${x}"`)
.join('.')
statements.push(`${indent}${statements.length < 1 ? '' : constraint.preop + ' '}${field} ${constraint.operator} ${this.escape(constraint.operand).value}`)
}
}
@@ -254,13 +279,15 @@ export class PostgreSQLDialect extends SQLDialect {
return statements.filter(Boolean).join('\n')
}
return constraintsToSql(constraints)
return constraintsToSql(allConstraints)
}
public renderUpdateSet(data: {[key: string]: EscapeValue}) {
public renderUpdateSet(data: {[key: string]: EscapeValue}): string {
const sets = []
for ( const key in data ) {
if ( !data.hasOwnProperty(key) ) continue
if ( !Object.prototype.hasOwnProperty.call(data, key) ) {
continue
}
sets.push(` "${key}" = ${this.escape(data[key])}`)
}