SQL tedious add array as parameter - sql

I'm running this SQL query with tedious.js using parameters:
var query = "select * from table_name where id in (#ids)";
request = new sql.Request(query, function(err, rowCount) {
if (err) {
}
});
request.on('row', function(columns) {
});
var id = [1, 2, 3];
request.addParameters('ids', TYPES.Int, id);
connection.execSql(request);
because I am looking for items that matches the ID provided with where ... in ... clause, I need to pass in an array. However, there is no TYPES.Array. How do I this properly?

for this query, i think you'll just have to manually build the entire sql string. the TYPES enum values are for the datatypes in the database, not in your JavaScript code.

//you can like this:
var userIds = result.map(function (el) {
return el.UserId;
}).join(',');
var params = [{
name: 'userIds',
type: TYPES.VarChar,
value: userIds,
options: null}];
var querySql = ['SELECT COUNT([MomentId]) FROM [T_Moment]',
'WHERE [RecordStatus] = ', sysConst.recordStatus.activation, " AND CHARINDEX(','+RTRIM([UserId])+',' , ','+ #userIds +',')>0 "].join(' ');
dbHelper.count(querySql, params, function (err, result) {
if (err) {
callback('error--');
} else {
callback(null, result);
}
});

Try creating the in clause parameters for query dynamically.
// create connection
let ids = [1, 2, 3];
let inClauseParamters = createInClauseParameters();
let query = `select * from table_name where id in (${inClauseParamters})`;
let request = new Request(query, (err, rowCount) => {
if (err) { /* handle error */ }
});
request.on('row', (columns) => { /* get row */});
request = addRequestParameters(ids, request);
connection.execSql(request);
function createInClauseParameters(values) {
return values.map((val, index) => `#Value${index}`).join(',');
}
function addRequestParameters(values, request) {
values.forEach((val, index) => {
request.addParameter(`Value${index}`, TYPES.VarChar, val);
});
return request;
}

Related

Node.JS Oracle Patch Request not dynamic

