const jsonFieldExpressionParser = require('./parsers/jsonFieldExpressionParser'); const { isObject } = require('../utils/objectUtils'); class ReferenceBuilder { constructor(expr) { this._expr = expr; this._reference = null; this._column = null; this._table = null; this._cast = null; this._toJson = false; this._table = null; this._as = null; if (expr !== null) { const reference = jsonFieldExpressionParser.parse(expr); const colParts = reference.columnName.split('.'); this._reference = reference; this._column = colParts[colParts.length - 1]; if (colParts.length >= 2) { this._table = colParts.slice(0, colParts.length - 1).join('.'); } } } get reference() { return this._reference; } get column() { return this._column; } get tableName() { return this._table; } get isPlainColumnRef() { return this._reference.access.length === 0 && !this._cast && !this._toJson && !this._as; } get fullColumn() { if (this.tableName) { return `${this.tableName}.${this.column}`; } else { return this.column; } } get expression() { return this._expr; } get cast() { return this._cast; } castText() { return this.castTo('text'); } castInt() { return this.castTo('integer'); } castBigInt() { return this.castTo('bigint'); } castFloat() { return this.castTo('float'); } castDecimal() { return this.castTo('decimal'); } castReal() { return this.castTo('real'); } castBool() { return this.castTo('boolean'); } castJson() { this._toJson = true; return this; } castType(sqlType) { console.log( 'castType(type) is deprecated. Use castTo(type) instead. castType(type) will be removed in 2.0' ); return this.castTo(sqlType); } castTo(sqlType) { this._cast = sqlType; return this; } from(table) { this._table = table; return this; } table(table) { this._table = table; return this; } as(as) { this._as = as; return this; } clone() { const clone = new this.constructor(null); clone._expr = this._expr; clone._reference = this._reference; clone._column = this._column; clone._table = this._table; clone._cast = this._cast; clone._toJson = this._toJson; clone._table = this._table; clone._as = this._as; return clone; } toRawArgs() { let referenceSql = `??`; if (this._reference.access.length > 0) { let extractor = this._cast ? '#>>' : '#>'; let jsonFieldRef = this._reference.access.map(field => field.ref).join(','); referenceSql = `??${extractor}'{${jsonFieldRef}}'`; } let castedRefQuery = this._cast ? `CAST(${referenceSql} AS ${this._cast})` : referenceSql; let toJsonQuery = this._toJson ? `to_jsonb(${castedRefQuery})` : castedRefQuery; if (this._as) { return [`${toJsonQuery} as ??`, [this.fullColumn, this._as]]; } else { return [toJsonQuery, [this.fullColumn]]; } } toKnexRaw(knex) { if (this.isPlainColumnRef) { // Fast path for the most common case. return knex.raw('??', this.fullColumn); } else { return knex.raw.apply(knex, this.toRawArgs()); } } } Object.defineProperties(ReferenceBuilder.prototype, { isObjectionReferenceBuilder: { enumerable: false, writable: false, value: true } }); const ref = reference => { if (isObject(reference) && reference.isObjectionReferenceBuilder) { return reference; } else { return new ReferenceBuilder(reference); } }; module.exports = { ReferenceBuilder, ref };