I'm attempting a query to get the latest N messages in a particular conversation from a table of messages. I think this is the correct sql:
select * from
(select * from messages where convoId = to order by timestamp DESC limit 10)
order by timestamp ASC;
I have attempted this in sqlite.swift:
static let table = Table("messages")
let query = (table.filter(convoId == to).order(timestamp.desc).limit(10)).select(table[*]).order(timestamp.asc)
which is not working once the amount of messages goes past the limit. Is there any way to see what sql is produced by the sqlite.swift query? Any suggestions?
EDIT: I have also attempted the raw SQL query but now I'm not sure how to extract the result. I feel like this should be a last resort:
let toQuoted = "'" + to + "'"
let subQueryStr: String = [
"(SELECT * FROM",
MessageDataHelper.TABLE_NAME,
"WHERE",
MessageDataHelper.CONVO_ID, "=", toQuoted, "ORDER BY", MessageDataHelper.TIMESTAMP, "DESC LIMIT", String(5), ")"
].joined(separator: " ")
let queryStr: String = [
"SELECT * FROM",
subQueryStr,
["ORDER BY", MessageDataHelper.TIMESTAMP, "ASC;"].joined(separator: " ")
].joined(separator: "\n")
let stmt = try db.prepare(queryStr)
for row in stmt {
// ? how can this be used to create model structure
for (index, name) in stmt.columnNames.enumerate() {
print ("\(name)=\(row[index]!)")
}
}
row[index] is of type Binding, so I'm unsure how to retrieve the value there. Help please!
Thanks
Okay, so looks like sub query might be too complex to express in sqllite.swift. I ended up going with the raw sql query. You can retrieve the result by casting the binding as mentioned here:
Getting results from arbitrary SQL statements with correct binding in SQLite.swift
Related
I'm having some strange feeling abour sqlite3 parameters that I would like to expose to you.
This is my query and the fail message :
#query
'SELECT id FROM ? WHERE key = ? AND (userid = '0' OR userid = ?) ORDER BY userid DESC LIMIT 1;'
#error message, fails when calling sqlite3_prepare()
error: 'near "?": syntax error'
In my code it looks like:
// Query is a helper class, at creation it does an sqlite3_preprare()
Query q("SELECT id FROM ? WHERE key = ? AND (userid = 0 OR userid = ?) ORDER BY userid DESC LIMIT 1;");
// bind arguments
q.bindString(1, _db_name.c_str() ); // class member, the table name
q.bindString(2, key.c_str()); // function argument (std::string)
q.bindInt (3, currentID); // function argument (int)
q.execute();
I have the feeling that I can't use sqlite parameters for the table name, but I can't find the confirmation in the Sqlite3 C API.
Do you know what's wrong with my query?
Do I have to pre-process my SQL statement to include the table name before preparing the query?
Ooookay, should have looked more thoroughly on SO.
Answers:
- SQLite Parameters - Not allowing tablename as parameter
- Variable table name in sqlite
They are meant for Python, but I guess the same applies for C++.
tl;dr:
You can't pass the table name as a parameter.
If anyone have a link in the SQLite documentation where I have the confirmation of this, I'll gladly accept the answer.
I know this is super old already but since your query is just a string you can always append the table name like this in C++:
std::string queryString = "SELECT id FROM " + std::string(_db_name);
or in objective-C:
[#"SELECT id FROM " stringByAppendingString:_db_name];
My current query:
select timestamp from messagesTable
where partner_jid='" + lastUserJid + "' AND msg='.roll'
order by timestamp DESC LIMIT 1 OFFSET 1;
This works fine... unless the values don't exist in the database.
If values do not exist in database, then it should Select * messagesTable; or do nothing if possible.
Is there a way to add a check for that within the same query? It has to be the same query unfortunately because I need to execute things through adb shell. I've been trying things out with CASE but I do not really understand much about SQL.
You can just append a second query, with a WHERE filter that checks whether the first query did not return anything:
SELECT *
FROM (SELECT timestamp
FROM messagesTable
WHERE partner_jid = ?
AND msg = '.roll'
ORDER BY timestamp DESC
LIMIT 1 OFFSET 1)
UNION ALL
SELECT -1 -- or "timestamp FROM msgTab", or whatever
WHERE NOT EXISTS (SELECT timestamp
FROM messagesTable
WHERE partner_jid = ?
AND msg = '.roll');
I would like to check if a value can be returned or not by my SQL request:
Search_IDItem = "SELECT * FROM giftshop WHERE id ="..item_id..""
for row_2 in db:nrows(Search_IDItem) do (..)
CheckInventory = "SELECT * FROM inventory WHERE code ="..row_2.code..""
if CheckInventory ~= nil then
print(row_2.code)
updateItemsCode(row_2.code, "inventory", "qtyoninventory", row_2.qtyoninventory+1)
else
insertInventory(2,row_2.code, row_2.name, row_2.src, row_2.desc, "no",row_2.qtyoninventory,row_2.price,row_2.usetxt)
end
The error is:
near "=": syntax error
Basicly, I would like to know if the value exist, I'll only update the field "quantity", if not, I will create the new item.
Unfortunately, it doesn't work! Is there any advice or solution?
Syntax error is a result of misunderstanding of your query by the SQL.
One of the most often reasons is incorrect string insertion. So I suppose that row_xx.code is a string
To solve the problem you should "quote" row_xx.code by ' symbol like this:
CheckInventory = "SELECT * FROM inventory WHERE code ='"..row_2.code.."'"
Update
To solve the second part of the problem and figure out if value exists I suggest following
In old good plain Java when I get SQLite Cursor I may check if it has rows at all like this:
if(cursor.moveToFirst())
// it has at least one value
else
// it hasn't values at all
Sure there is similar thing for Corona
You also may use following approach to update record (creating if not exists):
// create record IF NOT EXISTS:
String createNonExistentRec = "Insert or ignore into "...// insert key/unique values
db.execSQL(createNonExistentRec, ...);
// update values:
String updateQuery = "..."
...
I found a solution :
for x in db:urows "select count(*) from inventory" do
if x>0 then -- The inventory is empty
for w in db:urows("SELECT count (*) FROM inventory WHERE code ='"..row_2.code.."'") do
--The item is already in the inventory
if w > 0 then
CheckInventory = "SELECT * FROM inventory WHERE code ='"..row_2.code.."'"
for row_4 in db:nrows(CheckInventory) do
updateItemsCode(row_4.code, "inventory", "qtyoninventory", row_4.qtyoninventory+1)
end
else
--The item is not in the inventory, so we add it!
for maxid in db:urows("SELECT MAX(id) FROM inventory") do
insertInventory(maxid+1,row_2.code, row_2.name, row_2.src, row_2.desc, "no",1,row_2.price,row_2.usetxt)
end
end
end
else
insertInventory(1,row_2.code, row_2.name, row_2.src, row_2.desc, "no",1,row_2.price,row_2.usetxt)
end
end
Read this:
https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet
On the dynamic sql part it has various such as this:
So, if you had an existing Dynamic query being generated in your code that was going to Oracle that looked like this:
String query = "SELECT user_id FROM user_data WHERE user_name = '" + req.getParameter("userID")
+ "' and user_password = '" + req.getParameter("pwd") +"'";
try {
Statement statement = connection.createStatement( … );
ResultSet results = statement.executeQuery( query );
}
You would rewrite the first line to look like this:
Codec ORACLE_CODEC = new OracleCodec();
String query = "SELECT user_id FROM user_data WHERE user_name = '" +
ESAPI.encoder().encodeForSQL( ORACLE_CODEC, req.getParameter("userID")) + "' and user_password = '"
+ ESAPI.encoder().encodeForSQL( ORACLE_CODEC, req.getParameter("pwd")) +"'";
And it would now be safe from SQL injection, regardless of the input supplied.
But the later is says:
Oracle 10g escaping
An alternative for Oracle 10g and later is to place { and } around the string to escape the entire string. However, you have to be careful that there isn't a } character already in the string. You must search for these and if there is one, then you must replace it with }}. Otherwise that character will end the escaping early, and may introduce a vulnerability.
I did not see an example, but does this mean I can use braces instead of the Codec ORACLE_CODEC....etc.? Does anyone have an example? Thanks.
No, this is not an injection prevention technique. The only way to be 100% sure that you're not vulnerable to injection is to use prepared statements and bind parameters for all user input that needs to be inserted into the query. Anything less than that, and you're pretty much just rolling the dice.
I am writing a query to allow a user to search on what they provide keywords in asp.net, C# and mssql:
string projectPart = null;
string categoryPart = null;
string descriptionPart = null;
if (this.Textbox_ProjectNr.Text.Trim().Length > 0)
projectPart = " AND Number='" + this.Textbox_ProjectNr.Text.Trim() + "' ";
if (this.Textbox_Category.Text.Trim().Length > 0)
categoryPart = " AND Category LIKE '%" + this.Textbox_Category.Text.Trim() + "%' ";
if (this.Textbox_pDescription.Text.Trim().Length > 0)
descriptionPart = " AND ProductDescription LIKE '%" + this.Textbox_pDescription.Text.Trim() + "%' ";
string query = "SELECT * from Project = p.ID " + projectPart + descriptionPart + categoryPart;
I dont know whether this query is sufficient for a traditional query search. Because I see there are some bottlenecks of this search:
if the user does not type anything, it returns all of the data => For this I only do the query when one of the fields are filled.
if the user provides some keywords "P" for each field, the result will be millions of data.
I dont know how to improve the search query basically. any suggestions are appreciated.
Thanks in adavance.
The most important improvement is to protect you code against SQL injection attacks.
You should not concatenate the raw input in the SQL string. If someone searches for the following text for example:
Bwah ha ha'; DROP DATABASE northwind; PRINT'
This will be added to your query to produce
SELECT *
FROM mytable
WHERE category LIKE '%Bwah ha ha'; DROP DATABASE northwind; PRINT'%'
This is a valid SQL command and will happily execute and drop your database (or do anything else the attacker wants)
For more information see SQL Injection and Santitizng Inputs.
You must make this query injection proof! Do not concatenate user entered values, but use parameters, like this:
SqlCommand cmd = new SqlCommand(#"
SELECT * from Project
WHERE
( Number = #Number OR #Number IS NULL ) AND
( Category LIKE #Category OR #Category IS NULL ) AND
( ProductDescription LIKE #ProductDescription OR #ProductDescription IS NULL )", conn);
if(!String.IsNullOrEmpty(this.Textbox_ProjectNr.Text.Trim()))
cmd.Parameters.AddWithValue("#Number", this.Textbox_ProjectNr.Text.Trim());
if(!String.IsNullOrEmpty(this.Textbox_Category.Text.Trim()))
cmd.Parameters.AddWithValue("#Category", this.Textbox_Category.Text.Trim());
if(!String.IsNullOrEmpty(this.Textbox_pDescription.Text.Trim()))
cmd.Parameters.AddWithValue("#ProductDescription", this.Textbox_pDescription.Text.Trim());
Also, you can add some client validation on user entered values. For instance, ask for more than three (?) characaters before running that query.
<asp:TextBox ID="Textbox_ProjectNr" runat="server" />
<asp:RegularExpressionValidator ID="Textbox_ProjectNr_Validator" runat="server"
ControlToValidate="Textbox_ProjectNr"
ErrorMessage="Minimum length is 3"
ValidationExpression=".{3,}" />
First of all, you must protect yourself from sql injections. You haven't specified what connection to the database you are using but most libraries allow adding the parameters in a different field, so they are sanitized automatically.
Secondly, you can (and should) limit the results count using the "LIMIT" (for mysql) or "TOP X" Like so:
Select * from TableName LIMIT 100 or Select TOP 100 * from TableName