UNPKG

@nuvix/pg

Version:

PostgreSQL integration for Nuvix

1,728 lines (1,711 loc) 169 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var EventEmitter5 = require('events'); var assert3 = require('assert'); var assign3 = require('lodash/assign'); var clone2 = require('lodash/clone'); var each = require('lodash/each'); var isEmpty2 = require('lodash/isEmpty'); var isPlainObject2 = require('lodash/isPlainObject'); var last = require('lodash/last'); var reject = require('lodash/reject'); var tail = require('lodash/tail'); var toArray = require('lodash/toArray'); var lodash = require('lodash'); var util = require('util'); var Debug = require('debug'); var color = require('colorette'); var compact = require('lodash/compact'); var groupBy = require('lodash/groupBy'); var has = require('lodash/has'); var map = require('lodash/map'); var omitBy = require('lodash/omitBy'); var reduce2 = require('lodash/reduce'); var extend = require('lodash/extend'); var merge = require('lodash/merge'); var Redis = require('ioredis'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var EventEmitter5__default = /*#__PURE__*/_interopDefault(EventEmitter5); var assert3__default = /*#__PURE__*/_interopDefault(assert3); var assign3__default = /*#__PURE__*/_interopDefault(assign3); var clone2__default = /*#__PURE__*/_interopDefault(clone2); var each__default = /*#__PURE__*/_interopDefault(each); var isEmpty2__default = /*#__PURE__*/_interopDefault(isEmpty2); var isPlainObject2__default = /*#__PURE__*/_interopDefault(isPlainObject2); var last__default = /*#__PURE__*/_interopDefault(last); var reject__default = /*#__PURE__*/_interopDefault(reject); var tail__default = /*#__PURE__*/_interopDefault(tail); var toArray__default = /*#__PURE__*/_interopDefault(toArray); var Debug__default = /*#__PURE__*/_interopDefault(Debug); var color__namespace = /*#__PURE__*/_interopNamespace(color); var compact__default = /*#__PURE__*/_interopDefault(compact); var groupBy__default = /*#__PURE__*/_interopDefault(groupBy); var has__default = /*#__PURE__*/_interopDefault(has); var map__default = /*#__PURE__*/_interopDefault(map); var omitBy__default = /*#__PURE__*/_interopDefault(omitBy); var reduce2__default = /*#__PURE__*/_interopDefault(reduce2); var extend__default = /*#__PURE__*/_interopDefault(extend); var merge__default = /*#__PURE__*/_interopDefault(merge); var Redis__default = /*#__PURE__*/_interopDefault(Redis); var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); var Metadata = class { static defaultConfig = { enableRLS: true, enableCLS: true, computedFields: {}, cacheEnabled: true, defaultRole: "public", useNullAsDefault: false }; static globalConfig = { ...this.defaultConfig }; static eventEmitter = new EventEmitter5.EventEmitter(); // Get global config static get(key) { return this.globalConfig[key]; } // Set global config and notify listeners static set(key, value) { this.globalConfig = { ...this.globalConfig, [key]: value }; this.eventEmitter.emit("update", key, value); } // Subscribe to updates (Used by DataSource) static onUpdate(listener) { this.eventEmitter.on("update", listener); } // Override config per instance (Creates a mutable copy) static createInstanceConfig(overrides) { const config = { ...this.globalConfig }; if (overrides) { Object.assign(config, overrides); } return config; } // Reset to default values (for testing) static resetToDefaults() { this.globalConfig = { ...this.defaultConfig }; } }; // src/core/constants.ts var PgPermission = /* @__PURE__ */ ((PgPermission2) => { PgPermission2["CREATE"] = "insert"; PgPermission2["READ"] = "select"; PgPermission2["UPDATE"] = "update"; PgPermission2["DELETE"] = "delete"; return PgPermission2; })(PgPermission || {}); var Constants = { METADATA_SCHEMA: "system", SCHEMA_TABLE: "schemas", TABLE_TABLE: "tables" }; // src/core/context.ts var Context = class _Context { /** * A record of roles assigned to the context. Each role is represented as a key * with a boolean value indicating its presence. By default, the `any` role is set to `true`. */ roles = { any: true }; /** * The authentication status of the context. Defaults to `true` (enabled). */ authStatus = true; /** * The schema associated with the context. Defaults to `"public"`. */ schema = "public"; /** * Cache for permissions to improve performance */ permissionCache = /* @__PURE__ */ new Map(); /** * Additional metadata for context */ metadata = {}; /** * Retrieves the current authentication status. * @returns `true` if authentication is enabled, otherwise `false`. */ getAuthStatus() { return this.authStatus; } constructor(ctxOrObject, data) { if (ctxOrObject instanceof _Context) { Object.assign(this, ctxOrObject); if (ctxOrObject.permissionCache) { ctxOrObject.permissionCache.forEach((value, key) => { this.permissionCache.set(key, value); }); } if (ctxOrObject.metadata) { this.metadata = { ...ctxOrObject.metadata }; } if (data) { Object.assign(this, data); } } else { Object.assign(this, ctxOrObject); } } /** * Sets multiple roles at once * @param roles - Array of roles to assign */ setRoles(roles) { for (const role of roles) { this.roles[role] = true; } this.permissionCache.clear(); } /** * Assigns a role to the context. * @param role - The name of the role to assign. */ setRole(role) { this.roles[role] = true; this.permissionCache.clear(); } /** * Removes a role from the context. * @param role - The name of the role to remove. */ unsetRole(role) { delete this.roles[role]; this.permissionCache.clear(); } /** * Retrieves all roles currently assigned to the context. * @returns An array of role names. */ getRoles() { return Object.keys(this.roles); } /** * Clears all roles from the context. */ cleanRoles() { this.roles = {}; this.permissionCache.clear(); } /** * Checks if a specific role is assigned to the context. * @param role - The name of the role to check. * @returns `true` if the role is assigned, otherwise `false`. */ isRole(role) { return this.getRoles().includes(role); } /** * Check if the context has any of the specified roles * @param roles - Array of roles to check * @returns `true` if the context has any of the roles, otherwise `false` */ hasAnyRole(roles) { const cacheKey = `anyRole:${roles.sort().join(",")}`; if (this.permissionCache.has(cacheKey)) { return this.permissionCache.get(cacheKey); } const hasRole = roles.some((role) => this.isRole(role)); this.permissionCache.set(cacheKey, hasRole); return hasRole; } /** * Check if the context has all of the specified roles * @param roles - Array of roles to check * @returns `true` if the context has all of the roles, otherwise `false` */ hasAllRoles(roles) { const cacheKey = `allRoles:${roles.sort().join(",")}`; if (this.permissionCache.has(cacheKey)) { return this.permissionCache.get(cacheKey); } const hasAllRoles = roles.every((role) => this.isRole(role)); this.permissionCache.set(cacheKey, hasAllRoles); return hasAllRoles; } /** * Enables authentication for the context. */ enable() { this.authStatus = true; } /** * Disables authentication for the context. */ disable() { this.authStatus = false; } /** * Sets a metadata value * @param key - The metadata key * @param value - The metadata value */ setMetadata(key, value) { this.metadata[key] = value; } /** * Gets a metadata value * @param key - The metadata key * @param defaultValue - Default value if metadata doesn't exist * @returns The metadata value or default value */ getMetadata(key, defaultValue) { return this.metadata[key] ?? defaultValue; } /** * Clears the permission cache */ clearCache() { this.permissionCache.clear(); } }; // src/errors/base.ts var DatabaseError = class extends Error { constructor(message, code, error) { super(message); this.code = code; this.name = this.constructor.name; } }; var TransactionNotStartedError = class extends DatabaseError { constructor() { super("Transaction was not started.", "TRANSACTION_NOT_STARTED"); } }; // src/util/helpers.ts function normalizeArr(...args) { if (Array.isArray(args[0])) { return args[0]; } return args; } function containsUndefined(mixed) { let argContainsUndefined = false; if (lodash.isTypedArray(mixed)) return false; if (mixed && lodash.isFunction(mixed.toSQL)) { return argContainsUndefined; } if (Array.isArray(mixed)) { for (let i = 0; i < mixed.length; i++) { if (argContainsUndefined) break; argContainsUndefined = containsUndefined(mixed[i]); } } else if (lodash.isPlainObject(mixed)) { Object.keys(mixed).forEach((key) => { if (!argContainsUndefined) { argContainsUndefined = containsUndefined(mixed[key]); } }); } else { argContainsUndefined = mixed === void 0; } return argContainsUndefined; } function getUndefinedIndices(mixed) { const indices = []; if (Array.isArray(mixed)) { mixed.forEach((item, index) => { if (containsUndefined(item)) { indices.push(index); } }); } else if (lodash.isPlainObject(mixed)) { Object.keys(mixed).forEach((key) => { if (containsUndefined(mixed[key])) { indices.push(key); } }); } else { indices.push(0); } return indices; } function addQueryContext(Target) { Target.prototype.queryContext = function(context) { if (context === void 0) { return this._queryContext; } this._queryContext = context; return this; }; } function getClauseFromArguments(compilerType, bool, first, operator2, second) { if (typeof first === "function") { return { type: "onWrapped", value: first, bool }; } switch (arguments.length) { case 3: return { type: "onRaw", value: first, bool }; case 4: return { type: compilerType, column: first, operator: "=", value: operator2, bool }; default: return { type: compilerType, column: first, operator: operator2, value: second, bool }; } } var JoinClause = class { constructor(table, type, schema) { this.schema = schema; this.table = table; this.joinType = type; this.and = this; this.clauses = []; } get or() { return this._bool("or"); } // Adds an "on" clause to the current join object. on(first) { if (typeof first === "object" && typeof first.toSQL !== "function") { const keys = Object.keys(first); let i = -1; const method = this._bool() === "or" ? "orOn" : "on"; while (++i < keys.length) { this[method](keys[i], first[keys[i]]); } return this; } const data = getClauseFromArguments("onBasic", this._bool(), ...arguments); if (data) { this.clauses.push(data); } return this; } // Adds an "or on" clause to the current join object. orOn(first, operator2, second) { return this._bool("or").on.apply(this, arguments); } onJsonPathEquals(columnFirst, jsonPathFirst, columnSecond, jsonPathSecond) { this.clauses.push({ type: "onJsonPathEquals", columnFirst, jsonPathFirst, columnSecond, jsonPathSecond, bool: this._bool(), not: this._not() }); return this; } orOnJsonPathEquals(columnFirst, jsonPathFirst, columnSecond, jsonPathSecond) { return this._bool("or").onJsonPathEquals.apply(this, arguments); } // Adds a "using" clause to the current join. using(column) { return this.clauses.push({ type: "onUsing", column, bool: this._bool() }); } onVal(first) { if (typeof first === "object" && typeof first.toSQL !== "function") { const keys = Object.keys(first); let i = -1; const method = this._bool() === "or" ? "orOnVal" : "onVal"; while (++i < keys.length) { this[method](keys[i], first[keys[i]]); } return this; } const data = getClauseFromArguments("onVal", this._bool(), ...arguments); if (data) { this.clauses.push(data); } return this; } andOnVal() { return this.onVal(...arguments); } orOnVal() { return this._bool("or").onVal(...arguments); } onBetween(column, values) { assert3__default.default( Array.isArray(values), "The second argument to onBetween must be an array." ); assert3__default.default( values.length === 2, "You must specify 2 values for the onBetween clause" ); this.clauses.push({ type: "onBetween", column, value: values, bool: this._bool(), not: this._not() }); return this; } onNotBetween(column, values) { return this._not(true).onBetween(column, values); } orOnBetween(column, values) { return this._bool("or").onBetween(column, values); } orOnNotBetween(column, values) { return this._bool("or")._not(true).onBetween(column, values); } onIn(column, values) { if (Array.isArray(values) && values.length === 0) return this.on(1, "=", 0); this.clauses.push({ type: "onIn", column, value: values, not: this._not(), bool: this._bool() }); return this; } onNotIn(column, values) { return this._not(true).onIn(column, values); } orOnIn(column, values) { return this._bool("or").onIn(column, values); } orOnNotIn(column, values) { return this._bool("or")._not(true).onIn(column, values); } onNull(column) { this.clauses.push({ type: "onNull", column, not: this._not(), bool: this._bool() }); return this; } orOnNull(callback) { return this._bool("or").onNull(callback); } onNotNull(callback) { return this._not(true).onNull(callback); } orOnNotNull(callback) { return this._not(true)._bool("or").onNull(callback); } onExists(callback) { this.clauses.push({ type: "onExists", value: callback, not: this._not(), bool: this._bool() }); return this; } orOnExists(callback) { return this._bool("or").onExists(callback); } onNotExists(callback) { return this._not(true).onExists(callback); } orOnNotExists(callback) { return this._not(true)._bool("or").onExists(callback); } // Explicitly set the type of join, useful within a function when creating a grouped join. type(type) { this.joinType = type; return this; } _bool(bool) { if (arguments.length === 1) { this._boolFlag = bool; return this; } const ret = this._boolFlag || "and"; this._boolFlag = "and"; return ret; } _not(val) { if (arguments.length === 1) { this._notFlag = val; return this; } const ret = this._notFlag; this._notFlag = false; return ret; } }; Object.assign(JoinClause.prototype, { grouping: "join" }); JoinClause.prototype.andOn = JoinClause.prototype.on; JoinClause.prototype.andOnIn = JoinClause.prototype.onIn; JoinClause.prototype.andOnNotIn = JoinClause.prototype.onNotIn; JoinClause.prototype.andOnNull = JoinClause.prototype.onNull; JoinClause.prototype.andOnNotNull = JoinClause.prototype.onNotNull; JoinClause.prototype.andOnExists = JoinClause.prototype.onExists; JoinClause.prototype.andOnNotExists = JoinClause.prototype.onNotExists; JoinClause.prototype.andOnBetween = JoinClause.prototype.onBetween; JoinClause.prototype.andOnNotBetween = JoinClause.prototype.onNotBetween; JoinClause.prototype.andOnJsonPathEquals = JoinClause.prototype.onJsonPathEquals; var joinclause_default = JoinClause; var Analytic = class { schema; type; method; order; partitions; alias; and; grouping; constructor(method, schema, alias, orderBy, partitions) { this.schema = schema; this.type = "analytic"; this.method = method; this.order = orderBy || []; this.partitions = partitions || []; this.alias = alias; this.and = this; this.grouping = "columns"; } /** * Add columns to partition by */ partitionBy(column, direction2) { assert3__default.default( Array.isArray(column) || typeof column === "string", `The argument to an analytic partitionBy function must be either a string or an array of string.` ); if (Array.isArray(column)) { this.partitions = this.partitions.concat( column.map((col) => ({ column: col })) ); } else { this.partitions.push({ column, order: direction2 }); } return this; } /** * Add columns to order by */ orderBy(column, direction2) { assert3__default.default( Array.isArray(column) || typeof column === "string", `The argument to an analytic orderBy function must be either a string or an array of string.` ); if (Array.isArray(column)) { this.order = this.order.concat(column.map((col) => ({ column: col }))); } else { this.order.push({ column, order: direction2 }); } return this; } }; // src/util/save-async-stack.ts function saveAsyncStack(instance, lines) { if (instance.client.config.asyncStackTraces) { instance._asyncStack = { error: new Error(), lines }; } } // src/util/is.ts function isString(value) { return typeof value === "string"; } function isNumber(value) { return typeof value === "number"; } function isBoolean(value) { return typeof value === "boolean"; } function isUndefined(value) { return typeof value === "undefined"; } function isObject(value) { return typeof value === "object" && value !== null; } function isFunction2(value) { return typeof value === "function"; } // src/query/constants.ts var lockMode = { forShare: "forShare", forUpdate: "forUpdate", forNoKeyUpdate: "forNoKeyUpdate", forKeyShare: "forKeyShare" }; var waitMode = { skipLocked: "skipLocked", noWait: "noWait" }; // src/util/noop.ts function noop() { } // src/util/finally-mixin.ts var finallyMixin = (prototype) => Object.assign(prototype, { finally(onFinally) { return this.then().finally(onFinally); } }); var finally_mixin_default = typeof Promise.prototype.finally === "function" ? finallyMixin : noop; var _debugQuery = Debug__default.default("pg:query"); var debugBindings = Debug__default.default("pg:bindings"); var debugQuery = (sql, txId) => _debugQuery(sql.replace(/%/g, "%%"), txId); function formatQuery(sql, bindings, timeZone = void 0, client) { bindings = bindings == null ? [] : [].concat(bindings); let index = 0; return sql.replace(/\\?\?/g, (match) => { if (match === "\\?") { return "?"; } if (index === bindings.length) { return match; } const value = bindings[index++]; return client._escapeBinding(value, { timeZone }); }); } function enrichQueryObject(connection, queryParam, client) { const queryObject = isString(queryParam) ? { sql: queryParam } : queryParam; queryObject.bindings = client.prepBindings(queryObject.bindings || []); queryObject.sql = client.positionBindings(queryObject.sql); const { __pgUid, __pgTxId } = connection; client.emit("query", Object.assign({ __pgUid, __pgTxId }, queryObject)); debugQuery(queryObject.sql, __pgTxId); debugBindings(queryObject.bindings, __pgTxId); return queryObject; } async function executeQuery(connection, queryObject, client) { try { return await client._query(connection, queryObject); } catch (err) { if (client.config && client.config.compileSqlOnError === false) { err.message = queryObject.sql + " - " + err.message; } else { err.message = formatQuery( queryObject.sql, queryObject.bindings ?? null, void 0, client ) + " - " + err.message; } client.emit( "query-error", err, Object.assign( { __pgUid: connection.__pgUid, __pgTxId: connection.__pgUid }, queryObject ) ); throw err; } } // src/builder-interface-augmenter.ts function augmentWithBuilderInterface(Target) { Target.prototype.toQuery = function(tz) { let data = this.toSQL(this._method, tz); if (!Array.isArray(data)) data = [data]; if (!data.length) { return ""; } return data.map((statement) => { return formatQuery(statement.sql, statement.bindings, tz, this.client); }).reduce( (a, c) => a.concat(a.endsWith(";") ? "\n" : ";\n", c) ); }; Target.prototype.then = function(onFulfilled, onRejected) { let result = this.client.runner(this).run(); if (this.client.config.asyncStackTraces) { result = result.catch((err) => { err.originalStack = err.stack; const firstLine = err.stack?.split("\n")[0]; const { error, lines } = this._asyncStack; const stackByLines = error.stack.split("\n"); const asyncStack = stackByLines.slice(lines); asyncStack.unshift(firstLine); err.stack = asyncStack.join("\n"); throw err; }); } return result.then.apply(result, arguments); }; Target.prototype.options = function(opts) { this._options = this._options || []; this._options.push(lodash.clone(opts) || {}); return this; }; Target.prototype.connection = function(connection) { this._connection = connection; this.client.processPassedConnection(connection); return this; }; Target.prototype.debug = function(enabled) { this._debug = arguments.length ? enabled : true; return this; }; Target.prototype.transacting = function(transaction) { if (transaction && transaction.client) { if (!transaction.client.transacting) { transaction.client.logger.warn( `Invalid transaction value: ${transaction.client}` ); } else { this.client = transaction.client; } } if (lodash.isEmpty(transaction)) { this.client.logger.error( "Invalid value on transacting call, potential bug" ); throw Error( "Invalid transacting value (null, undefined or empty object)" ); } return this; }; Target.prototype.stream = function(options) { return this.client.runner(this).stream(options); }; Target.prototype.pipe = function(writable, options) { return this.client.runner(this).pipe(writable, options); }; Target.prototype.asCallback = function(cb) { const promise = this.then(); util.callbackify(() => promise)(cb); return promise; }; Target.prototype.catch = function(onReject) { return this.then().catch(onReject); }; Object.defineProperty(Target.prototype, Symbol.toStringTag, { get: () => "object" }); finally_mixin_default(Target.prototype); } // src/query/querybuilder.js var SELECT_COMMANDS = /* @__PURE__ */ new Set(["pluck", "first", "select"]); var CLEARABLE_STATEMENTS = /* @__PURE__ */ new Set([ "with", "select", "columns", "hintComments", "where", "union", "join", "group", "order", "having", "limit", "offset", "counter", "counters" ]); var LOCK_MODES = /* @__PURE__ */ new Set([ lockMode.forShare, lockMode.forUpdate, lockMode.forNoKeyUpdate, lockMode.forKeyShare ]); var Builder = class _Builder extends EventEmitter5.EventEmitter { constructor(dataSource, context) { super(); this.client = dataSource; this.context = context; this.and = this; this._single = {}; this._comments = []; this._statements = []; this._method = "select"; if (dataSource.config) { saveAsyncStack(this, 5); this._debug = dataSource.config.debug; } this._joinFlag = "inner"; this._boolFlag = "and"; this._notFlag = false; this._asColumnFlag = false; } toString() { return this.toQuery(); } // Convert the current query "toSQL" toSQL(method, tz) { return this.client.queryCompiler(this).toSQL(method || this._method, tz); } // Create a shallow clone of the current query builder. clone() { const cloned = new this.constructor(this.client, this.context); cloned._method = this._method; cloned._single = clone2__default.default(this._single); cloned._comments = clone2__default.default(this._comments); cloned._statements = clone2__default.default(this._statements); cloned._debug = this._debug; if (this._options !== void 0) { cloned._options = clone2__default.default(this._options); } if (this._queryContext !== void 0) { cloned._queryContext = clone2__default.default(this._queryContext); } if (this._connection !== void 0) { cloned._connection = this._connection; } return cloned; } timeout(ms, { cancel } = {}) { if (isNumber(ms) && ms > 0) { this._timeout = ms; if (cancel) { this.client.assertCanCancelQuery(); this._cancelOnTimeout = true; } } return this; } // With // ------ isValidStatementArg(statement) { return typeof statement === "function" || statement instanceof _Builder || statement && statement.isRawInstance; } _validateWithArgs(alias, statementOrColumnList, nothingOrStatement, method) { const [query, columnList] = typeof nothingOrStatement === "undefined" ? [statementOrColumnList, void 0] : [nothingOrStatement, statementOrColumnList]; if (typeof alias !== "string") { throw new Error(`${method}() first argument must be a string`); } if (this.isValidStatementArg(query) && typeof columnList === "undefined") { return; } const isNonEmptyNameList = Array.isArray(columnList) && columnList.length > 0 && columnList.every((it) => typeof it === "string"); if (!isNonEmptyNameList) { throw new Error( `${method}() second argument must be a statement or non-empty column name list.` ); } if (this.isValidStatementArg(query)) { return; } throw new Error( `${method}() third argument must be a function / QueryBuilder or a raw when its second argument is a column name list` ); } with(alias, statementOrColumnList, nothingOrStatement) { this._validateWithArgs( alias, statementOrColumnList, nothingOrStatement, "with" ); return this.withWrapped(alias, statementOrColumnList, nothingOrStatement); } updateFrom(name) { this._single.updateFrom = name; return this; } using(tables) { this._single.using = tables; return this; } withMaterialized(alias, statementOrColumnList, nothingOrStatement) { this._validateWithArgs( alias, statementOrColumnList, nothingOrStatement, "with" ); return this.withWrapped( alias, statementOrColumnList, nothingOrStatement, true ); } withNotMaterialized(alias, statementOrColumnList, nothingOrStatement) { this._validateWithArgs( alias, statementOrColumnList, nothingOrStatement, "with" ); return this.withWrapped( alias, statementOrColumnList, nothingOrStatement, false ); } // Helper for compiling any advanced `with` queries. withWrapped(alias, statementOrColumnList, nothingOrStatement, materialized) { const [query, columnList] = typeof nothingOrStatement === "undefined" ? [statementOrColumnList, void 0] : [nothingOrStatement, statementOrColumnList]; const statement = { grouping: "with", type: "withWrapped", alias, columnList, value: query }; if (materialized !== void 0) { statement.materialized = materialized; } this._statements.push(statement); return this; } // With Recursive // ------ withRecursive(alias, statementOrColumnList, nothingOrStatement) { this._validateWithArgs( alias, statementOrColumnList, nothingOrStatement, "withRecursive" ); return this.withRecursiveWrapped( alias, statementOrColumnList, nothingOrStatement ); } // Helper for compiling any advanced `withRecursive` queries. withRecursiveWrapped(alias, statementOrColumnList, nothingOrStatement) { this.withWrapped(alias, statementOrColumnList, nothingOrStatement); this._statements[this._statements.length - 1].recursive = true; return this; } // Select // ------ // Adds a column or columns to the list of "columns" // being selected on the query. columns(column) { if (!column && column !== 0) return this; this._statements.push({ grouping: "columns", value: normalizeArr(...arguments) }); return this; } // Adds a comment to the query comment(txt) { if (!isString(txt)) { throw new Error("Comment must be a string"); } const forbiddenChars = ["/*", "*/", "?"]; if (forbiddenChars.some((chars) => txt.includes(chars))) { throw new Error(`Cannot include ${forbiddenChars.join(", ")} in comment`); } this._comments.push({ comment: txt }); return this; } // Allow for a sub-select to be explicitly aliased as a column, // without needing to compile the query in a where. as(column) { this._single.as = column; return this; } // Adds a single hint or an array of hits to the list of "hintComments" on the query. hintComment(hints) { hints = Array.isArray(hints) ? hints : [hints]; if (hints.some((hint) => !isString(hint))) { throw new Error("Hint comment must be a string"); } if (hints.some((hint) => hint.includes("/*") || hint.includes("*/"))) { throw new Error('Hint comment cannot include "/*" or "*/"'); } if (hints.some((hint) => hint.includes("?"))) { throw new Error('Hint comment cannot include "?"'); } this._statements.push({ grouping: "hintComments", value: hints }); return this; } // Prepends the `schemaName` on `tableName` defined by `.table` and `.join`. withSchema(schemaName) { this._single.schema = schemaName; return this; } // Sets the `tableName` on the query. // Alias to "from" for select and "into" for insert statements // e.g. builder.insert({a: value}).into('tableName') // `options`: options object containing keys: // - `only`: whether the query should use SQL's ONLY to not return // inheriting table data. Defaults to false. table(tableName, options = {}) { this._single.table = tableName; this._single.only = options.only === true; return this; } // Adds a `distinct` clause to the query. distinct(...args) { this._statements.push({ grouping: "columns", value: normalizeArr(...args), distinct: true }); return this; } distinctOn(...args) { if (isEmpty2__default.default(args)) { throw new Error("distinctOn requires at least on argument"); } this._statements.push({ grouping: "columns", value: normalizeArr(...args), distinctOn: true }); return this; } // Adds a join clause to the query, allowing for advanced joins // with an anonymous function as the second argument. join(table, first, ...args) { let join; const schema = table instanceof _Builder || typeof table === "function" ? void 0 : this._single.schema; const joinType = this._joinType(); if (typeof first === "function") { join = new joinclause_default(table, joinType, schema); first.call(join, join); } else if (joinType === "raw") { join = new joinclause_default(this.client.raw(table, first), "raw"); } else { join = new joinclause_default(table, joinType, schema); if (first) { join.on(first, ...args); } } this._statements.push(join); return this; } // JOIN blocks: innerJoin(...args) { return this._joinType("inner").join(...args); } leftJoin(...args) { return this._joinType("left").join(...args); } leftOuterJoin(...args) { return this._joinType("left outer").join(...args); } rightJoin(...args) { return this._joinType("right").join(...args); } rightOuterJoin(...args) { return this._joinType("right outer").join(...args); } outerJoin(...args) { return this._joinType("outer").join(...args); } fullOuterJoin(...args) { return this._joinType("full outer").join(...args); } crossJoin(...args) { return this._joinType("cross").join(...args); } joinRaw(...args) { return this._joinType("raw").join(...args); } // Where modifiers: get or() { return this._bool("or"); } get not() { return this._not(true); } // The where function can be used in several ways: // The most basic is `where(key, value)`, which expands to // where key = value. where(column, operator2, value) { const argsLength = arguments.length; if (column === false || column === true) { return this.where(1, "=", column ? 1 : 0); } if (typeof column === "function") { return this.whereWrapped(column); } if (isObject(column) && !column.isRawInstance) return this._objectWhere(column); if (column && column.isRawInstance && argsLength === 1) return this.whereRaw(column); if (argsLength === 2) { value = operator2; operator2 = "="; if (value === null) { return this.whereNull(column); } } const checkOperator = `${operator2}`.toLowerCase().trim(); if (argsLength === 3) { if (checkOperator === "in" || checkOperator === "not in") { return this._not(checkOperator === "not in").whereIn(column, value); } if (checkOperator === "between" || checkOperator === "not between") { return this._not(checkOperator === "not between").whereBetween( column, value ); } } if (value === null) { if (checkOperator === "is" || checkOperator === "is not") { return this._not(checkOperator === "is not").whereNull(column); } } this._statements.push({ grouping: "where", type: "whereBasic", column, operator: operator2, value, not: this._not(), bool: this._bool(), asColumn: this._asColumnFlag }); return this; } whereColumn(...args) { this._asColumnFlag = true; this.where(...args); this._asColumnFlag = false; return this; } // Adds an `or where` clause to the query. orWhere(column, ...args) { this._bool("or"); const obj = column; if (isObject(obj) && !obj.isRawInstance) { return this.whereWrapped(function() { for (const key in obj) { this.andWhere(key, obj[key]); } }); } return this.where(column, ...args); } orWhereColumn(column, ...args) { this._bool("or"); const obj = column; if (isObject(obj) && !obj.isRawInstance) { return this.whereWrapped(function() { for (const key in obj) { this.andWhereColumn(key, "=", obj[key]); } }); } return this.whereColumn(column, ...args); } // Adds an `not where` clause to the query. whereNot(column, ...args) { if (args.length >= 2) { if (args[0] === "in" || args[0] === "between") { this.client.logger.warn( 'whereNot is not suitable for "in" and "between" type subqueries. You should use "not in" and "not between" instead.' ); } } return this._not(true).where(column, ...args); } whereNotColumn(...args) { return this._not(true).whereColumn(...args); } // Adds an `or not where` clause to the query. orWhereNot(...args) { return this._bool("or").whereNot(...args); } orWhereNotColumn(...args) { return this._bool("or").whereNotColumn(...args); } // Processes an object literal provided in a "where" clause. _objectWhere(obj) { const boolVal = this._bool(); const notVal = this._not() ? "Not" : ""; for (const key in obj) { this[boolVal + "Where" + notVal](key, obj[key]); } return this; } // Adds a raw `where` clause to the query. whereRaw(sql, bindings) { const raw = sql.isRawInstance ? sql : this.client.raw(sql, bindings); this._statements.push({ grouping: "where", type: "whereRaw", value: raw, not: this._not(), bool: this._bool() }); return this; } orWhereRaw(sql, bindings) { return this._bool("or").whereRaw(sql, bindings); } // Helper for compiling any advanced `where` queries. whereWrapped(callback) { this._statements.push({ grouping: "where", type: "whereWrapped", value: callback, not: this._not(), bool: this._bool() }); return this; } // Adds a `where exists` clause to the query. whereExists(callback) { this._statements.push({ grouping: "where", type: "whereExists", value: callback, not: this._not(), bool: this._bool() }); return this; } // Adds an `or where exists` clause to the query. orWhereExists(callback) { return this._bool("or").whereExists(callback); } // Adds a `where not exists` clause to the query. whereNotExists(callback) { return this._not(true).whereExists(callback); } // Adds a `or where not exists` clause to the query. orWhereNotExists(callback) { return this._bool("or").whereNotExists(callback); } // Adds a `where in` clause to the query. whereIn(column, values) { if (Array.isArray(values) && isEmpty2__default.default(values)) return this.where(this._not()); this._statements.push({ grouping: "where", type: "whereIn", column, value: values, not: this._not(), bool: this._bool() }); return this; } // Adds a `or where in` clause to the query. orWhereIn(column, values) { return this._bool("or").whereIn(column, values); } // Adds a `where not in` clause to the query. whereNotIn(column, values) { return this._not(true).whereIn(column, values); } // Adds a `or where not in` clause to the query. orWhereNotIn(column, values) { return this._bool("or")._not(true).whereIn(column, values); } // Adds a `where null` clause to the query. whereNull(column) { this._statements.push({ grouping: "where", type: "whereNull", column, not: this._not(), bool: this._bool() }); return this; } // Adds a `or where null` clause to the query. orWhereNull(column) { return this._bool("or").whereNull(column); } // Adds a `where not null` clause to the query. whereNotNull(column) { return this._not(true).whereNull(column); } // Adds a `or where not null` clause to the query. orWhereNotNull(column) { return this._bool("or").whereNotNull(column); } // Adds a `where between` clause to the query. whereBetween(column, values) { assert3__default.default( Array.isArray(values), "The second argument to whereBetween must be an array." ); assert3__default.default( values.length === 2, "You must specify 2 values for the whereBetween clause" ); this._statements.push({ grouping: "where", type: "whereBetween", column, value: values, not: this._not(), bool: this._bool() }); return this; } // Adds a `where not between` clause to the query. whereNotBetween(column, values) { return this._not(true).whereBetween(column, values); } // Adds a `or where between` clause to the query. orWhereBetween(column, values) { return this._bool("or").whereBetween(column, values); } // Adds a `or where not between` clause to the query. orWhereNotBetween(column, values) { return this._bool("or").whereNotBetween(column, values); } _whereLike(type, column, value) { this._statements.push({ grouping: "where", type, column, value, not: this._not(), bool: this._bool(), asColumn: this._asColumnFlag }); return this; } // Adds a `where like` clause to the query. whereLike(column, value) { return this._whereLike("whereLike", column, value); } // Adds a `or where like` clause to the query. orWhereLike(column, value) { return this._bool("or")._whereLike("whereLike", column, value); } // Adds a `where ilike` clause to the query. whereILike(column, value) { return this._whereLike("whereILike", column, value); } // Adds a `or where ilike` clause to the query. orWhereILike(column, value) { return this._bool("or")._whereLike("whereILike", column, value); } // Adds a `group by` clause to the query. groupBy(item) { if (item && item.isRawInstance) { return this.groupByRaw.apply(this, arguments); } this._statements.push({ grouping: "group", type: "groupByBasic", value: normalizeArr(...arguments) }); return this; } // Adds a raw `group by` clause to the query. groupByRaw(sql, bindings) { const raw = sql.isRawInstance ? sql : this.client.raw(sql, bindings); this._statements.push({ grouping: "group", type: "groupByRaw", value: raw }); return this; } // Adds a `order by` clause to the query. orderBy(column, direction2, nulls = "") { if (Array.isArray(column)) { return this._orderByArray(column); } this._statements.push({ grouping: "order", type: "orderByBasic", value: column, direction: direction2, nulls }); return this; } // Adds a `order by` with multiple columns to the query. _orderByArray(columnDefs) { for (let i = 0; i < columnDefs.length; i++) { const columnInfo = columnDefs[i]; if (isObject(columnInfo)) { this._statements.push({ grouping: "order", type: "orderByBasic", value: columnInfo["column"], direction: columnInfo["order"], nulls: columnInfo["nulls"] }); } else if (isString(columnInfo) || isNumber(columnInfo)) { this._statements.push({ grouping: "order", type: "orderByBasic", value: columnInfo }); } } return this; } // Add a raw `order by` clause to the query. orderByRaw(sql, bindings) { const raw = sql.isRawInstance ? sql : this.client.raw(sql, bindings); this._statements.push({ grouping: "order", type: "orderByRaw", value: raw }); return this; } _union(clause, args) { let callbacks = args[0]; let wrap2 = args[1]; if (args.length === 1 || args.length === 2 && isBoolean(wrap2)) { if (!Array.isArray(callbacks)) { callbacks = [callbacks]; } for (let i = 0, l = callbacks.length; i < l; i++) { this._statements.push({ grouping: "union", clause, value: callbacks[i], wrap: wrap2 || false }); } } else { callbacks = toArray__default.default(args).slice(0, args.length - 1); wrap2 = args[args.length - 1]; if (!isBoolean(wrap2)) { callbacks.push(wrap2); wrap2 = false; } this._union(clause, [callbacks, wrap2]); } return this; } // Add a union statement to the query. union(...args) { return this._union("union", args); } // Adds a union all statement to the query. unionAll(...args) { return this._union("union all", args); } intersect(...args) { return this._union("intersect", args); } except(...args) { return this._union("except", args); } // Adds a `having` clause to the query. having(column, operator2, value) { if (column.isRawInstance && arguments.length === 1) { return this.havingRaw(column); } if (typeof column === "function") { return this.havingWrapped(column); } this._statements.push({ grouping: "having", type: "havingBasic", column, operator: operator2, value, bool: this._bool(), not: this._not() }); return this; } orHaving(column, ...args) { this._bool("or"); const obj = column; if (isObject(obj) && !obj.isRawInstance) { return this.havingWrapped(function() { for (const key in obj) { this.andHaving(key, obj[key]); } }); } return this.having(column, ...args); } // Helper for compiling any advanced `having` queries. havingWrapped(callback) { this._statements.push({ grouping: "having", type: "havingWrapped", value: callback, bool: this._bool(), not: this._not() }); return this; } havingNull(column) { this._statements.push({ grouping: "having", type: "havingNull", column, not: this._not(), bool: this._bool() }); return this; } orHavingNull(callback) { return this._bool("or").havingNull(callback); } havingNotNull(callback) { return this._not(true).havingNull(callback); } orHavingNotNull(callback) { return this._not(true)._bool("or").havingNull(callback); } havingExists(callback) { this._statements.push({ grouping: "having", type: "havingExists", value: callback, not: this._not(), bool: this._bool() }); return this; } orHavingExists(callback) { return this._bool("or").havingExists(callback); } havingNotExists(callback) { return this._not(true).havingExists(callback); } orHavingNotExists(callback) { return this._not(true)._bool("or").havingExists(callback); } havingBetween(column, values) { assert3__default.default( Array.isArray(values), "The second argument to havingBetween must be an array." ); assert3__default.default( values.length === 2, "You must specify 2 values for the havingBetween clause" ); this._statements.push({ grouping: "having", type: "havingBetween", column, value: values, not: this._not(), bool: this._bool() }); return this; } orHavingBetween(column, values) { return this._bool("or").havingBetween(column, values); } havingNotBetween(column, values) { return this._not(true).havingBetween(column, values); } orHavingNotBetween(column, values) { return this._not(true)._bool("or").havingBetween(column, values); } havingIn(column, values) { if (Array.isArray(values) && isEmpty2__default.default(values)) return this.where(this._not()); this._statements.push({ grouping: "having", type: "havingIn", column, value: values, not: this._not(), bool: this._bool() }); return this; } // Adds a `or where in` clause to the query. orHavingIn(column, values) { return this._bool("or").havingIn(column, values); } // Adds a `where not in` clause to the query. havingNotIn(column, values) { return this._not(true).havingIn(column, values); } // Adds a `or where not in` clause to the query. orHavingNotIn(column, values) { return this._bool("or")._not(true).havingIn(column, values); } // Adds a raw `having` clause to the query. havingRaw(sql, bindings) { const raw = sql.isRawInstance ? sql : this.client.raw(sql, bindings); this._statements.push({ grouping: "having", type: "havingRaw", value: raw, bool: this._bool(), not: this._not() }); return this; } orHavingRaw(sql, bindings) { return this._bool("or").havingRaw(sql, bindings); } // set the skip binding parameter (= insert the raw value in the query) for an attribute. _setSkipBinding(attribute, options) { let skipBinding = options; if (isObject(options)) { skipBinding = options.skipBinding; } this._single.skipBinding = this._single.skipBinding || {}; this._single.skipBinding[attribute] = skipBinding; } // Only allow a single "offset" to be set for the current query. offset(value, options) { if (value == null || value.isRawInstance || value instanceof _Builder) { this._single.offset = value; } else { const val = parseInt(value, 10); if (isNaN(val)) { this.client.logger.warn("A valid integer must be provided to offset"); } else if (val < 0) { throw new Error(`A non-negative integer must be provided to offset.`); } else { this._single.offset = val; } } this._setSkipBinding("offset", options); return this; } // Only allow a single "limit" to be set for the current query. limit(value, options) { const val = parseInt(value, 10); if (isNaN(val)) { this.client.logger.warn("A valid integer must be provided to limit"); } else { this._single.limit = val; this._setSkipBinding("limit", options); } return this; } // Retrieve the "count" result of the query. count(column, options) { return this._aggregate("count", column || "*", options); } // Retrie