I'm trying to dynamically make a patch request for oracle tables through Node.JS
Here's my setup:
In my router.js file I have this:
const express = require('express');
const router = new express.Router();
const employees = require('../controllers/employees.js');
const smiCats = require('../controllers/smi/smiCats.js');
const auth = require('../controllers/auth.js');
router.route('/login/:id?')
.post(auth.getToken);
router.route('/ams/:id?')
.get(auth.verifyToken, employees.get)
.post(auth.verifyToken, employees.post)
.put(auth.verifyToken, employees.put)
.delete(auth.verifyToken, employees.delete)
.patch(auth.verifyToken, employees.patch);
router.route('/smi/cats/:id?')
.get(auth.verifyToken, smiCats.get)
.post(auth.verifyToken, smiCats.post)
.put(auth.verifyToken, smiCats.put)
.patch(auth.verifyToken, smiCats.patch);
module.exports = router;
That then calls my controller that has my patch function & gets sanitized.
//sanitizer
function sanitizeCats(req) {
const cats = {
cat_desc: req.body.cat_desc,
msg_for: req.body.msg_for,
msg_user_owner: req.body.msg_user_owner || 0,
msg_realtor_owner: req.body.msg_realtor_owner || 0
};
return cats;
}
async function patch(req, res, next) {
try {
let category = sanitizeCats(req);
category.cat_id = parseInt(req.params.id, 10);
const success = await smiCats.patch(category);
if (success) {
res.status(204).end();
} else {
res.status(404).end();
}
} catch (err) {
next(err);
}
}
module.exports.patch = patch;
When that gets executed it calls my db_api module, which assembles the sql statement
(THE NEXT CODE SECTION IS WHERE MY QUESTION COMES FROM)
const database = require('../../services/database.js');
const oracledb = require('oracledb');
const patchSql =
`BEGIN
DECLARE
BEGIN
IF nvl(:cat_desc,'zzz') != 'zzz' THEN
UPDATE smi_contact_cats
SET cat_desc = :cat_desc
WHERE cat_id = :cat_id;
END IF;
IF nvl(:msg_for,'zzz') != 'zzz' THEN
UPDATE smi_contact_cats
SET msg_for = :msg_for
WHERE cat_id = :cat_id;
END IF;
IF nvl(:msg_user_owner,-1) > -1 THEN
UPDATE smi_contact_cats
SET msg_user_owner = :msg_user_owner
WHERE cat_id = :cat_id;
END IF;
IF nvl(:msg_realtor_owner,-1) > -1 THEN
UPDATE smi_contact_cats
SET msg_realtor_owner = :msg_realtor_owner
WHERE cat_id = :cat_id;
END IF;
:rowcount := sql%rowcount;
END;
END;`;
async function patch(cats) {
const category = Object.assign({}, cats);
//add binds
category.rowcount = {
dir: oracledb.BIND_OUT,
type: oracledb.NUMBER
};
const result = await database.simpleExecute(patchSql, category);
return result.outBinds.rowcount === 1;
}
module.exports.patch = patch;
This then calls the database function to actually execute & assemble the sql with the bind variables:
const oracledb = require('oracledb');
const dbConfig = require('../config/database.js');
async function initialize() {
const pool = await oracledb.createPool(dbConfig.beta);
}
module.exports.initialize = initialize;
async function close() {
await oracledb.getPool().close();
}
module.exports.close = close;
function simpleExecute(statement, binds = [], opts = {}) {
return new Promise(async (resolve, reject) => {
let conn;
opts.outFormat = oracledb.OBJECT;
opts.autoCommit = true;
try {
conn = await oracledb.getConnection();
const result = await conn.execute(statement, binds, opts);
resolve(result);
} catch (err) {
reject(err);
} finally {
if (conn) { // conn assignment worked, need to close
try {
await conn.close();
} catch (err) {
console.log(err);
}
}
}
});
}
module.exports.simpleExecute = simpleExecute;
So all of this works... but it's not dynamic enough for me to build our company api. How do I make a more dynamic patch request in Node.JS without having to type out every single column & put an nvl around it to check if it's there. As a side not if there's a better way to dynamically sanitize as well, I'm all ears, but the main question is on how to dynamically build the patch request better.
The current code is suboptimal in that is does one update per property. Here's a more dynamic solution...
Given the following:
create table smi_contact_cats (
cat_id number,
cat_desc varchar2(50),
msg_for varchar2(50),
msg_user_owner varchar2(50),
msg_realtor_owner varchar2(50)
);
insert into smi_contact_cats (
cat_id,
cat_desc,
msg_for,
msg_user_owner,
msg_realtor_owner
) values (
1,
'cat_desc orginal value',
'msg_for orginal value',
'msg_user_owner orginal value',
'msg_realtor_owner orginal value'
);
commit;
You can use logic like this. updatableColumns is the whitelist of columns that can be updated. Note that you can comment and uncomment some of the lines toward the bottom to test various input.
const oracledb = require('oracledb');
const config = require('./db-config.js');
async function patch(cat) {
let conn;
try {
const category = Object.assign({}, cat);
const categoryProps = Object.getOwnPropertyNames(category);
const updatableColumns = ['cat_desc', 'msg_for', 'msg_user_owner'];
// Validate that the pk was passed in
if (!categoryProps.includes('cat_id')) {
throw new Error('cat_id is required');
}
// Now remove the pk col from categoryProps
categoryProps.splice(categoryProps.indexOf('cat_id'), 1);
if (categoryProps.length === 0) {
throw new Error('At least one property must be specified');
}
let sql = 'update smi_contact_cats\nset ';
for (let propIdx = 0; propIdx < categoryProps.length; propIdx++) {
// Here's the whitelist check
if (!updatableColumns.includes(categoryProps[propIdx])) {
throw new Error('Invalid "update" column');
} else {
if (propIdx > 0 && propIdx < categoryProps.length) {
sql += ',\n ';
}
sql += categoryProps[propIdx] + ' = :' + categoryProps[propIdx];
}
}
sql += '\nwhere cat_id = :cat_id';
console.log('here is the sql', sql);
conn = await oracledb.getConnection(config);
const result = await conn.execute(
sql,
category,
{
autoCommit: true
}
);
if (result.rowsAffected && result.rowsAffected === 1) {
return category;
} else {
return null;
}
} catch (err) {
console.error(err);
} finally {
if (conn) {
try {
await conn.close();
} catch (err) {
console.error(err);
}
}
}
}
const patchObj = {
cat_id: 1
};
// Comment and uncomment the following to see various dynamic statements
patchObj.cat_desc = 'cat_desc value';
patchObj.msg_for = 'msg_for value';
patchObj.msg_user_owner = 'msg_user_owner value';
// Uncomment the following line to add a column that's not whitelisted
//patchObj.msg_realtor_owner = 'msg_realtor_owner value';
patch(patchObj)
.then(function(cat) {
console.log('Updated succeeded', cat);
})
.catch(function(err) {
console.log(err);
});

