Skip to content

Commit

Permalink
Merge 416d6af into 40a3ace
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaly-t committed Sep 23, 2019
2 parents 40a3ace + 416d6af commit ba49228
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 60 deletions.
83 changes: 52 additions & 31 deletions lib/formatting.js
Expand Up @@ -49,16 +49,16 @@ const maxVariable = 100000; // maximum supported variable is '$100000'

////////////////////////////////////////////////////
// Converts a single value into its Postgres format.
function formatValue(value, fm, cc) {
function formatValue({value, fm, cc, options}) {

if (typeof value === 'function') {
return formatValue(resolveFunc(value, cc), fm, cc);
return formatValue({value: resolveFunc(value, cc), fm, cc});
}

const ctf = getCTF(value); // Custom Type Formatting
if (ctf) {
fm |= ctf.rawType ? fmFlags.raw : 0;
return formatValue(resolveFunc(ctf.toPostgres, value), fm, cc);
return formatValue({value: resolveFunc(ctf.toPostgres, value), fm, cc});
}

const isRaw = !!(fm & fmFlags.raw);
Expand All @@ -72,7 +72,7 @@ function formatValue(value, fm, cc) {
case fmFlags.json:
return $as.json(value, isRaw);
case fmFlags.csv:
return $as.csv(value);
return $to.csv(value, options);
case fmFlags.value:
return $as.value(value);
default:
Expand All @@ -98,7 +98,7 @@ function formatValue(value, fm, cc) {
return $to.date(value, isRaw);
}
if (value instanceof Array) {
return $to.array(value);
return $to.array(value, options);
}
if (value instanceof Buffer) {
return $to.buffer(value, isRaw);
Expand All @@ -115,21 +115,25 @@ function formatValue(value, fm, cc) {
//
// Top-level empty arrays are formatted as literal '{}' to avoid the necessity of explicit type casting,
// as the server cannot automatically infer type of an empty non-literal array.
function formatArray(array) {
const loop = a => '[' + a.map(v => v instanceof Array ? loop(v) : formatValue(v)).join() + ']';
return array.length ? ('array' + loop(array)) : '\'{}\'';
function formatArray(array, options) {
const loop = a => '[' + a.map(value => value instanceof Array ? loop(value) : formatValue({
value,
options
})).join() + ']';
const prefix = options && options.capSQL ? 'ARRAY' : 'array';
return array.length ? (prefix + loop(array)) : '\'{}\'';
}

///////////////////////////////////////////////////////////////////
// Formats array/object/value as a list of comma-separated values.
function formatCSV(values) {
function formatCSV(values, options) {
if (values instanceof Array) {
return values.map(v => formatValue(v)).join();
return values.map(value => formatValue({value, options})).join();
}
if (typeof values === 'object' && values !== null) {
return Object.keys(values).map(v => formatValue(values[v])).join();
return Object.keys(values).map(v => formatValue({value: values[v], options})).join();
}
return values === undefined ? '' : formatValue(values);
return values === undefined ? '' : formatValue({value: values, options});
}

///////////////////////////////
Expand All @@ -145,14 +149,14 @@ const formatAs = {
throw new Error('Invalid property name \'' + v.name + '\'.');
}
if (c.has) {
return formatValue(c.value, v.fm, c.target);
return formatValue({value: c.value, fm: v.fm, cc: c.target, options});
}
if (v.name === 'this') {
return formatValue(obj, v.fm);
return formatValue({value: obj, fm: v.fm, options});
}
if ('def' in options) {
const d = options.def, value = typeof d === 'function' ? d.call(obj, v.name, obj) : d;
return formatValue(value, v.fm, obj);
return formatValue({value, fm: v.fm, cc: obj, options});
}
if (options.partial) {
return name;
Expand All @@ -171,11 +175,11 @@ const formatAs = {
throw new RangeError('Variable $' + v.name + ' exceeds supported maximum of $' + maxVariable);
}
if (idx < array.length) {
return formatValue(array[idx], v.fm);
return formatValue({value: array[idx], fm: v.fm, options});
}
if ('def' in options) {
const d = options.def, value = typeof d === 'function' ? d.call(array, idx, array) : d;
return formatValue(value, v.fm);
return formatValue({value, fm: v.fm, options});
}
if (options.partial) {
return name;
Expand All @@ -184,10 +188,10 @@ const formatAs = {
});
},

value(query, value, raw) {
value(query, value, raw, options) {
return query.replace(npm.patterns.singleValue, name => {
const v = formatAs.stripName(name, raw);
return formatValue(value, v.fm);
return formatValue({value, fm: v.fm, options});
});
},

Expand Down Expand Up @@ -296,14 +300,14 @@ function formatQuery(query, values, raw, options) {
}
}
// $1 formatting to be applied, if values != undefined;
return values === undefined ? query : formatAs.value(query, values, raw);
return values === undefined ? query : formatAs.value(query, values, raw, options);
}

//////////////////////////////////////////////////////
// Formats a standard PostgreSQL function call query;
function formatFunction(funcName, values, capSQL) {
const sql = capSQL ? 'SELECT * FROM ' : 'select * from ';
return sql + funcName + '(' + formatCSV(values) + ')';
function formatFunction(funcName, values, options) {
const sql = options && options.capSQL ? 'SELECT * FROM ' : 'select * from ';
return sql + funcName + '(' + formatCSV(values, options) + ')';
}

function formatSqlName(name) {
Expand Down Expand Up @@ -552,7 +556,7 @@ const $as = {
if (isNull(value)) {
throw new TypeError('Open values cannot be null or undefined.');
}
return safeText(formatValue(value, fmFlags.raw));
return safeText(formatValue({value, fm: fmFlags.raw}));
},

/**
Expand Down Expand Up @@ -659,15 +663,24 @@ const $as = {
* @param {Array|function} arr
* Array to be converted, or a function that returns one.
*
* @param {object} [options]
* **Added in v9.2.0**
*
* Array-Formatting Options.
*
* @param {boolean} [options.capSQL=false]
* When `true`, outputs `ARRAY` instead of `array`.
*
* @returns {string}
*/
array(arr) {
array(arr, options) {
options = assertOptions(options, ['capSQL']);
arr = resolveFunc(arr);
if (isNull(arr)) {
return 'null';
}
if (arr instanceof Array) {
return $to.array(arr);
return $to.array(arr, options);
}
throw new TypeError(wrapText(arr) + ' is not an Array object.');
},
Expand All @@ -690,7 +703,7 @@ const $as = {
* @see {@link formatting.format format}
*/
csv(values) {
return formatCSV(resolveFunc(values));
return $to.csv(values);
},

/**
Expand Down Expand Up @@ -746,7 +759,7 @@ const $as = {
throw new TypeError(wrapText(func) + ' is not a function.');
}
const fm = raw ? fmFlags.raw : null;
return formatValue(resolveFunc(func, cc), fm, cc);
return formatValue({value: resolveFunc(func, cc), fm, cc});
},

/**
Expand Down Expand Up @@ -797,6 +810,11 @@ const $as = {
* @param {object} [options]
* Formatting Options.
*
* @param {boolean} [options.capSQL=false]
* **Added in v9.2.0**
*
* Formats reserved SQL words capitalized. Presently, this only concerns arrays, to output `ARRAY` when required.
*
* @param {boolean} [options.partial=false]
* Indicates that we intend to do only a partial replacement, i.e. throw no error when encountering a variable or
* property name that's missing within the formatting parameters.
Expand Down Expand Up @@ -826,7 +844,7 @@ const $as = {
* The function will throw an error, if any occurs during formatting.
*/
format(query, values, options) {
options = assertOptions(options, ['partial', 'def']);
options = assertOptions(options, ['capSQL', 'partial', 'def']);
const ctf = getCTF(query);
if (ctf) {
query = ctf.toPostgres.call(query, query);
Expand All @@ -837,8 +855,11 @@ const $as = {

/* Pre-parsed type formatting */
const $to = {
array(arr) {
return formatArray(arr);
array(arr, options) {
return formatArray(arr, options);
},
csv(values, options) {
return formatCSV(resolveFunc(values), options);
},
bool(value) {
return value ? 'true' : 'false';
Expand Down
23 changes: 14 additions & 9 deletions lib/helpers/index.js
Expand Up @@ -47,18 +47,23 @@ const utils = require('../utils');
* {@link helpers.concat concat} static method.
*/
module.exports = config => {
const capSQL = () => config.options && config.options.capSQL;
const res = {
insert: (data, columns, table) => {
const capSQL = config.options && config.options.capSQL;
return method.insert(data, columns, table, capSQL);
insert(data, columns, table) {
return method.insert(data, columns, table, capSQL());
},
update: (data, columns, table, options) => {
const capSQL = config.options && config.options.capSQL;
return method.update(data, columns, table, options, capSQL);
update(data, columns, table, options) {
return method.update(data, columns, table, options, capSQL());
},
concat(queries) {
return method.concat(queries, capSQL());
},
values(data, columns) {
return method.values(data, columns, capSQL());
},
sets(data, columns) {
return method.sets(data, columns, capSQL());
},
concat: method.concat,
values: method.values,
sets: method.sets,
TableName,
ColumnSet,
Column
Expand Down
7 changes: 5 additions & 2 deletions lib/helpers/methods/concat.js
Expand Up @@ -48,10 +48,11 @@ const npm = {
*
* // query = concatenated string with all the queries
*/
function concat(queries) {
function concat(queries, capSQL) {
if (!Array.isArray(queries)) {
throw new TypeError('Parameter \'queries\' must be an array.');
}
const fmOptions = {capSQL};
const all = queries.map((q, index) => {
if (typeof q === 'string') {
// a simple query string without parameters:
Expand All @@ -64,7 +65,9 @@ function concat(queries) {
}
if ('query' in q) {
// object {query, values, options}:
return clean(npm.formatting.as.format(q.query, q.values, q.options));
let opt = q.options && typeof q.options === 'object' ? q.options : {};
opt = opt.capSQL === undefined ? Object.assign(opt, fmOptions) : opt;
return clean(npm.formatting.as.format(q.query, q.values, opt));
}
}
throw new Error('Invalid query element at index ' + index + '.');
Expand Down
7 changes: 4 additions & 3 deletions lib/helpers/methods/insert.js
Expand Up @@ -124,19 +124,20 @@ function insert(data, columns, table, capSQL) {
}

let query = capSQL ? sql.capCase : sql.lowCase;
const fmOptions = {capSQL};

const format = npm.formatting.as.format;
query = format(query, [table.name, columns.names]);
query = format(query, [table.name, columns.names], fmOptions);

if (isArray) {
return query + data.map((d, index) => {
if (!d || typeof d !== 'object') {
throw new Error('Invalid insert object at index ' + index + '.');
}
return '(' + format(columns.variables, columns.prepare(d)) + ')';
return '(' + format(columns.variables, columns.prepare(d), fmOptions) + ')';
}).join();
}
return query + '(' + format(columns.variables, columns.prepare(data)) + ')';
return query + '(' + format(columns.variables, columns.prepare(data), fmOptions) + ')';
}

const sql = {
Expand Down
4 changes: 2 additions & 2 deletions lib/helpers/methods/sets.js
Expand Up @@ -64,7 +64,7 @@ const npm = {
* //=> "val"=123,"msg"='hello'
*
*/
function sets(data, columns) {
function sets(data, columns, capSQL) {

if (!data || typeof data !== 'object' || Array.isArray(data)) {
throw new TypeError('Invalid parameter \'data\' specified.');
Expand All @@ -74,7 +74,7 @@ function sets(data, columns) {
columns = new ColumnSet(columns || data);
}

return npm.format(columns.assign({source: data}), columns.prepare(data));
return npm.format(columns.assign({source: data}), columns.prepare(data), {capSQL});
}

module.exports = {sets};
9 changes: 5 additions & 4 deletions lib/helpers/methods/update.js
Expand Up @@ -168,7 +168,8 @@ function update(data, columns, table, options, capSQL) {
options = assertOptions(options, ['tableAlias', 'valueAlias', 'emptyUpdate']);

const format = npm.formatting.as.format,
useEmptyUpdate = 'emptyUpdate' in options;
useEmptyUpdate = 'emptyUpdate' in options,
fmOptions = {capSQL};

if (isArray) {
const tableAlias = npm.formatting.as.alias(npm.utils.isNull(options.tableAlias) ? 't' : options.tableAlias);
Expand All @@ -190,10 +191,10 @@ function update(data, columns, table, options, capSQL) {
if (!d || typeof d !== 'object') {
throw new Error('Invalid update object at index ' + index + '.');
}
return '(' + format(columns.variables, columns.prepare(d)) + ')';
return '(' + format(columns.variables, columns.prepare(d), fmOptions) + ')';
}).join();

return format(q, [table.name, tableAlias, targetCols, values, valueAlias, columns.names]);
return format(q, [table.name, tableAlias, targetCols, values, valueAlias, columns.names], fmOptions);
}

const updates = columns.assign({source: data});
Expand All @@ -206,7 +207,7 @@ function update(data, columns, table, options, capSQL) {

const query = capSQL ? sql.single.capCase : sql.single.lowCase;

return format(query, table.name) + format(updates, columns.prepare(data));
return format(query, table.name) + format(updates, columns.prepare(data), fmOptions);

function checkTable() {
if (table && !(table instanceof TableName)) {
Expand Down
9 changes: 5 additions & 4 deletions lib/helpers/methods/values.js
Expand Up @@ -79,7 +79,7 @@ const npm = {
* //=> (123,'hello'),(456,'world!')
*
*/
function values(data, columns) {
function values(data, columns, capSQL) {

if (!data || typeof data !== 'object') {
throw new TypeError('Invalid parameter \'data\' specified.');
Expand All @@ -98,17 +98,18 @@ function values(data, columns) {
throw new Error('Cannot generate values without any columns.');
}

const format = npm.formatting.as.format;
const format = npm.formatting.as.format,
fmOptions = {capSQL};

if (isArray) {
return data.map((d, index) => {
if (!d || typeof d !== 'object') {
throw new Error('Invalid object at index ' + index + '.');
}
return '(' + format(columns.variables, columns.prepare(d)) + ')';
return '(' + format(columns.variables, columns.prepare(d), fmOptions) + ')';
}).join();
}
return '(' + format(columns.variables, columns.prepare(data)) + ')';
return '(' + format(columns.variables, columns.prepare(data), fmOptions) + ')';
}

module.exports = {values};
2 changes: 1 addition & 1 deletion lib/query.js
Expand Up @@ -123,7 +123,7 @@ function $query(ctx, query, values, qrm, config) {
// use 'pg-promise' implementation of values formatting;
if (isFunc) {
params = undefined;
query = npm.formatting.formatFunction(query, values, capSQL);
query = npm.formatting.formatFunction(query, values, {capSQL});
} else {
query = npm.formatting.formatQuery(query, values);
}
Expand Down

0 comments on commit ba49228

Please sign in to comment.