SQLite REAL error - sqlite

I am having some trouble with SQLite. I used to save "time" as an int, but i now want it as a double, which is causing some problems. I remake my database everytime i try a new fix, but it has no effect. Whenever i try to save, it get the error "4 values for 3 columns". when i change it all back to an int, it works fine again. Also, if i just remove "time all togeater from the saving process, i get the error "2 values for 3 columns". I have no clue what is going on. Does a real take up two values or whats the deal?
Creating the table:
sql = "CREATE TABLE Speed (id INTEGER PRIMARY KEY AUTOINCREMENT, LanguageType VARCHAR(20), cardsetNumber INT, time REAL)";
command = new SQLiteCommand(sql, dbConnection);
command.ExecuteNonQuery();
Adding to the table:
public void SaveSpeed(int cardsetNumber, LanguageType lt, double time)
{
Open();
sql = "insert into Speed (cardsetNumber, LanguageType, time) values (" + cardsetNumber + ", '" + lt + "', " + time + ")";
command = new SQLiteCommand(sql, dbConnection);
command.ExecuteNonQuery();
Close();
}

According to Wikipedia, Denmark uses a comma as a decimal separator. Most probably, the default culture of your process is the Danish one that imposes exactly that decimal separator when converting decimal numbers to strings.
You are doing two things that you shouldn't be doing in your code, though in this case, changing one will fix the problem:
You convert a value to a string based on a culture that is not the invariant culture for any other purpose than displaying it to the user.
You are constructing an SQL query with custom values by concatenating the values unescaped into the query string.
The first means that code that does not expect to read locale-specific number formats will simply interpret your string differently than intended. In particular, that is true when reading the string on another machine (that has another default locale set), but in this case, it is simply because the SQL parser does never expect any locale-specific number formats (and, as the comma serves as the argument separator, the syntax would actually stop being well-defined unless the parser would add any further locale-specific amendments).
The second can cause really bad trouble, so you are best served by always avoiding it.
So, what is happening here? With that locale-specific conversion, the time number is converted to something like 1,2, which is interpreted by the SQL parser as two integer numbers (separated with a comma), not as one decimal number. Hence the error message about the argument count.
The first problem could be solved by explicitly specifying the invariant culture for string conversions when the result is targeted at programs rather than humans. Always do that when you, or someone else, intends to read the value back into a number type, for example, when storing it in a text-based format for computers such as CSV or XML.
As for the second issue, to insert any custom (literal) values into your SQL query, (especially, but not exclusively, those that are not hard-coded somewhere but received from user-input), parametrize your query string:
sql = "insert into Speed (cardsetNumber, LanguageType, time) values (#cardsetNumber, #lt, #time)";
command = new SQLiteCommand(sql, dbConnection);
command.Parameters.AddWithValue("#cardsetNumber", cardsetNumber);
command.Parameters.AddWithValue("#lt", lt);
command.Parameters.AddWithValue("#time", time);
This will automatically insert any quotation or escape marks to the literal values, and there is no risk of intentional or accidental SQL injection.

Related

SQLite treating hyphens as arithmetic operators

In a React Native App I'm attempting to insert data into a local sqlite db
let submissionID = "1-2-3";
this.dbQuery("INSERT INTO Submissions (ID, Data) VALUES("+submissionID+",'Test')");
(dbQuery is the name of a function I made to simplify my queries but the statement inside it should be the same)
If I viewed the Submissions table after this insert statement I would expect to see a row with [ID:"1-2-3",Data:"Test"] but instead I see [ID:"-4",Data:"Test"]
I created the table like so
CREATE TABLE IF NOT EXISTS Submissions(ID BLOB PRIMARY KEY NOT NULL, Data BLOB NOT NULL)
I used Blob because I read "The value is a blob of data, stored exactly as it was input." but I've also tried Text. I've also casted submissionID as a string like so
this.dbQuery("INSERT INTO Submissions (ID, Data) VALUES("+String(submissionID)+",'Test')");
But none of that worked. I do see here how sqlite takes advantage of arithmetic operators
https://www.w3resource.com/sqlite/arithmetic-operators.php
but I'm not sure how to stop it from doing so.
How would I get sqlite to treat my hyphens as hyphens instead of subtraction signs?
What you're doing is the equivalent of:
this.dbQuery("INSERT INTO Submissions (ID, Data) VALUES(1-2-3,'Test')");
passing the numeric expression 1-2-3 to the INSERT statement. The simplest fix is to quote the string literal.
let submissionID = "1-2-3";
this.dbQuery("INSERT INTO Submissions (ID, Data) VALUES('"+submissionID+"','Test')");
However, to guard against SQL injection attacks, you really ought to be using prepared statements instead of using string concatenation to build SQL statements.
Enclose the string in single quotes i.e.
this.dbQuery("INSERT INTO Submissions (ID, Data) VALUES('"+String(submissionID)+"','Test')");
Thus the value is treated as a literal by SQLite, without enclosing the value it will either be treated as a numeric value or as an identifier (column, table, trigger, view depending upon where it is coded and thus what the parser expects).
The data type (column affinity) has little bearing other than if you specified ID INTEGER PRIMARY KEY, then you could not store anything other than an integer. As ID INTEGER PRIMARY key has a special interpretation that is the column is an alias of the rowid.
I used Blob because I read "The value is a blob of data, stored
exactly as it was input." but I've also tried Text. I've also casted
submissionID as a string like so
That is only if the value to be inserted is a BLOB byte[] or in the case of raw SQL x'FF01FE02', otherwise SQLite will store the value according to how it interprets the type should be stored.