Store Auth::user()-id in DB using SQL query in laravel

I want to store Auth::user()->id on the default column user_id in the SQL query shown below.
I tried to set put like this but does not send any data to the database.
public function saveLoadingsData() {
//Validate for a valid Post Request
if (isset($_POST['orderNumber']) && isset($_POST['Truck']) && isset($_POST['receiptNumber']) && isset($_POST['items'])) {
// {"orderNumber":"CRS1104200001","agentId":"3","items":[{"itemId":"4","itemName":"Embe","quantity":"13"}]}
$orderNumber = $_POST['orderNumber'];
$items = $_POST['items'];
$receiptNumber = $_POST['receiptNumber'];
$Truck = $_POST['Truck'];
$driverName = $_POST['driverName'];
foreach ($items as $singleItem) {
$data = array('order_no' => $orderNumber,'user_id'=>Auth::user()->id,"receiptNumber" => $receiptNumber, "Truck" => $Truck, "driverName" => $driverName, "pid" => $singleItem['itemId'], "qty" => $singleItem['quantity']);
// print_r($data);
DB::table('loadings')->insert($data);
// return redirect()->back();
}
// return redirect()->back();
echo "Success";
}
My ajax function
$("#btnSaveOrder").on('click', function(e){
var orderNumber=$("#order_no").val();
var receiptNumber=$("#receiptNumber").val();
var Truck=$("#Truck").val();
var driverName=$("#driverName").val();
var jsonData=convertTableToJson();
$.ajax('/api/loading/saveLoadingsData', {
type: 'POST',
data: {
orderNumber:orderNumber,
receiptNumber:receiptNumber,
Truck:Truck,
driverName:driverName,
items:jsonData
},
success: function (data, status, xhr) {
alert("Data Saved");
document.location.reload(true);
},
error: function (jqXhr, textStatus, errorMessage) {
console.log(errorMessage);
}
});
});
var convertTableToJson = function(){
var rows = [];
$('table#tableSelectedItems tr').each(function(i, n){
if (i!=0) {
var $row = $(n);
rows.push({
itemId: $row.find('td:eq(0)').text(),
itemName: $row.find('td:eq(1)').text(),
quantity: $row.find('td:eq(2)').text(),
});
}
});
return rows;
};
My api route
Route::post('loading/saveLoadingsData', 'LoadingController#saveLoadingsData');
Can someone help me?
I recommend you the following
Pass the $request object in your method and log all object, maybe are missing data and for that reason it does not meet the condition:
saveLoadingsData(Request $request){
Log::info(json_encode($request->all()));`
}
Then check your logs files to see the result in /storage/logs/laravel.log

Reading from one db into another

I am trying to right a function that copies some fields from several company databases into my own database once a day. What I have so far is below. I am wondering if where I console.log(rs) I can open another sql connection to my database and write the data or if I have to store the results somewhere and then open a new connection and send the stored results.
function updateJobs() {
var query = "SELECT JobStart, JobEnd FROM JobData";
sql.connect(Config, (err) => {
if (err) {
console.log("Error while connecting database :- " + err);
} else {
var request = new sql.Request();
request.query(query, function (err, rs) {
if (err) {
console.log("Error while querying database :- " + err);
sql.close();
} else {
console.log(rs);
sql.close();
}
})
}
})
}
This might help
// Source Database
sourceDB.each(`select * from ${Sourcetable}`, (error, row) => {
console.log(row);
const keys = Object.keys(row); // ['columnA', 'columnB']
const columns = keys.toString(); // 'columnA,columnB'
let parameters = {};
let values = '';
// Generate values and named parameters
Object.keys(row).forEach((r) => {
var key = '$' + r;
// Generates '$columnA,$columnB'
values = values.concat(',', key);
// Generates { $columnA: 'ABC', $columnB: 'GHK' }
parameters[key] = row[r];
});
// Insert into another database into OneTable (columnA,columnB) values ($columnA,$columnB)
// Parameters: { $columnA: 'ABC', $columnB: 'GHK' }
destDB.run(`insert into ${Desttable} (${columns}) values (${values})`, parameters);
})
})

Pg-promise : transaction issue

I got something strange with pg-promise with a transaction with generators.
This is what I want :
Get or register a user (getOrRegisterUser)
Do batch stuff (4 inserts generator)
Finally , do the last insert (generator registerCall) with the result of getOrRegisterCurrentParkingIfProvided generator
Here is my code :
db.tx(function (t) {
return t.task.call(params, getOrRegisterUser).then(function (user) {
params.masterId = user.id; // NOTICE : MY USER ID DATABASE
return t.batch([
t.task.call(params, registerNewPhones),
t.task.call(params, registerNewPlate),
t.task.call(params, registerNewSubscriptions),
t.task.call(params, getOrRegisterCurrentParkingIfProvided)
]).then(function (result) {
params.ParkingId = (result[3] !== undefined) ? result[3].id : null;
return t.task.call(params, registerCall);
})
});
}).then(function () {
// job done
resolve();
}).catch(function (err) {
console.log(err);
reject(err);
});
I got this error message at the second generator (registerNewPhones) :
severity: 'ERREUR',
code: '23503',
detail: 'La clé (customer)=(3) n\'est pas présente dans la table « users ».',
Any way to solve this ? I tried transactions like this : https://github.com/vitaly-t/pg-promise#nested-transactions or https://github.com/vitaly-t/pg-promise#synchronous-transactions but with some unknown circumstances I still got error somewhere.
Thanks
PS: I know that the implementation of these generators aren't guilty so ...
EDIT: if you really want to see the code
let squel = require('squel');
// squel with PostgresSQL syntax
let squelPostgres = squel.useFlavour('postgres');
registerNewPhones :
// Register all new phones numbers for user
function * registerNewPhones(t) {
let params = t.ctx.context;
let findPhonesForUserQuery = squelPostgres
.select()
.from("HELPDESK.phones")
.field("number")
.where("customer = ?", params.masterId)
.toString();
let registerPhoneForUser = squelPostgres
.insert()
.into("HELPDESK.phones")
.set("customer", params.masterId);
// find the already known phone number(s) for this user
return t.any(findPhonesForUserQuery).then(function (result) {
// data
let phones = (params.hasOwnProperty("phones") ? params.phones : []);
let alreadyRegisteredPhones = result.map(function (element) {
return element.number;
});
// filter data
let phonesToRegister = phones.filter(function (aPhoneNumber) {
return alreadyRegisteredPhones.indexOf(aPhoneNumber) == -1;
});
// create queries
let queries = phonesToRegister.map(function (phone) {
return db.none(
registerPhoneForUser
.clone()
.set("number", phone)
.toString()
);
});
return t.batch(queries);
});
}
and the generator getOrRegisterUser:
function * getOrRegisterUser(t) {
let params = t.ctx.context;
// QUERIES:
let findUserQuery = squelPostgres
.select()
.from("HELPDESK.users")
.field("id")
.where("registered_id = ?", params.userId)
.toString();
let insertUserQuery = squelPostgres
.insert()
.into("HELPDESK.users")
.setFields({
name: params.userName,
registered_id: params.userId,
typeOfAccount: 'BASIC',
email: params.email
})
.returning('id')
.toString();
let user = yield t.oneOrNone(findUserQuery);
return yield user || t.one(insertUserQuery);
}
The issue is within the ES6-Generator function registerNewPhones:
return t.any(findPhonesForUserQuery)...
it doesn't yield the promise result, which is required for ES6 Generator functions.
i.e. it must be:
return yield t.any(findPhonesForUserQuery)...

dojo ItemFileReadStore.getValue mixed return value is not handled as string

I'am using dojo.data.ItemFileReadStore to query a json file with data. the main purpose is finding translations at Js level.
The Json data has "id" the word and "t" the translation
function translate(word)
{
var json = '/my/language/path/es.json';
var reader = new dojo.data.ItemFileReadStore({
url: json
});
var queryObj = {};
queryObj["id"] = word;
reader.fetch({
query: queryObj,
onComplete: function(items, request){
if (items.length > 0) {
var t = reader.getValue(items[0], 't');
if (dojo.isString(t)) {
return t;
}
}
return word;
},
onError: function(error, request){
return word;
}
});
}
The return value is always a undefined wether there is a translation or not. any ideas?
I tried typecasting with no success.
You can do it like this:
function translate(wordId) {
var translatedWord= wordId;
var store = new dojo.data.ItemFileReadStore({ data: storeData });
store.fetch({ query: { id: wordId },
onItem: function (item) {
translatedWord= (store.getValue(item, 't'));
}
});
return translatedWord;
}