How do I parameterize a Postgres array value in Node / pg-pool - node.js

I am querying an array column (style text[]) using the contains operator #>. My raw query is:
SELECT * FROM songs WHERE style #> '{Acoustic}'
When I drop this into node (using pg-pool) and attempt to parameterize it, the braces surrounding the value reference seem to prevent it from working properly. Note these examples:
const style = 'Acoustic';
// this works, but I don't want to in-line like this:
const { rows } = await pool.query(`SELECT * FROM songs WHERE style #> '{` + style + `}'`);
// this fails with 'error: bind message supplies 1 parameters, but prepared statement "" requires 0'
const { rows } = await pool.query(`SELECT * FROM songs WHERE style #> '{$1}'`, [style]);
What's the proper way to parameterize this?

const { rows } = await pool.query(`SELECT * FROM songs WHERE style #> '{$1}'`, [style]);
The $1 here, being in quotes, is just regarded as part of the string, not as a substitutable variable. If you wish to pass in a scalar and have it treated as an array, you should be able to do it this way:
const { rows } = await pool.query(`SELECT * FROM songs WHERE style #> ARRAY[$1]`, [style]);
An alternative might be to have node.js bind an array directly (not tested):
const { rows } = await pool.query(`SELECT * FROM songs WHERE style #> $1`, [[style]]);

Related

How to change code to prevent SQL injection in typeorm

I am writing code using nestjs and typeorm.
However, the way I write is vulnerable to SQL injection, so I am changing the code.
//Before
.where(`userId = ${userId}`)
//After
.where(`userId = :userId` , {userId:userId})
I am writing a question because I was changing the code and couldn't find a way to change it for a few cases.
//CASE1
const query1 = `select id, 'normal' as type from user where id = ${userId}`;
const query2 = `select id, 'doctor' as type from doctor where id = ${userId}`;
const finalQuery = await getConnection().query(`select id, type from (${query1} union ${query2}) as f limit ${limit} offset ${offset};`);
//CASE2
...
.addSelect(`CASE WHEN userRole = '${userRole}' THEN ...`, 'userType')
...
//CASE3 -> to find search results in order of accuracy
...
.orderBy(`((LENGTH(user.name) - LENGTH((REPLACE(user.name, '${keyword.replace( / /g, '', )}', '')))) / LENGTH('${keyword.replace(/ /g, '')}'))`,
'ASC')
...
//CASE4
let query = 'xxxx';
let whereQuery = '';
for(const i=0;i<5;i++)
whereQuery += ' or ' + `user.name like '%${keyword}%'`
query.where(whereQuery)
I cannot use parameter in the select function.
In the above case, I am wondering how to change it.
Is it ok to not have to modify the select code?

How do we add quotes to an oracle query in node.js

I am new to node.js and it seems like a simple problem but I have a parametrised variable which I need to put in quotes for the value to appear as it is appering in oracle server.
How do I add this?
Here is the nodejs code:
export const orderItemCRQry = async (coil: any) => {
try {
const sql = `SELECT ccl_id_order, ccl_id_order_item FROM v_cold_coil WHERE
ccl_id_coil = :coil_id`;
const binds = [`${coil}`];
return await query(sql, binds);
} catch (error) {
console.log(error);
throw new Error.InternalServerError("Error!");
}
};
This is the query section. I need to put :coil_id in quotes to get a value when I put a static variable like '001300' in quotes it gives the correct value however if I want to put coil id like this ':coil_id' it gives bad request . How do I put quotes there for the parametrised query to work
In oraclesql:
when I run the query:
SELECT ccl_id_order, ccl_id_order_item FROM v_cold_coil WHERE
ccl_id_coil = '001300'
like this it works
and If I remove the quotes I get no value. There fore I need to add quotes to the default 001300 value which I am getting . How do I do that?
You don't need quotes when using bind variables. The following snippet:
const coil = 'abc';
const result = await connection.execute(
`SELECT * FROM DUAL where 'abc' = :coil_id`,
[`${coil}`]);
console.dir(result.rows, { depth: null });
gives:
[ [ 'X' ] ]
showing that the WHERE condition was satisfied.

Passing a JSON array to fulfillmentText

I have an array of JSON objects that looks like this...
JSONarray =
[ { name: 'Steven' },
{ name: 'Clark' },
{ name: 'Kensington' } ]
With respect to dialogflow and google assistant I would need to send a webhook response that looks something like this...
"fulfillmentText": JSONarray}),
"fulfillmentMessages" : [
{
"text":
{"text": [JSONarray]
}
}
]}
The issue I have is this piece "fulfillmentText": JSONarray. 'fulfillmentText' is the part that Google Assistant reads back and ideally I want to have it read back each of the three names but if I just pass the array it fails. Is there away I can build the JSON that will let this happen?
First, a minor semantic point. That is not a "JSON Array". It is an array of JavaScript Objects. JSON refers to the string representation of those objects (JavaScript Object Notation).
This is important because "fulfillmentText" expects a string. You can turn JavaScript objects into a string by using the JSON.stringify() method, so you'd do something like
const text = JSON.stringify( JSONarray );
However, this probably isn't what you want, since the Assistant would read this out as something like
Open bracket open brace quote name quote colon quote Steven quote close bracket, ...
and so forth.
Instead, you probably should write a function that takes the array and turns it into a string. Simplistically, you could call join() on just the values of the names. Something like this:
const names = JSONarray.map( o => o.name ); // Gets just an array of the names
const namesString = names.join(', '); // Joins them into a single string, separated by commas
const text = `The names are: ${namesString}`;
This is better, and what would be read out is something like
The names are Steven, Clark, Kensington
But this isn't how people actually read (or listen to) names. We expect it to be something more like
The names are Steven, Clark, and Kensington
This is so common that multivocal includes a template function to do this based on a list of strings. So your template would just be something like:
"The names are: {{Oxford names}}"
If you're not using multivocal, something like this would work:
/**
* Similar to Array.join(), but using rules for the Oxford comma.
* #param values The array to return joined values for
* #param sep1 The normal separator for each element (defaults to ", ")
* #param sep2 The separator before the final element (defaults to "and ")
* #returns {*}
*/
function oxford( values, sep1=", ", sep2="and " ){
if( !Array.isArray( values ) ){
return values;
} else if( values.length === 0 ){
return '';
} else if( values.length === 1 ){
return values[0];
} else if( values.length === 2 ){
return values[0]+' '+sep2+values[1];
}
var ret = '';
for( var co=0; co<values.length-1; co++ ){
ret += values[co]+sep1;
}
ret += sep2;
ret += values[values.length-1];
return ret;
};
const namesString = oxford( names );
const text = `The names are: ${namesString}`;