Using ADO through ODBC MariaDB Connector, I can't seem to return the decimal properly

I recently migrated from MySQL to MariaDB and my prices were off by two decimal places. I've done a check and the column for price had it's type set to decimal(19,4) so that I could have four decimal points of accuracy if I needed it.
Logged into MariaDB using a select statement the prices are okay.
Logging in with heidisql also shows that the prices are okay.
Looking at the linked table that uses the same ODBC connection I'm trying to use is also correct.
So I've concluded that there is something with connecting via ADO and the ODBC connector.
I looked and I found that if I cast my sql statement as decimal(6,2) then the decimals appear properly, so I recast the table directly within mariadb.
However I noticed that all the prices appear correctly, except for the one that has two zeros in the decimal places.
The value is 5.00 and what I get returned is 0.05
I'm not sure where I'm going wrong but this means that any round number or zeros won't keep their place. How do I fix this problem? Is it the way my column is cast, or the way vba:ado interprets what it recieves, or is it what the odbc connector returns?
This is the code I am using in order to try to debug this problem:
Public Sub decimalcheck()
Dim db As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim constring As String
constring = "DSN=my_dsn;"
db.Open constring, "user", "pass"
rs.ActiveConnection = db
rs.Source = "select Prices FROM my_table "
rs.Source = "select cast(my_prices as decimal(6,2) ) FROM my_table"
rs.Open
rs.MoveFirst
Do While Not rs.EOF And Not rs.BOF
Debug.Print rs.Fields(0)
Debug.Print CDec(rs.Fields(0))
rs.MoveNext
Loop
rs.Close
end sub
Update
I'm having some results casting as double, so no I need to research if it's worth keeping it as decimal or going to double.
Okay, Here are my results:
So my assumption is that ado, (or vba) is more used to using double and isn't really able to interpret decimal correctly. So that leaves us with a question:
What's the difference between Decimal and Double
I didn't find much. There is a difference in how big the numbers can be, but if you are going to provide a limit such as decimal(19,4) or double(19,4) then that doesn't matter unless you are beyond one of the two's ranges, then you are kind of stuck using the other, or finding a different solution. So for me this is not a big deal.
It seems that people are saying that decimal are more precise than double. Not sure what that means, I guess scientific precision. I'm working with money, so maybe I'll settle for decimal in my case, even though it may not be required, and probably overkill. (See Vladislav Vaintroub Comment, Decimal is absolutely precise if it fits within the M,D range. Thus use it with figures representing money.)
I see two solutions but there are probably many
First you can just change the decimal to double and leave it at that.
Second, you keep the column as a decimal and then in the select statement cast it as double when using it with ADO. Sample Code:
SELECT CAST( column_name AS DOUBLE(19,4) ) FROM my_table
Sorry for asking a question only to figure it out a few moments later. Hopefully it helps others, if not... well delete it.

Strange issue with Date.Parse / ParseExact not altering date (ASP.Net)

