How to INSERT dynamic info in MySQL table using Node/Express JS - mysql

I am working on a react app with a Node/Express JS back end.
I am trying to insert a value, based on the input of the user, into MySQL database. Here is my Node post request:
// To create a new user account
app.post('/createUser', function(req, res) {
console.log('in createUser');
const {firstName, lastName, username, passcode, email} = req.query;
const CreateUser = `CALL CreateUser('${firstName}', '${lastName}',
'${username}', '${passcode}', '${email}')`;
mysqlConnection.query(CreateUser, (error, result) => {
if(error) {
res.status(500)
res.send("Could not create user account.")
} else {
console.log('create user cart');
console.log(`${req.query.username}`);
const CreateUserCart = `CALL CreateUserCart('${req.query.username}')`;
mysqlConnection.query(CreateUserCart, (error1, result1) => {
if(error1) {
res.status(500)
res.send("Could not create user account.")
} else {
res.status(201)
res.send("Account successfully created!")
}
})
}
})
});
The first stored procedure, CreateUser, is working fine. What I want to do is take the value in req.query of username and pass it to the stored procedure called CreateUserCart. Here is that stored procedure:
CREATE DEFINER=`lsharon`#`%` PROCEDURE `CreateUserCart`(IN
username VARCHAR(45)
)
BEGIN
INSERT INTO SHOPPING_CART(CustomerID)
SELECT CustomerID
FROM CUSTOMER
WHERE Username = username;
END
My desire is to insert the CustomerID that belongs to the user I just created into the shopping cart table. However, currently it is inserting every CustomerID into that table, even if it already exists there. I only want to insert the ID for the single user who just created an account.
Any suggestions would be appreciated!!

The query in your sp says
WHERE Username = username;
Your column names are case insensitive. So Username = username matches every row in your CUSTOMER. It's a version of WHERE 1 = 1 -- always true.
Try changing the name of your parameter, something like this:
CREATE PROCEDURE `CreateUserCart` (IN newUsername VARCHAR(45) )
BEGIN
INSERT INTO SHOPPING_CART(CustomerID)
SELECT CustomerID
FROM CUSTOMER
WHERE Username = newUsername;
END
MySQL stored programs are tons of fun to troubleshoot, eh? Lots of people avoid them for that reason. On the other hand, many large enterprises use lots of stored programs to interact with their data. But those enterprises usually pay the rather large licensing fees for Oracle or SQL Server. Those databases have better stored programming languages and tools. So, if you work at one of those places, you'll appreciate having a little bit of exposure to stored programs.
Pro tip there's a much more robust way to handle this workflow. Get your first stored procedure to end with something like
SELECT LAST_INSERT_ID() CustomerID;
This will make the procedure return a resultset, which you can handle just as if it were a SELECT query. LAST_INSERT_ID() gets the autoincrement value from the most recent INSERT.
Then read that customer id from your resultset, and pass it as a parameter to CreateUserCart rather than the customer's name.

Related

Multi-parameter search with mysql and node.js

