My question is : A Stored Procedure that receives a given flight and a given date as input, and displays a customer call list, which includes name, address and phone number as output.
The query is simple, I can get all the data from a single table but, I don’t know how to take an input while running the procedure and then comparing it with the values inside.
The query is:
select NAME, ADDRESS, PHONE
FROM SCH_FLIGHTS SF, PASSENGERS P
WHERE SF.DATE(input) = P.DATE
Would highly appreciate you helping me on the same....just need to convert that query into a stored procedure which takes 2 inputs, a date and a flight_no as mentioned in the question above.
Thanks.
To create the SP (don't know the exact datatypes so i am just guessing) :
delimiter //
CREATE PROCEDURE NameOfProcedure(input VARCHAR(32))
BEGIN
select NAME, ADDRESS, PHONE
FROM SCH_FLIGHTS SF, PASSENGERS P
WHERE SF.DATE(input) = P.DATE
END
//
your PHP code would look somthing like this (TODO replace $input by query parameter for security reasons):
$result = $db->query("call NameOfProcedure($input)");
Related
I'm working on a MySQL Way of printing an "affiliate tree" and got the thing working with Common Table Expression. I'm using the following code right now:
WITH RECURSIVE recUsers AS
(
SELECT ID, username, sponsorID, 1 AS depth, username AS path
FROM users
WHERE id = 1
UNION ALL
SELECT c.ID, c.username, c.sponsorID, sc.depth + 1, CONCAT(sc.path, ' > ', c.username)
FROM recUsers AS sc
JOIN users AS c ON sc.ID = c.sponsorID
)
SELECT * FROM recUsers;
This selects the tree underneath the user with the id 1.
Now what I'd need to get is a way to pass that id as a parameter, so I don't need to define everything from the beginning every time I want to get the result.. So my idea is to put everything in a stored prodecure and pass the id in as a parameter.. However, so far I didn't get it working and always getting various errors that are very self speaking...
Basically what I've tried was
DELIMITER //
CREATE PROCEDURE getAffiliateTree(IN userid INT())
BEGIN
---my code here, the userid 1 replaced with userid
END//
DELIMITER;
However, this doesn't seem to work.. How can I get this done?
Two things I would suggest:
Use INT, not INT(). The optional length argument to integer types is deprecated in MySQL 8.0 (which I know you're using, because you're using CTE syntax). Even if you did use the length argument, using an empty argument is not legal syntax.
Make sure that the userid input parameter name is distinct from all of the columns in the tables you reference. That is, if the table has a column named userid (any capitalization), then change the name of your input parameter. Otherwise you may make ambiguous expressions like:
... WHERE userid = userid
Even though you intend one of these to be the column and the other to be the parameter, the SQL parser has no way of knowing that. It ends up treating both as the column name, so it's trivially true on all rows of the table.
Actually, a third thing I would suggest: when you ask questions, "it doesn't seem to work" isn't clear enough. Did it produce an error? If so, what was the full error message? Did it produce no error, but didn't give you the result you wanted? If so, show a mocked-up example of what you expected, and what the query produced that didn't match. It helps to be as clear as you can when you post questions, so readers don't have to guess what trouble you need help with.
I am trying to automate mathematic formulas that I need to run many times over a dataset for a view page. I have used the SELECT INTO function to successfully create these values. Below is an example of code that has worked:
DELIMITER //
DROP FUNCTION IF EXISTS InvLevel;
CREATE FUNCTION InvLevel(
metric DECIMAL (65,4),
norm DECIMAL (65,10)
)
RETURNS DECIMAL(65,4)
BEGIN
SET #InvLevel = NULL;
SELECT
metric * norm
INTO #InvLevel;
RETURN #InvLevel;
END //
I then call this with:
CREATE VIEW Mainfile
SELECT
Clients, Users, Normalization, ID, Year
MNorm(Clients, Normalization) AS ClientsN,
MNorm(Users, Normalization) AS UsersN
FROM Dataset
Does everything I want! I'm happy.
However, one of the important functions I need to run is a lag function--i.e. I'm interested in how clients, users, etc. has changed over time. So I wrote the following stored function:
DELIMITER //
DROP FUNCTION IF EXISTS Pace;
CREATE FUNCTION Pace(
metric decimal(65,4),
id VARCHAR(20),
year INT
)
RETURNS DECIMAL(65,4)
BEGIN
SELECT
metric - (LAG(metric, 1) OVER (PARTITION BY id ORDER BY year))
INTO #paceco;
RETURN #paceco;
END //
That I then call as:
Pace(Clients,ID,Year) AS ClientsP
However, this operation only returns NULL values in ClientsP. I know it's not a question of the formula since if I directly write out the math in the create view function, I receive the correct values.
I know that I can just plug the original math into the view function but I have many metrics I will be repeating this formula on (for the next few years or so) so I would vastly prefer to automate it instead of have long, messy SQL files. Thanks in advance.
I notice that many people have said it's possible to create an injection attack, but my understanding is that is if someone is creating a query from a string, not parameters. In order to test the statement that Stored Procedures do not protect you against Injection Attacks, I am putting this example up in the hopes someone can show me a vulnerability if there is one.
Please note that I have built the code this way to easily insert a function that calls a procedure and embed it in a SELECT query. That means I cannot create a Prepared Statement. Ideally I'd like to keep my setup this way, as it is dynamic and quick, but if someone can create an injection attack that works, obviously that is not going to happen.
DELIMITER $$
#This procedure searches for an object by a unique name in the table.
#If it is not found, it inserts. Either way, the ID of the object
#is returned.
CREATE PROCEDURE `id_insert_or_find` (in _value char(200), out _id bigint(20))
BEGIN
SET #_value = _value;
SET #id = NULL;
SELECT id INTO _id FROM `table` WHERE name=_value;
IF _id IS NULL THEN
BEGIN
INSERT INTO `table` (`name`) VALUE (_value);
SELECT LAST_INSERT_ID() INTO _id;
END;
END IF;
END$$
CREATE FUNCTION `get_id` (_object_name char(200)) RETURNS INT DETERMINISTIC
BEGIN
SET #id = NULL;
call `id_insert_or_find`(_object_name,#id);
return #id;
END$$
The PHP Code
The PHP code I use here is:
(note, Boann has pointed out the folly of this code, below. I am not editing it for the sake of honoring the answer, but it will certainly not be a straight query in the code. It will be updated using ->prepare, etc. I still welcome any additional comments if new vulnerabilities are spotted.)
function add_relationship($table_name,$table_name_child) {
#This table updates a separate table which has
#parent/child relationships listed.
$db->query("INSERT INTO table_relationships (`table_id`,`tableChild_id`) VALUES (get_id('{$table_name}'),get_id('{$table_name_child}')");
}
The end result is
table `table`
id name
1 oak
2 mahogany
Now if I wanted to make oak the child of mahogany, I could use
add_relationship("mahogany","oak");
And if I wanted to make plastic the child of oak, I could use
add_relationship("oak","plastic");
Hopefully that helps give some framework and context.
It is not necessarily the stored procedure that is unsafe but the way you call it.
For example if you do the following:
mysqli_multi_query("CALL id_insert_or_find(" + $value + ", " + $id + ")");
then the attacker would set $value="'attack'" and id="1); DROP SCHEMA YOUR_DB; --"
then the result would be
mysqli_multi_query("CALL id_insert_or_find('attack', 1); DROP SCHEMA YOUR_DB; --)");
BOOM DEAD
Strictly speaking, that query should be written to escape the table names:
$db->query("INSERT INTO table_relationships (`table_id`,`tableChild_id`) " .
"VALUES (get_id(" . $db->quote($table_name) + ")," .
"get_id(" . $db->quote($table_name_child) . "))");
Otherwise, it would break out of the quotes if one of the parameters contained a single quote. If you only ever call that function using literal strings in code (e.g., add_relationship("mahogany", "oak");) then it is safe to not escape it. If you might ever call add_relationship using data from $_GET/$_POST/$_COOKIE or other database fields or files, etc, it's asking for trouble. I would certainly not let it pass a code review.
If a user could control the table name provided to that function then they could do, for example:
add_relationship("oak", "'+(SELECT CONCAT_WS(',', password_hash, password_salt) FROM users WHERE username='admin')+'");
Now you might say that there's no practical way to then extract that information if the resulting table name doesn't exist, but even then you could still extract information one binary bit at a time using a binary search and separate queries, just by breaking the query. Something like this (exact syntax not tested):
add_relationship("oak", "plastic'+(IF(ORD(SUBSTR(SELECT password_hash FROM users WHERE username='admin'),1,1)>=128, 'foo', ''))+'");
Really, it's easier to just escape the parameters and then you don't have to worry.
I am using a database someone else produced (and I am not really authorised to change it). However, as I was looking into the stored procedures within the database I noticed the following procedure:
DELIMITER $$
CREATE PROCEDURE `logIn`(userName varChar(50), userPass varChar(50))
BEGIN
declare userID int;
SELECT
u.userID INTO userID
FROM
users u
WHERE
u.userName=userName
AND u.userPassword=MD5(userPass);
IF (IFNULL(uID,-1) > 0) THEN
select 1 as outMsg;
ELSE
select 0 as outMsg;
END IF;
END$$
with the corresponding table users having three columns: userID INT, userName VARCHAR(50) and userPassword VARCHAR(50).
As I am not very good at this, could someone let me know whether the input for such a function needs to be sanitised as to not allow any SQL injections and if not - why? A general rule of thumb would be very much appreciated.
P.S. This function will be called from a JS script on a form submit.
There are a few rules of thumb here that depend on the underlying datatype and how it's inserted into the database.
First, Parameterized queries are always best for SQL Injection protection.. but.. if you can't change that..
String type:
Remove any single quotes
OR
Replace any single quotes with the single quote twice.
Replace any of the following characters with their encoded alternative;
>
<
"
;
(chr 34)
)
(
For example.. ) is replaced with & #x29;
-(the space in the above example is so you'll see the code, remove it to get ")")
For a datatype other then string, check that the datatype is sane and remove any character that shouldn't be in the datatype. If it's an integer, make sure the string that you're passing in is an integer. This can commonly be done by casting to the type in code. The cast will either work.. or cause an error. It's also good to check that the datatype min and maxes have not been exceeded. For example.. If I was checking for an integer, I might use code similar to this:
var myInt = parseInt(param);
Then I might check it's bounds to be sure it's less then the maximum integer value and greater then the minimum integer value.
That should be good enough to prevent a SQL Injection attack...
And.. since you have not posted the code that actually interfaces with the database... As an added precaution.. you may also want to remove --,`,%,",", "".
You only want 'sane' values getting to the database call.. so an integer like, $309 wouldn't make sense, you'd want to remove the $.. . probably by using a regex replace for any non numeric characters a comma and a period.
[^[0-9,.]]
Be extra cautious.
Yes, the input must be sanitized before trying to run the procedure.
You might want to share the actual calling point for the procedure to get more help here, since there is no way that the procedure is called directly from JS on form submit. You probably have a Servlet, PHP page or some HTTP friendly intermediary to make the database call somehow.
I'm trying to write a PL/SQL or T-SQL for the following example:
Write a PL/SQL or T-SQL procedure to retrieve and output the marina number, slip number, rental fee, boat name, and owner number for every slip whose length is equal to the length stored in I_LENGTH.
So far I've come up with this:
'Create Procedure Boat_Info (I_Length IN Marina_Slip.Length%Type) AS
I_Marina_Num Marina_Slip.Marina_Num%Type I_Slip_Num
Marina_Slip.Slip_Num%Type I_Rental_Fee Marina_Slip.Rental_Fee%Type
I_Boat_Name Marina_Slip.Boat_Name%Type I_Owner_Num
Marina_Slip.Owner_Num%Type;
Begin Select Marina_Num, Slip_Num, Rental_Fee, Boat_Name, Owner_Num
Into I_Marina_Num, I_Slip_Num, I_Rental_Fee, I_Boat_Name, I_Owner_Num
from Marina_Slip Where Length = ??
That last part I'm still missing, as I'm have no specific value for Length to restrict my output. and also cannot come up with a DBMS output.
The variable in paranthesis is a parameter. Yo can use it in your where clause.
For example :
(myLenght INT)
where length = myLength
You may define more than one parameters separated by a comma sign ,
See also: Mysql stored procedure parameters