/*
* Copyright (c) 2015-present, Vitaly Tomilov
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Removal or modification of this copyright notice is prohibited.
*/
const {ServerFormatting} = require('./server-formatting');
const {ParameterizedQueryError} = require('../errors');
const {QueryFile} = require('../query-file');
const {assert} = require('../assert');
const npm = {
EOL: require('os').EOL,
utils: require('../utils')
};
/**
* @class ParameterizedQuery
* @description
* Constructs a new {@link ParameterizedQuery} object. All properties can also be set after the object's construction.
*
* This type extends the basic `{text, values}` object, i.e. when the basic object is used with a query method,
* a new {@link ParameterizedQuery} object is created in its place.
*
* The type can be used in place of the `query` parameter, with any query method directly.
*
* The type is available from the library's root: `pgp.ParameterizedQuery`.
*
* @param {string|QueryFile|Object} [options]
* Object configuration options / properties.
*
* @param {string|QueryFile} [options.text] - See property {@link ParameterizedQuery#text text}.
* @param {array} [options.values] - See property {@link ParameterizedQuery#values values}.
* @param {boolean} [options.binary] - See property {@link ParameterizedQuery#binary binary}.
* @param {string} [options.rowMode] - See property {@link ParameterizedQuery#rowMode rowMode}.
* @param {ITypes} [options.types] - See property {@link ParameterizedQuery#types types}.
*
* @returns {ParameterizedQuery}
*
* @see
* {@link errors.ParameterizedQueryError ParameterizedQueryError}
*
* @example
*
* const {ParameterizedQuery: PQ} = require('pg-promise');
*
* // Creating a complete Parameterized Query with parameters:
* const findUser = new PQ({text: 'SELECT * FROM Users WHERE id = $1', values: [123]});
*
* db.one(findUser)
* .then(user => {
* // user found;
* })
* .catch(error => {
* // error;
* });
*
* @example
*
* const {ParameterizedQuery: PQ} = require('pg-promise');
*
* // Creating a reusable Parameterized Query without values:
* const addUser = new PQ('INSERT INTO Users(name, age) VALUES($1, $2)');
*
* // setting values explicitly:
* addUser.values = ['John', 30];
*
* db.none(addUser)
* .then(() => {
* // user added;
* })
* .catch(error=> {
* // error;
* });
*
* // setting values implicitly, by passing them into the query method:
* db.none(addUser, ['Mike', 25])
* .then(() => {
* // user added;
* })
* .catch(error=> {
* // error;
* });
*/
class ParameterizedQuery extends ServerFormatting {
constructor(options) {
if (typeof options === 'string' || options instanceof QueryFile) {
options = {
text: options
};
} else {
options = assert(options, ['text', 'values', 'binary', 'rowMode', 'types']);
}
super(options);
}
}
/**
* @method ParameterizedQuery#parse
* @description
* Parses the current object and returns a simple `{text, values}`, if successful,
* or else it returns a {@link errors.ParameterizedQueryError ParameterizedQueryError} object.
*
* This method is primarily for internal use by the library.
*
* @returns {{text, values}|errors.ParameterizedQueryError}
*/
ParameterizedQuery.prototype.parse = function () {
const _i = this._inner, options = _i.options;
const qf = options.text instanceof QueryFile ? options.text : null;
if (!_i.changed && !qf) {
return _i.target;
}
const errors = [], values = _i.target.values;
_i.target = {
text: options.text
};
_i.changed = true;
_i.currentError = undefined;
if (qf) {
qf.prepare();
if (qf.error) {
errors.push(qf.error);
} else {
_i.target.text = qf[QueryFile.$query];
}
}
if (!npm.utils.isText(_i.target.text)) {
errors.push('Property \'text\' must be a non-empty text string.');
}
if (!npm.utils.isNull(values)) {
_i.target.values = values;
}
if (options.binary !== undefined) {
_i.target.binary = !!options.binary;
}
if (options.rowMode !== undefined) {
_i.target.rowMode = options.rowMode;
}
if (options.types !== undefined) {
_i.target.types = options.types;
}
if (errors.length) {
return _i.currentError = new ParameterizedQueryError(errors[0], _i.target);
}
_i.changed = false;
return _i.target;
};
/**
* @method ParameterizedQuery#toString
* @description
* Creates a well-formatted multi-line string that represents the object's current state.
*
* It is called automatically when writing the object into the console.
*
* @param {number} [level=0]
* Nested output level, to provide visual offset.
*
* @returns {string}
*/
ParameterizedQuery.prototype.toString = function (level) {
level = level > 0 ? parseInt(level) : 0;
const gap = npm.utils.messageGap(level + 1);
const pq = this.parse();
const lines = [
'ParameterizedQuery {'
];
if (npm.utils.isText(pq.text)) {
lines.push(gap + 'text: "' + pq.text + '"');
}
if (this.values !== undefined) {
lines.push(gap + 'values: ' + npm.utils.toJson(this.values));
}
if (this.binary !== undefined) {
lines.push(gap + 'binary: ' + npm.utils.toJson(this.binary));
}
if (this.rowMode !== undefined) {
lines.push(gap + 'rowMode: ' + npm.utils.toJson(this.rowMode));
}
if (this.error !== undefined) {
lines.push(gap + 'error: ' + this.error.toString(level + 1));
}
lines.push(npm.utils.messageGap(level) + '}');
return lines.join(npm.EOL);
};
module.exports = {ParameterizedQuery};
/**
* @name ParameterizedQuery#text
* @type {string|QueryFile}
* @description
* A non-empty query string or a {@link QueryFile} object.
*
* Only the basic variables (`$1`, `$2`, etc) can be used in the query, because _Parameterized Queries_
* are formatted on the server side.
*/
/**
* @name ParameterizedQuery#values
* @type {array}
* @description
* Query formatting parameters, depending on the type:
*
* - `null` / `undefined` means the query has no formatting parameters
* - `Array` - it is an array of formatting parameters
* - None of the above, means it is a single formatting value, which
* is then automatically wrapped into an array
*/
/**
* @name ParameterizedQuery#binary
* @type {boolean}
* @default undefined
* @description
* Activates binary result mode. The default is the text mode.
*
* @see {@link http://www.postgresql.org/docs/devel/static/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY Extended Query}
*/
/**
* @name ParameterizedQuery#rowMode
* @type {string}
* @default undefined
* @description
* Changes the way data arrives to the client, with only one value supported by $[pg]:
* - `array` will make all data rows arrive as arrays of values. By default, rows arrive as objects.
*/
/**
* @name ParameterizedQuery#types
* @type {ITypes}
* @default undefined
* @description
* Custom type parsers just for this query result.
*/
/**
* @name ParameterizedQuery#error
* @type {errors.ParameterizedQueryError}
* @default undefined
* @readonly
* @description
* When in an error state, it is set to a {@link errors.ParameterizedQueryError ParameterizedQueryError} object. Otherwise, it is `undefined`.
*
* This property is primarily for internal use by the library.
*/