Let me preface by saying I'm very new to SQL (and back end design) in general. So for those annoyed with noob questions, please be gentle.
BACKGROUND:
I'm trying to build a product test database (storing test data for all our products) where I want a user to be able to refine a search to find test data they actually want. For example, they may start by searching for all products of a certain brand name, and then refine it with a product type, and/or refine it with a date range of when the test was done.
PROBLEM:
I'm having a hard time finding information on how to implement multi-parameter searches with mysql and node.js. I know you can do nested queries and joins and such within pure SQL syntax, but it's not abundantly clear to me how I would do this from node.js, especially when certain search criteria aren't guaranteed to be used.
Ex:
CREATE PROCEDURE `procedureName`(
IN brandname VARCHAR(20),
producttype VARCHAR(30))
BEGIN
SELECT * FROM products
WHERE brand = brandname
AND product_type = producttype;
END
I know how to pass data from node.js to this procedure, but what if the user didn't specify a product type? Is there a way to nullify this part of the query? Something like:
AND product_type = ALL;
WHAT I'VE TRIED:
I've also looked into nesting multiple SQL procedures, but passing in dynamic data to the "FROM" clause doesn't seem to be possible. Ex: if I had a brandname procedure, and a product type procedure, I don't know how/if I can pass the results from one procedure to the "FROM" clause of the other to actually refine the search.
One idea was to create tables with the results in each of these procedures, and pass those new table names to subsequent procedures, but that strikes me as an inefficient way to do this (Am I wrong? Is this a completely legit way to do this?).
I'm also looking into building a query string on the node side that would intelligently decide what search criteria have been specified by the front end, and figure out where to put SQL AND's and JOIN's and what-nots. The example below actually works, but this seems like it could get ugly quick as I add more search criteria, along with JOINS to other tables.
// Build a SQL query based on the parameters in a request URL
// Example request URL: http://localhost:3000/search?brand=brandName&type=productType
function qParams(req) {
let q = "SELECT * FROM products WHERE ";
let insert = [];
if(req.query.brand) {
brandname = req.query.brand; // get brandname from url request
q = q + `brand = ?`, // Build brandname part of WHERE clause
insert.push(brandname); // Add brandname to insert array to be used with query.
};
if(req.query.type) {
productType = req.query.type; // get product type from url request
insert.length > 0 ? q = q + ' AND ' : q = q; // Decide if this is the first search criteria, add AND if not.
q = q + 'product_type = ?'; // Add product_type to WHERE clause
insert.push(productType); // Add product_type variable to insert array.
}
// Return query string and variable insert array
return {
q: q,
insert: insert
};
};
// Send Query
async function qSend(req, res) {
const results = await qParams(req); // Call above function, wait for results
// Send query string and variables to MySQL, send response to browser.
con.query(results.q, results.insert, (err, rows) => {
if(err) throw err;
res.send(rows);
res.end;
})
};
// Handle GET request
router.use('/search', qSend);
CONCISE QUESTIONS:
Can I build 1 SQL procedure with all my search criteria as variables, and nullify those variables from node.js if certain criteria aren't used?
Is there way to nest multiple MySQL procedures so I can pick the procedures applicable to the search criteria?
Is creating tables of results in a procedure, and passing those new table names to other procedures a reasonable way to do that?
Building the query from scratch in node is working, but it seems bloated. Is there a better way to do this?
Googling "multi-parameter search mysql nodejs" is not producing useful results for my question, i.e. I'm not asking the right question. What is the right question? What do I need to be researching?
One option is to use coalesce():
SELECT p.*
FROM products p
WHERE
p.brand = COALESCE(:brandname, p.brand)
AND p.product_type = COALESCE(:producttype, p.producttype);
It may be more efficient do explicit null checks on the parameters:
SELECT p.*
FROM products p
WHERE
(:brandname IS NULL OR p.brand = :brandname)
AND (:producttype IS NULL OR p.product_type = :producttype);

why a variable defined in a node module is shared between two users