I have a bit of a head scratcher with the Date.Parse /ParseExact functionality in VB.
To surmise, I have an ASP.Net 4.0 app, on one of the pages there is a calendar control which the user chooses a date and time, these are fed into a string (strReqDeadline) which takes the following European / UK date time format: dd/MM/yyyy HH:mm:ss.fff
So for example the contents of strReqDeadline would be: 29/03/2013 16:30:00.000
I then need to insert this into a SQL datetime column, so obviously it needs converted from UK to the US/datetime format. I've been attempting to do this with Date.Parse and Date.ParseExact with no success. The following should work according to the research I've done:
strReqDeadline = "29/03/2013 16:30:00.000"
Dim usDate = As Date = Date.ParseExact(strReqDeadline, "dd/MM/yyyy HH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture)
However, what actually happens at runtime is bizzare, the Date.ParseExact function trims off the fractal seconds from the time (as far as I can see it shouldn't be doing this because the filter specifies .fff), and otherwise leaves the entire string completely unchanged.
So, if the value of usDate is output, it appears as follows: 29/03/2013 16:30:00
What it should contain is datetime: 3/29/2013 4:30PM
The really strange thing is if I put a watch on usDate and start the app, in the development environment its value shows as #3/29/2013 4:30PM#, both in the watch list and when hovered over in the source window, but any form of output displays the original string, just minus the fractions of second, and doesn't convert to datetime.
From what I read the 'InvariantCulture' specification should negate any locale specific issues with output, but just in case this were the issue I also tried specifying an explicit local culture with System.Globalization.CultureInfo.CreateSpecificCulture("en-GB") (tried fr-FR too), but this makes no difference. The Windows regional settings on both the client and server are set to UK if this bears any relevance.
Maybe I'm missing something very obvious but I just can't see why I'm getting this output, Date.ParseExact doesn't throw any exceptions or complain about the string not being recognised, but I'm struggling to understand why it just removes the fraction seconds and does nothing else, especially since the input spring matches the specified mask exactly.
I'd be very interested to hear if anyone else has experienced an odd issue like this and what you did with it!
Thanks :)
EDIT: Full code with SQL section is as follows:
strReqDeadline = "29/03/2013 16:30:00.000"
Dim usDate As Date = Date.ParseExact(strReqDeadline, "dd/MM/yyyy HH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture)
'SQL
Dim con As New Data.SqlClient.SqlConnection("data source=XXXXX;initial catalog=YYYYY;Integrated Security=True")
Dim cmd As New Data.SqlClient.SqlCommand()
cmd.Connection = con
cmd.CommandText = "INSERT INTO Requests (ReqOwnerID, ReqDeadline, ReqStatus)" _
& "VALUES ('" & UserID & "', '" & usDate & "', '1')"
con.Open()
Dim NewReqID = cmd.ExecuteScalar()
con.Close()
'
Why is it you thin it is not working? These are all the same underlying date/time:
29/03/2013 16:30:00.000
29/03/2013 16:30:00
3/29/2013 4:00PM
You cannot rely on what hovering over a non-string variable shows to determine its inner value. All you are seeing is the evaluation of ToString(). If you want a String to show the fractions seconds, then you need to call ToString() and specify the format "dd/MM/yyyy HH:mm:ss.fff". By default a DateTime type if not going to show your fractions seconds when you convert to a String.
If you are not using parameters (and you should be) then your final SQL statement after injecting the DateTime would be something like this:
INSERT INTO MyTableWithDate
(column1
,column2
,MyDateCol)
VALUES
('a'
,'b'
,'20130329 16:30:00.557')
As I mentioned before, a Date datatype is not String. It's an object (or a rather a DateTime structure, by I digress). You must call the correct ToString() meth0d.
Try using this withing your SQL string:
& "VALUES ('" & UserID & "', '" & usDate.ToString("yyyyMMdd HH:mm:ss.fff") & "', '1')"
Of course, there is little point converting a string to Date object to immediately convert it back to a string again, but this code should work.
usDate is an object of type DateTime, and it appears to be storing the correct value. When you are inspecting it, you are seeing a string representation of that datetime value. It doesn't contain either 29/03/2013 16:30:00 or 3/29/2013 4:30PM, those are just two valid representations of what it contains.
You say
any form of output displays the original string
This is not true. In fact you have control over how it is output when you call ToString(), where a format can be specified
What you are doing looks correct i.e. using Date.ParseExact to convert a date in UK format to a Date type. The issue you are having is that when it displays this as a string it is displaying it in your local culture (the debugger appears to always want to display in US format), but the Date you have set is correct.

SQL query not working as expected

I am using the standard ASP.NET Membership table structure in SQL Server and was doing a bit of manually querying in Management studio and ran this query
SELECT *
FROM [aspnet_Users]
WHERE UserId = '2ac5dd56-2630-406a-9fb8-d4445bc781da&cID=49'
Notice the &cID=49 at the end - I copied this from a querystring and forgot to remove that part.
However, to my surprise it returned the data correctly (there is a user with the ID 2ac5dd56-2630-406a-9fb8-d4445bc781da) - any idea why this works? To my mind it should not match or probably more likely throw an error as it shouldn't be able to convert to a Guid?
The uniqueidentifier type is considered a character type for the purposes of conversion from a character expression, and therefore is subject to the truncation rules for converting to a character type. That is, when character expressions are converted to a character data type of a different size, values that are too long for the new data type are truncated.
Because the uniqueidentifier type is limited to 36 characters, the characters that exceed that length are truncated.
Note that above is quoted from MSDN
The parser is (remarkably) lenient when converting string literals to guid literals, apparently:
SELECT CAST('E63F4FFC-8574-428B-B6B8-95CFCA05ED52' AS uniqueidentifier)
SELECT CAST('E63F4FFC-8574-428B-B6B8-95CFCA05ED52a' AS uniqueidentifier)
SELECT CAST('E63F4FFC-8574-428B-B6B8-95CFCA05ED52-!' AS uniqueidentifier)
SELECT CAST('E63F4FFC-8574-428B-B6B8-95CFCA05ED52~#5' AS uniqueidentifier)
SELECT CAST('E63F4FFC-8574-428B-B6B8-95CFCA05ED52$3%] ' AS uniqueidentifier)
all give the same result, no errors.
This is documented behaviour, so we can't really complain:
The following example demonstrates the truncation of data when the
value is too long for the data type being converted to. Because the
uniqueidentifier type is limited to 36 characters, the characters that
exceed that length are truncated.
DECLARE #ID nvarchar(max) = N'0E984725-C51C-4BF4-9960-E1C80E27ABA0wrong';
SELECT #ID, CONVERT(uniqueidentifier, #ID) AS TruncatedValue;
Here is the result set.
String TruncatedValue
-------------------------------------------- ------------------------------------
0E984725-C51C-4BF4-9960-E1C80E27ABA0wrong 0E984725-C51C-4BF4-9960-E1C80E27ABA0
(1 row(s) affected)

Converting Decimal In a Label to an Integer

Currently using VS2008, VB.NET, SQL.
I have a FormView from a Data Source that is getting some fields that are stored as Decimals in the SQL Database.
I am grabbing the field from the FormView as such:
Dim AvgTicketL As Label = CType(frmMerchantProfile.FindControl("F10Label"), Label)
I need to take this value, and convert it to an Integer, then send it along to an API. I have the API Calls done, tested and working, but I'm getting an error as when it is getting this value, the API is returning "Must be an Integer" error.
What I have tried so far:
Dim AvgTicketL As Label = CType(frmMerchantProfile.FindControl("F10Label"), Label)
Dim AvgTicket1 As Integer
AvgTicket1 = Double.Parse(AvgTicket.Text)
Do something with AvgTicket1
I have also attempted to Round the Value, then convert it and call it - no luck.
Checking the value of AvgTicket1 (Writing it out to a Label or Response.Write) shows "100", where the database value was 100.00. But the API is still getting 100.00, apparently. Any other conversion method that I've attempted states errors that the Label cannot be converted to Integer.
What are some methods I can successfully convert this value to an integer from a label?
The title of your question and the text of your question point to two different things.
Assuming you want to know how to safely convert the decimal value retrieved from the database, which is presumably the value of AvgTicketL, before calling your API you would do the following:
Create a variable of datatype Integer and use System.Int32.TryParse() to safely convert the decimal to an integer. Then pass that variable. (Code coming)
Dim testInt as Integer = -1
If System.Int32.TryParse(AvgTicketL.Text, testInt) Then
' Do something with testInt - call the API using the value
Else
' code to execute if the parse fails.
' This could be whatever you need the code to do if the value of AvgTicketL.Text can't be properly parsed into an Int value.
End If
After some fooling around this is what I was able to do to get this to work...
I took some of what David had said, and then just made a simple adjustment - I don't know why I hadn't thought of it earlier!
Dim AvgTicketL As Label = CType(frmMerchantProfile.FindControl("F10Label"), Label)
Dim AvgTicketI As Integer = "-1"
I dimmed a second variable as an int, then
AvgTicketI = CInt(AvgTicketL.Text)
From there, I just called AvgTicketI as the variable to pass to the API. Worked!
Thanks, David, for the guidance!

Resources