@nuvix/pg
Version:
PostgreSQL integration for Nuvix
1,728 lines (1,711 loc) • 169 kB
JavaScript
'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