I run a node API server with pm2 cluster mode that will communicate with a mysql DB server.
In module x.js I have a code like this:
let insertMappingQuery = ``;
...
...
const constructInsertMappingQuery = () => {
insertMappingQuery += `
INSERT IGNORE INTO messages_mapping (message_id, contact_id)
VALUES (` + message_id + `, ` + contact_id + ` + `);`;
}
When a user sends a message a function will call module x and the code above is executed for his message (let's say message_id = 1)
INSERT IGNORE INTO messages_mapping (message_id, contact_id)
VALUES (1, some_id);
then another user sends a message and the code is executed for let's say message_id = 2 however the query will look like this:
INSERT IGNORE INTO messages_mapping (message_id, contact_id)
VALUES (1, some_id);
INSERT IGNORE INTO messages_mapping (message_id, contact_id)
VALUES (2, some_id);
So basically when user two sends a message, this query will contain what user one already executed. So user one will have his record inserted twice.
This doesn't happen all the time but it happens a lot (I would say 30% to 50%) and I couldn't find any pattern when this happens.
Users don't have to do it at the same time, there might be some time difference (minutes or even hours).
could this be related to the variable not being cleared in the memory? or a memory leakage of some kind?
I don't understand how two different users will share a variable.
Remember that require caches modules and all subsequent require calls are given the same things, so write something that exports a function, or class, so that you can safely call/instantiate things without variables getting shared.
For example:
const db = require(`your/db/connector`);
const Mapper = {
addToMessageMapping: async function(messageId, contactId) {
const query = `
INSERT IGNORE INTO messages_mapping (message_id, contact_id)
VALUES (${message_id}, ${contact_id});
`;
...
return db.run(query);
},
...
}
module.exports = Mapper;
And of course this could have been a class, too, or it could even have been that function directly - the only thing that changes is how you make it run that non-conflicting-with-any-other-call function.
Now, consumers of this code simply trust that the following is without side effects:
const mapper = require('mapper.js');
const express, app, etc, whatever = ...
....
app.post(`/api/v1/mappings/message/:msgid`, (req, res, next) => {
const post = getPOSTparamsTheUsualWay();
mapper.addToMessageMapping(req.params.msgId, post.contactId)
.then(() => next());
.catch(error => next(error));
}, ..., moreMiddleware, ... , (req,res) => {
res.render(`blah.html`, {...});
});
Also note that template strings exist specifically to prevent string composition by concatenating strings with +, the whole point is that they can take ${...} inside them and template in "whatever is in those curly braces" (variables, function calls, any JS really).
(The second power they have is that you can prefix tag them with a function name and that function will run as part of the templating action, but not a lot of folks need this on a daily basis. ${...} templating though? Every day, thousands of times).
And of course on a last note: it looks like you're creating raw SQL, which is always a bad idea. Use prepared statements for whatever database library you're using: it supports them, and means any user-input is made safe. Right now, someone could post to your API with a message id that's ); DROP TABLE messages_mapping; -- and done: your table's gone. Fun times.
Apparently I didn't know that requireing a module will cache it and reuse it. Thus global variables in that module will be cached too.
So the best solution here is to avoid using global variables and restructure the code. However if you need a quick solution you can use:
delete require.cache[require.resolve('replaceWithModulePathHere')]
Example:
let somefuncThatNeedsModuleX = () => {
delete require.cache[require.resolve('./x')];
const x = require('./x');
}

storing JSON data using to html5 webstorage

I am using HTML5 web storage for storing data locally. I want to store the JSON data into database. For that i am converting the JSON as string using JSON.stringify(obj).
But, I could not able to storing the data. can any body suggest the best approach?
var customerStr = JSON.stringify($scope.indexData);
var db = openDatabase('customerDB', '1.0', 'customer DB', 2 * 1024 * 1024);
db.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS CUSTOMER (id unique, userid, customerdata VARCHAR)');
tx.executeSql('INSERT INTO CUSTOMER (id, userid, customerdata) VALUES (1, 2, customerStr)');
console.log('<p>Log message created and row inserted.</p>');
});
I think there is some confusion as posted code and title differs.
Code you have posted is creating a table if not exists and inserting a record.
however create table line is not correct as datatype of userid is missing.
try to run this sql in query analyser to find out syntax error
'CREATE TABLE IF NOT EXISTS CUSTOMER (id unique, userid, customerdata VARCHAR)'
Take a look here for how to use HTML5 WEb storage
you can use localstorage as below.
if(typeof(Storage) !== "undefined") {
// Code for localStorage/sessionStorage.
} else {
// Sorry! No Web Storage support..
}
// Store
localStorage.setItem("lastname", "Smith");
// Retrieve
document.getElementById("result").innerHTML = localStorage.getItem("lastname");

mysql - node - Row inserted and queryable via server connection lost on DB restart

I ran into an issue testing today that occurred during or after an insert via a connection on my node server. The code where the insert is performed looks something like this:
// ...
username = esc(username);
firstname = esc(firstname);
lastname = esc(lastname);
var values = [username, firstname, lastname].join(',');
var statement = 'INSERT INTO User(Username,FirstName,LastName) VALUES({0});\n'+
'SELECT FirstName, LastName, Username, Id, IsActive FROM User WHERE Username={1};'
statement = merge( statement, [ values, username ] );
conn.query(statement, function(e, rows, fields){
e ? function() {
res.status(400);
var err = new Error;
err.name = 'Bad request';
err.message = 'A problem occurred during sign-up.';
err.details = e;
res.json(err);
}() : function(){
res.json( rows[1] );
}();
}
A quick note on esc() and merge(), these are simply util functions that help prepare the database statement.
The above code completed successfully, ie. the response was a 200 with the newly inserted user row in the body. The row inserted was queryable via the same connection throughout the day. I only noticed this afternoon when running the following generic query as root via shell, the row was missing.
SELECT Id, FirstName, LastName FROM User;
So at that point I restarted the database and the node server. Unfortunately. Now it would appear the row is gone entirely, as well as any reliable path to troubleshoot.
Here are some details of interest to my server setup. As of yet, no idea how (if at all) any of these could be suspect.
Uses only single connection as opposed to conn pool (for now)
multipleStatements=true in the connection config (obviously above snippet makes use of this)
SET autocommit = 0; START TRANSACTION; COMMIT; used elsewhere in the codebase to control rollback
Using poor man's keep-alive every 30 seconds to avoid connection timing out: SELECT 1;
I've been reading up all evening and am running out of ideas. Any suggestions? Is this likely an issue of uncommitted data? If so, is there a reliable way to debug this? Better yet, is there any way to prevent it? What could be the cause? And finally, if in debugging my server I find data in this state, is there a way to force commit at least so that I don't lose my changes?

Stored Procedure for multiple value with single parameter in SQL Server

I have this form in C# with a listbox where I selected 4 items. Now I want to make single stored procedure using which I can find data from single table for all this selected item with single parameter.
As I am a beginner when it comes to SQL Server, I completely don't know this type of procedure
Thanks, but this is not my question's answer
I want a Single Stored Procedure for all Items which are selected in ListBox
Create Procedure procedureName
(
#ItemName varchar(50),
)
AS
BEGIN
(
Select * from item_master where item_name = #ItemName
)
END
by this Query i can find data for one ItemName, but i want for all selected Items in Listbox, even I don't know the C# code also,
so plz help me....
This is a very simple example that does what you want. You would not want to use hard-coded connection strings, especially in-line, and you would want error-handling, but I am going for as much clarity as possible. You would also probably want to make the column length greater than 50 characters, but I made it match your column definition.
Also, I would recommend a generic approach, passing keys (column names) and values, so as to be able to use it for any sort of criteria, but you asked that I keep it to exactly what you require, so I trimmed it down to the essential.
This example returns all the Employees with FirstName matching any in the list passed to the stored procedure (as a user-defined table type).
First, create a user-defined table type (to hold the values you want to pass to the stored procedure) in your SQL Server database as follows:
CREATE TYPE [dbo].[FilterValues] AS TABLE(
[Value] [varchar](50) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Value] ASC
)
)
The stored procedure to return the Employees looks as follows (note that it has the user-defined table type as the type of the single parameter passed in):
CREATE PROCEDURE [dbo].[GetEmployees] (
#FirstNameFilterValues dbo.FilterValues READONLY
)
AS
BEGIN
SELECT * FROM Employees
INNER JOIN #FirstNameFilterValues fv ON fv.Value = Employees.FirstName;
END
That's the SQL Server side done. To call it from C#, you can create a DataTable with a single column matching the column name and populate it with the values you want. In this simple example, I populate it with two names, but it could be as many as you want.
var filterValuesDataTable = new DataTable();
filterValuesDataTable.Columns.Add(new DataColumn("Value", typeof(string)) { AllowDBNull = false });
filterValuesDataTable.Rows.Add("Frodo");
filterValuesDataTable.Rows.Add("Sam");
using (var connection = new SqlConnection("server=.;Initial Catalog=Test;Integrated Security=True;"))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "GetEmployees";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#FirstNameFilterValues", filterValuesDataTable);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine("{0} {1}", reader["FirstName"], reader["LastName"]);
}
reader.Close();
}
}
connection.Close();
}