@dazejs/framework
Version:
Daze.js - A powerful web framework for Node.js
390 lines (353 loc) • 10.1 kB
text/typescript
import { Builder } from '../builder';
export class Parser {
selectComponents: string[] = [
'aggregate',
'columns',
'from',
'joins',
'wheres',
'groups',
'havings',
'orders',
'limit',
'offset',
'lock',
];
/**
* parse select query builkder
* @param builder
*/
parseSelect(builder: Builder): string {
// 当聚合函数与 union 一起使用时,使用临时表名包裹
if (builder._aggregate && builder._unions.length) {
const _sql = `${this.parseAggregate(builder)}`;
return `${_sql} from (${this.parseSelect(builder.removeAggregate())}) as temp`;
}
// select sql
const sql = this.parseSelectComponents(builder);
// 当使用了 union, 将 union sql 合并
if (builder._unions.length) {
return `(${sql}) ${this.parseUnions(builder)}`;
}
return sql;
}
/**
* parse insert
* @param builder
* @param columns
*/
parseInsert(builder: Builder, data?: Record<string, any>[]) {
builder.alias('');
if (!data || !data.length) {
return `insert into ${this.getTable(builder)} default values`;
}
const columns = Object.keys(data[0]);
const _columns = this.columnDelimite(columns, builder);
// const values = data.map(() => `(${columns.map(() => this.parameter()).join(', ')})`).join(', ');
const values = data.map(() => `(${this.parameter()})`).join(', ');
return `insert into ${this.getTable(builder)} (${_columns}) values ${values}`;
}
/**
* parse update
* @param builder
* @param columns
*/
parseUpdate(builder: Builder, columns: string[] = []) {
const _columns = this.columnDelimiteForUpdate(columns, builder);
const where = this.parseWheres(builder);
if (builder._joins.length > 0) {
const joins = this.parseJoins(builder);
return `update ${this.getTable(builder)} ${joins} set ${_columns} ${where}`;
}
return `update ${this.getTable(builder)} set ${_columns} ${where}`;
}
/**
* parse delete
* @param builder
*/
parseDelete(builder: Builder) {
builder.alias('');
const where = this.parseWheres(builder);
if (builder._joins.length > 0) {
const joins = this.parseJoins(builder);
return `delete from ${this.getTable(builder)} ${joins} ${where}`;
}
return `delete from ${this.getTable(builder)} ${where}`;
}
/**
* parser query components
* @param builder
*/
parseSelectComponents(builder: Builder): string {
const sqls: string[] = [];
for (const component of this.selectComponents) {
const sql = this.parseComponent(builder, component);
if (sql) {
sqls.push(sql);
}
}
return sqls.join(' ');
}
/**
* parse components
* @param builder
* @param part
*/
parseComponent(builder: Builder, part: string) {
switch(part) {
case 'aggregate':
return this.parseAggregate(builder);
case 'columns':
return this.parseColumns(builder);
case 'from':
return this.parseFrom(builder);
case 'joins':
return this.parseJoins(builder);
case 'wheres':
return this.parseWheres(builder);
case 'groups':
return this.parseGroups(builder);
case 'havings':
return this.parseHavings(builder);
case 'orders':
return this.parseOrders(builder);
case 'limit':
return this.parseLimit(builder);
case 'offset':
return this.parseOffset(builder);
case 'lock':
return this.parseLock(builder);
default:
return '';
}
}
/**
* parse aggregate function
* @param builder
*/
parseAggregate(builder: Builder) {
if (!builder._aggregate) return '';
let { column } = builder._aggregate;
// 需要去重
if (builder._distinct === true && column !== '*') {
column = `distinct ${this.wrapColum(column, builder)}`;
} else if (Array.isArray(builder._distinct)) {
column = `distinct ${this.columnDelimite(builder._distinct, builder)}`;
}
return `select ${builder._aggregate.func}(${column}) as aggregate`;
}
/**
* parse Columns
* @param builder
*/
parseColumns(builder: Builder) {
if (builder._aggregate) return '';
const select = builder._distinct ? 'select distinct' : 'select';
if (!builder._columns.length) return `${select} *`;
const str = builder._columns.map(({
type,
column,
as
}) => {
if (type === 'raw') return column;
return this.wrapColum(column, builder, as);
}).join(', ');
return `${select} ${str}`;
}
/**
* parse table target
* @param builder
*/
parseFrom(builder: Builder) {
return builder._table ? `from ${this.getTable(builder)}` : '';
}
/**
* parse groups
* @param builder
*/
parseGroups(builder: Builder) {
return builder._groups.length ? `group by ${this.columnDelimite(builder._groups, builder)}`: '';
}
/**
* parse where
* @param builder
*/
parseWheres(builder: Builder, conjunction = 'where') {
if (!builder._wheres.length) return '';
const wheres: string[] = [];
for (const where of builder._wheres) {
const leadSymlink = where.symlink ? `${where.symlink} ` : '';
// value type
if (where.type === 'value') {
wheres.push(
//where.value
`${leadSymlink}${this.wrapColum(where.column as string, builder)} ${where.operator} ${this.parameter()}`
);
}
// column type
if (where.type === 'column') {
wheres.push(
`${leadSymlink}${this.wrapColum(where.column as string, builder)} ${where.operator} ${this.wrapColum(where.value as string, builder)}`
);
}
if (where.type === 'sql') {
wheres.push(
`${leadSymlink}${where.value}`
);
}
if (where.type === 'in') {
wheres.push(
`${leadSymlink}${this.wrapColum(where.column as string, builder)} in (${this.parameterize(where.value)})`
);
}
if (where.type === 'notIn') {
wheres.push(
`${leadSymlink}${this.wrapColum(where.column as string, builder)} not in (${this.parameterize(where.value)})`
);
}
if (where.type === 'null') {
wheres.push(
`${leadSymlink}${this.wrapColum(where.column as string, builder)} is null`
);
}
if (where.type === 'notNull') {
wheres.push(
`${leadSymlink}${this.wrapColum(where.column as string, builder)} is not ull`
);
}
}
return `${conjunction} ${wheres.join(' ')}`;
}
/**
* parse orders
* @param builder
*/
parseOrders(builder: Builder) {
if (!builder._orders.length || builder._aggregate) return '';
const flatOrders = builder._orders.map(order => {
if (order.type === 'column') {
return `${order.column} ${order.direction}`;
}
return order.column;
});
return `order by ${flatOrders.join(', ')}`;
}
/**
* parse limit
* @param builder
*/
parseLimit(builder: Builder) {
return builder._limit && builder._limit >= 0 ? `limit ${builder._limit}` : '';
}
/**
* parse offset
*/
parseOffset(builder: Builder) {
return builder._offset && builder._offset >= 0 ? `offset ${builder._offset}` : '';
}
/**
* parse lock
* @param builder
*/
parseLock(builder: Builder) {
if (typeof builder._lock === 'string') return builder._lock;
return '';
}
/**
* parse joins
* @param builder
*/
parseJoins(builder: Builder) {
if (!builder._joins.length) return '';
const joins: string[] = [];
for (const join of builder._joins) {
joins.push(
`${join.type} join ${this.getTable(join.builder)} ${this.parseWheres(join.builder, 'on')}`
);
}
return joins.join(' ');
}
/**
* parse having
* @param builder
*/
parseHavings(builder: Builder) {
if (!builder._havings.length) return '';
const havings: string[] = [];
for (const having of builder._havings) {
const leadSymlink = having.symlink ? `${having.symlink} ` : '';
havings.push(
`${leadSymlink}${having.column} ${having.operator} ${having.value}`
);
}
return `having ${havings.join(' ')}`;
}
/**
* parse unnions
* @param builder
*/
parseUnions(builder: Builder) {
const sqls: string[] = [];
if (!builder._unions.length) return '';
for (const union of builder._unions) {
sqls.push(
`${union.isAll ? 'union all' : 'union'} (${union.builder.toSql()})`
);
}
return `${sqls.join(' ')}`;
}
/**
* parameterize params
* @param value
*/
parameterize(value: any[]) {
return value.map(() => this.parameter());
}
/**
* parameter param
*/
parameter() {
return '?';
}
/**
* Delimite columns
* @param columns
*/
columnDelimite(columns: string[], builder: Builder) {
if (!columns) return '';
return columns.map(column => this.wrapColum(column, builder)).join(', ');
}
/**
* Delimite columns for update
* @param columns
*/
columnDelimiteForUpdate(columns: string[], builder: Builder) {
if (!columns) return '';
return columns.map(column => `${this.wrapColum(column, builder)} = ${this.parameter()}`).join(', ');
}
/**
* wrapColum
* @param column
*/
wrapColum(column: string, builder: Builder, as?: string) {
if (~column.indexOf('.')) {
const [_alias, _column] = column.split('.');
if (_column === '*') return _column;
return `\`${_alias}\`.\`${_column}\`${as ? ` as ${as}` : ''}`;
}
if (builder._alias) {
return `\`${builder._alias}\`.\`${column}\`${as ? ` as ${as}` : ''}`;
}
return `\`${builder._table}\`.\`${column}\`${as ? ` as ${as}` : ''}`;
}
/**
* get table name
* @param builder
*/
getTable(builder: Builder) {
if (builder._alias) {
return `\`${builder._table}\` as \`${builder._alias}\``;
}
return `\`${builder._table}\``;
}
}