How can I sort an object by two unique fields in NodeJS

I need to sort an array of objects by the two fields. I need it to be as least computationally heavy as possible.
This is how the array of objects looks
let arr = [
{
name: 'Liate',
surname: 'Tsek'
},
...
]
This is how I am doing it
let unique = [...new Set(arr.map(item => item.name))]
This only works for the 1st field, how can I get it to work with both?
let seen = new Set()
let uniArr = arr.filter(entry => {
/*
** Create unique key of code and segment
*/
const key = entry.name+ "-" + entry.surname
/*
** Check if the key does not exist in the SET
*/
const keep = !seen.has(key)
/*
** If keep doesn't exist, add it to the set
*/
if (keep)
seen.add(key)
/*
** Return
*/
return keep
})
This works given that the delimiter you use to join the fields will never appear in either fields. Hope it helps

How to access 'Abbreviation' field of a custom list in NetSuite custom lists

I have a custom list that is used as a matrix option of Inventory item. Its 'Color'. This custom list has an abbreviation column. I am creating a saved search on item and using Color field(join) and trying to access 'Abbreviation' field of color.
Abbreviation on custom list is available on when 'Matrix Option List' is checked.
Can someone please help me achieve this? I tried to do this through script and it seems like we cannot access 'Abbreviation' column through script. I also tried to use script to write a search directly on 'Color' - custom list and get the 'abbreviation' through search columns. It did not work. Is there a way to access 'Abbreviation' from custom lists?
Thanks in Advance
You can access it via suitescript by using the record type "customlist" and the internal id of the list like so:
var rec = nlapiLoadRecord('customlist', 5);
var abbreviation = rec.getLineItemValue('customvalue', 'abbreviation', 1);
nlapiLogExecution('DEBUG', 'abbreviation', abbreviation);
Keep in mind that the third argument of getLineItemValue is the line number, not the internal ID of the item in the list. If you want to find a specifc line item, you may want to use rec.findLineItemValue(group, fldnam, value).
Unfortunately, it doesn't look like this translates to saved searches. The suiteanswer at https://netsuite.custhelp.com/app/answers/detail/a_id/10653 has the following code:
var col = new Array();
col[0] = new nlobjSearchColumn('name');
col[1] = new nlobjSearchColumn('internalid');
var results = nlapiSearchRecord('customlist25', null, null, col);
for ( var i = 0; results != null && i < results.length; i++ )
{
var res = results[i];
var listValue = (res.getValue('name'));
var listID = (res.getValue('internalid'));
nlapiLogExecution('DEBUG', (listValue + ", " + listID));
}
However, whatever part of the application layer translates this into a query doesn't handle the abbreviation field. One thing to keep in mind is that the 'custom list' record is basically a header record, and each individual entry is it's own record that ties to it. You can see some of the underlying structure here, but the takeaway is that you'd need some way to drill-down into the list entries, and the saved search interface doesn't really support it.
I could be wrong, but I don't think there's any way to get it to execute in a saved search as-is. I thought the first part of my answer might help you find a workaround though.
Here is a NetSuite SuiteScript 2.0 search I use to find the internalId for a given abbreviation in a custom list.
/**
* look up the internal id value for an abbreviation in a custom list
* #param string custom_list_name
* #param string abbreviation
* #return int
* */
function lookupNetsuiteCustomListInternalId( custom_list_name, abbreviation ){
var internal_id = -1;
var custom_list_search = search.create({
type: custom_list_name,
columns: [ { name:'internalId' }, { name:'abbreviation' } ]
});
var filters = [];
filters.push(
search.createFilter({
name: 'formulatext',
formula: "{abbreviation}",
operator: search.Operator.IS,
values: abbreviation
})
);
custom_list_search.filters = filters;
var result_set = custom_list_search.run();
var results = result_set.getRange( { start:0, end:1 } );
for( var i in results ){
log.debug( 'found custom list record', results[i] );
internal_id = results[i].getValue( { name:'internalId' } );
}
return internal_id;
}
Currently NetSuite does not allows using join on matrix option field. But as you mentioned, you can use an extra search to get the result, you could first fetch color id from item and then use search.lookupFields as follows
search.lookupFields({ type: MATRIX_COLOR_LIST_ID, id: COLOR_ID, columns: ['abbreviation'] });
Note: Once you have internalid of the color its better to use search.lookupFields rather than creating a new search with search.create.

Resources