Why won't this delete the data in my MySQL database!?
Private Sub Button4_Click(sender As System.Object, e As System.EventArgs) Handles Button4.Click
Dim dbCon As MySqlConnection
Dim strQuery As String = ""
Dim SQLCmd As MySqlCommand
Dim DR As MySqlDataReader
Try
dbCon = New MySqlConnection("Server=Localhost;Database=myusers;Uid=root;Pwd=Mypassword")
strQuery = "DELETE settings FROM settings WHERE user=" & Me.loginuser.Text
'* FROM settings WHERE user = "Testuser"'
SQLCmd = New MySqlCommand(strQuery, dbCon)
' OPEN THE DB AND KICKOFF THE QUERY
dbCon.Open()
DR = SQLCmd.ExecuteReader
While DR.Read
req1.Text = "" And exlink.Text = ""
End While
' DONE! Close DB
DR.Close()
dbCon.Close()
Catch ex As Exception
TextBox8.Text = ("Fail" & vbCrLf & vbCrLf & ex.Message)
End Try
Here is a picture of my database:
Alternatively I could somehow make it replace what is already in the database, in which case please help me with that.
Try
strQuery = "DELETE FROM settings " _
& " WHERE user = '" & Me.loginuser.Text & "'"
but as was stated earlier, you should be using parameterized queries. If you had a user named O'Brien then your query (as composed above) would fail because of the embedded single quote. When you use DELETE, you are deleting entire records and you already specify the table name in the FROM clause.
I will try to change your code in this way
Using con = New MySqlConnection("Server=.....")
con.Open()
Dim sqlText = "DELETE * FROM settings WHERE user = #ulogin"
Using cmd = new MySqlCommand(sqlText, con)
cmd.Parameters.AddWithValue("#ulogin", Me.loginuser.Text)
cmd.ExecuteNonQuery()
End Using
End Using
First and foremost, do not use string concatenation to create command texts to pass to the database engine. In that way you risk Sql Injections, also, if the user name contains a single quote (i.e. O'Hara) your code will fail with a syntax error (Same problems arise for date formatting, parsing numeric decimals and other globalization issues). Instead a parametrized query like the one in code above will avoid all of these problems.
In a parametrized query, the text of the query doesn't contains the actual value for the search condition or the update or insert data. Instead it contains placeholders ( in our case it is called #ulogin). The correct value to insert at the placeholders position is specified using one or more MySqlParameter added to the Parameters collection of the MySqlCommand. In this case I have used the AddWithValue method that derives the correct datatype directly from the datatype of the value. Because Me.loginuser.Text is a string value then the parameter will be treated as a string value replacing incorrect single quotes and removing extraneus characters usually used to Mount Sql Injections Attacks. The engine will do the rest inserting the correct value at the placeholder at execution time
EDIT: Seeing your comment about the MySql connector used, I will try to update my answer to show a semi-equivalent version for NET 1.
Try
Dim con As MySqlConnection = New MySqlConnection("Server=.....")
con.Open()
Dim sqlText as String = "DELETE * FROM settings WHERE user = #ulogin"
Dim cmd As MySqlCommand = new MySqlCommand(sqlText, con)
Dim par As MySqlParameter = New MySqlParameter("#ulogin", MySqlDbType.VarChar)
par.Value = Me.loginuser.Text
cmd.Parameters.Add(par)
cmd.ExecuteNonQuery()
Finally
con.Close()
End Try
I am not sure if the connector 1.0.10 supports the parameter name with the # prefix or just the : prefix
i dont think you can use double quotes in mysql, i think its single quotes only. try
Query = "DELETE * FROM settings WHERE user = '" & Me.loginuser.Text & "'"
Related
Can you please help me, what the problem of my code:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
'UPDATE Data
openCon()
Try
cmd.Connection = con
cmd.CommandText = "UPDATE emp_table SET FNAME = '" & TextBox1.Text & "', LNAME= '" & TextBox2.Text & "', AGE = '" & TextBox3.Text & "', GENDER ='" & Gender & "', OFFICE STAFF= '" & ComboBox1.Text & "' Where ID ='" & TxtID.Text & "' "
cmd.ExecuteNonQuery()
con.Close()
MsgBox("Suceessfully Updated Record")
TxtID.Clear()
TextBox1.Clear()
TextBox2.Clear()
TextBox3.Clear()
RBMale.Checked = False
RBFemale.Checked = False
ComboBox1.Text = ""
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
There are many problems in your code and if you look around this site I think you will find many partial answers that step by step will help you solve your problems. So I try to give you an answer where all the problems are examined, discussed and solved.
First problem: How do you handle the connection.
It seems that you have a global connection instance called con that you open with openCon. This is not a good approach and always a source of problems. You always need to check if the connection is closed properly or not. For example, in the code above you have forgot to close the connection in case of exception and this will lead to other exceptions in some code not related to this one. You keep resources on the server locked to you and this will decrease the performance of every one connection to that server.
I would change your openCon to this
Public Function openCon() as MySqlConnection
Dim con as MySqlConnection = new MySqlConnection(....here connection string ...)
con.Open()
return con
End Function
This will create a new instance of the MySqlConnection every time you call this method Now you can remove the global connection instance and use the one returned by openCon in this way
Using con As MySqlConnection = openCon()
.... code that use the local con object
End Using
This will close and destroy the connection even if an exception occurs inside the Using block and the ADO.NET libraries are smart enough to use a thing called Connection Pooling to reduce the time required to build and open a connection with the same connection string.
Second problem: The syntax error.
Looking at the point of the error suggested by the message I can see a field name composed by two words separated by a space. This is fine, but then you should remember that the sql parser cannot understand this and you need to help it enclosing the two words in a backtick character (ALT+096) so the parser understand that this is a single field name. Given the fact column names are an internal information of no concern for your end user then why use spaces in column names? If possible remove the space in column names.
Third problem: Sql Injection and other syntax errors
You are concatenating strings to build an sql command. But this is an easy target for wannabe hackers. Suppose that I write in your textBox1 this string instead of a First Name: Mario'; --
Then your command becomes
UPDATE emp_table SET FNAME = 'Mario'; -- xxxxxxxxxxxx
everything after -- is considered a comment and the query is still executable, but it changes every record in emp_table to have a first name equal to Mario.
But the query could fail as well if someone writes a Last Name that contains an apostrophe like O'Leary just now the query is no more syntactically valid.
The solution to this is always one. Use Parameters.
Recap of changes to your code.
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Dim cmdText as String = "UPDATE emp_table SET FNAME = #fname,
LNAME= #lname, AGE = #age, GENDER =#gender,
`OFFICE STAFF` = #staff
Where ID =#id"
Using con as MySqlConnection = openCon()
Using cmd as MySqlCommand = new MySqlCommand(cmdText, con)
Try
cmd.Parameters.Add("#fname", MySqlDbType.VarChar).Value = textBox1.Text
cmd.Parameters.Add("#lname", MySqlDbType.VarChar).Value = textBox2.Text
cmd.Parameters.Add("#age", MySqlDbType.VarChar).Value = textBox3.Text
cmd.Parameters.Add("#gender", MySqlDbType.VarChar).Value = gender
cmd.Parameters.Add("#staff", MySqlDbType.VarChar).Value = combobox1.Text
cmd.Parameters.Add("#id", MySqlDbType.VarChar).Value = txtID.Text
cmd.ExecuteNonQuery()
MsgBox("Suceessfully Updated Record")
TxtID.Clear()
TextBox1.Clear()
TextBox2.Clear()
TextBox3.Clear()
RBMale.Checked = False
RBFemale.Checked = False
ComboBox1.Text = ""
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Using
End Using
End Sub
In the recap I have added parameters for every single field that you want to update. But remember. Parameters should have a Type (The MySqlDbType here) that should match the type of the field and the value should be of the same type. For example it seems improbable that Age is a varchar field. So you should convert TextBox3.Text to an integer if the field is an integer.
What I want to happen is if, textbox3.Text does not equal data(0) value then I want the MsgBox("test") to trigger. However, it does not. If the value of textbox3 does not exist with data(0) I want MsgBox("test") to trigger. I've tried every variation I could think of and I cannot get it to work.
Right now, if textbox.Text does not equal data(0) value nothing happens. However, if textbox3.Text equals data(0) then both Label3.Text = data(1) and MsgBox("Join code has been applied.") work.
Dim conn As New MySqlConnection
conn.ConnectionString = "server=;userid=;password=;database="
conn.Open()
Dim sqlquery As String = "SELECT * FROM joincodes WHERE code = '" & TextBox3.Text & "';"
Dim data As MySqlDataReader
Dim adapter As New MySqlDataAdapter
Dim command As New MySqlCommand
command.CommandText = sqlquery
command.Connection = conn
adapter.SelectCommand = command
data = command.ExecuteReader
While data.Read()
If data.HasRows() = True Then
If TextBox3.Text = data(0) Then
Label3.Text = data(1)
MsgBox("Join code has been applied.")
Else
MsgBox("test")
End If
End If
End While
There are a few things that need to be changed in the code.
Database connections have "unmanaged resources" associated with them, which means that you have to .Dispose() of them when you have finished using them. To avoid some fiddly code, VB.NET conveniently provides the Using statement.
It is best to give controls meaningful names because it is much easier to see what is going on in the code. E.g. if you accidentally typed TextBox37 when you meant TextBox87 it would be hard to see, but you wouldn't mistype tbUserName for tbFavouriteColour.
In MySQL, CODE is a keyword, so you need to escape it with backticks to be safe: MySQL Keywords and Reserved Words
Putting variables directly into SQL statements is generally a mistake. SQL parameters are used for doing that; they are easy to use and prevent a lot of problems.
If you are relying on the order of the columns from a database query (e.g. data(0)) then you must specify that order in the query (e.g. SELECT `col1`, `col2` FROM joincodes) because if you use * then they could be returned in any order.
You are probably only interested in the first returned value from the database (if there is a returned value), so I added the ORDER BY `col1` LIMIT 1.
Always use Option Strict On. It will save you time.
With regard to the question as asked, all you need to do is have a flag, I used a boolean variable named success, to indicate if things went right.
I also added some points indicated with 'TODO: in the following code which you'll need to take care of to make sure it works properly:
Option Infer On
Option Strict On
Imports MySql.Data.MySqlClient
' ... (other code) ... '
'TODO: Any type conversion from the string TextBox3.Text.'
'TODO: Give TextBox3 a meaningful name.'
Dim userCode = TextBox3.Text
Dim connStr = "your connection string"
Using conn As New MySqlConnection(connStr)
'TODO: Use the correct column names.'
Dim sql = "SELECT `col1`, `col2` FROM `joincodes` WHERE `code` = #code ORDER BY `col1` LIMIT 1"
Using sqlCmd As New MySqlCommand(sql, conn)
'TODO: Use correct MySqlDbType and change .Size if applicable.'
sqlCmd.Parameters.Add(New MySqlParameter With {.ParameterName = "#code", .MySqlDbType = MySqlDbType.String, .Size = 24, .Value = userCode})
Dim success = False
Dim rdr = sqlCmd.ExecuteReader()
If rdr.HasRows Then
rdr.Read()
'TODO: Change GetString to the appropriate Get<whatever>.'
If rdr.GetString(0) = userCode Then
success = True
'TODO: Check that `col2` is a string - change the GetString call as required and call .ToString() on the result if needed.'
Label3.Text = rdr.GetString(1)
MessageBox.Show("Join code has been applied.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
End If
If Not success Then
MsgBox("test")
End If
End Using
End Using
I want parameterize some SQL Statements so my code isn't vunerable to SQL Injections any longer But i have actually no plan how to parameterize for example a where clause.
Dim accID As String = DatabaseConnecter.readField("SELECT ID FROM accounts WHERE accountname ='" & user & "' AND password='" & pw & "';")
The Problem is if you type in a given username, for example test and extend the username with. You can log in without entering the password into the Application.
Edit:
Public Function readField(ByVal sql As String) As String
Dim output As String = "ERROR"
Using cn = New MySqlConnection(connString.ToString())
Using cmd = New MySqlCommand(sql, cn)
cn.Open()
Using rd = cmd.ExecuteReader()
Try
rd.Read()
output = rd.GetString(0)
rd.Close()
Catch ex As Exception
End Try
End Using
cn.Close()
End Using
End Using
Return output
End Function
ยดยดยด
To have a parameterized query you need to create parameters and write a proper SQL text where, in place of values typed directly from your user, you have parameter placeholders.
So, for example, you sql text should be something like this
Dim sqlText = "SELECT ID FROM accounts WHERE accountname =#name AND password=#pwd"
Now you have a parameterized text, but stil we need to create the parameters that will be sent to the database engine together with your sql command.
You can create the parameter (two in this case) in this way before calling the method that executes the query
Dim p1 as MySqlParameter = new MySqlParameter("#name", MySqlDbType.VarChar)
p1.Value = user
Dim p2 as MySqlParameter = new MySqlParameter("#pwd", MySqlDbType.VarChar)
p2.Value = password
Dim pms As List(Of MySqlParameter) = new List(Of MySqlParameter)()
pms.Add(p1)
pms.Add(p2)
Now we need to pass this list to your method (and this requires changes to your method signature)
DatabaseConnecter.readField(sqlText, pms)
The method itself should change to something like
Public Function readField(ByVal sql As String, Optional pms As List(Of MySqlParameter) = Nothing) As String
Dim output As String = "ERROR"
Using cn = New MySqlConnection(connString.ToString())
Using cmd = New MySqlCommand(sql, cn)
cn.Open()
' This block adds the parameter defined by the caller to the command
' The parameters are optional so we need to check if we have really received the list or not
if pms IsNot Nothing Then
cmd.Parameters.AddRange(pms.ToArray())
End If
Using rd = cmd.ExecuteReader()
Try
rd.Read()
output = rd.GetString(0)
rd.Close()
Catch ex As Exception
End Try
End Using
' no need to close when inside a using block
' cn.Close()
End Using
End Using
Return output
End Function
The method now has an optional parameter that will contain the list of the parameters required by the query (or nothing if your query doesn't require parameters). This list is added to the command parameters collection and the query is now executed.
Final Note: Storing passwords in clear text into a database is a well known security problem. I suggest you to search about how to store passwords in a database.
Private Function GetID(User As String, pw As String) As String
Using cmd As New SqlCommand("SELECT ID FROM accounts WHERE accountname =#user AND password=#password", New SqlConnection(SQLConnString))
cmd.Parameters.AddWithValue("#user", User)
cmd.Parameters.Add("#password", SqlDbType.NVarChar)
cmd.Parameters("#password").Value = pw
Try
cmd.Connection.Open()
Return cmd.ExecuteScalar()
Catch ex As Exception
'handle error
Return Nothing
Finally
cmd.Connection.Close()
End Try
End Using
End Function
I've demostrated two methods of setting the parameters. Search for more info or comparison.
I've searched around the web looking for samples on how to use MySqlHelper.UpdateDataSet but all I've found is:
Public Shared Sub UpdateDataSet( _
ByVal connectionString As String, _
ByVal commandText As String, _
ByVal ds As DataSet, _
ByVal tablename As String _
)
I'll be grateful if someone will give me:
an example of commandText because I didn't understand what kind of command I need to give;
an explanation of tablename because I need to know if is the name of a table of the DB or of the DataSet or both (with the same name);
a vb.net code example (to start testing).
I tryed to use the command this way:
Private Sub Btn_Mod_Dataset_Click(sender As Object, e As EventArgs) Handles Btn_Mod_Dataset.Click
Dim SqlStr$ = "SELECT * FROM MyTest.Users"
Using DS_Test As DataSet = DB_Functions.mQuery(SqlStr)
With DS_Test
.Tables(0).TableName = "Users"
Dim User$ = .Tables(0).Rows(0)("UserName").ToString
MsgBox(User)
.Tables(0).Rows(0)("User") = "Upd_Test"
User = .Tables(0).Rows(0)("UserName").ToString
MsgBox(User)
End With
Dim DB_Name = "MyTest"
Dim connectionString$ = "datasource=localhost;username=" + UserDB _
+ ";password=" + Password + ";database=" + DB_Name
MySqlHelper.UpdateDataSet(connectionString, _
"Update MyTest.Users Set UserName = 'Test_Ok' WHERE UserName = 'Steve'", _
DS_Test, "Users")
End Using
End Sub
This gives me
System.NullReferenceException' in System.Data.dll
EDIT (to explain my code):
a) DB_Functions is a sepate class where I've stored some function to use on a MySql DataBase. mQuery is a function who extract query result into a dataset;
b) 'User' is a field Name : I've changed it to 'UserName' but same result;
d) The code between With and End With is just a test to see what happens;
NOTE that the code gives error but my DB is updated as in the commandText String. I don't understand what happens
This might get you part of the way.
First get rid of DB_Functions. MySQLHelper has a method to create the DataSet for you; in general, db Ops are so query-specific that there is very little that is generic and reusable. The exception to this is building the ConnectionString: MySQL has gobs of cool options you can enable/disable via the connection string. But for that you just need the standard MySqlConnectionStringBuilder.
Build a DataSet:
' form/class level vars
Private dsSample As DataSet
Private MySqlConnStr As String = "..."
...
Dim SQL = "SELECT Id, FirstName, Middle, LastName FROM Employee"
Using dbcon As New MySqlConnection(MySQLConnStr)
dsSample = MySqlHelper.ExecuteDataset(dbcon, SQL)
dsSample.Tables(0).TableName = "Emps"
End Using
There does not appear to be a way to specify a tablename when you build it, so that is a separate step.
Update a Record
To update a single row, you want ExecuteNonQuery; this will also allow you to use Parameters:
Dim uSQL = "UPDATE Employee SET Middle = #p1 WHERE Id = #p2"
Using dbcon As New MySqlConnection(MySQLConnStr)
Dim params(1) As MySqlParameter
params(0) = New MySqlParameter("#p1", MySqlDbType.String)
params(0).Value = "Q"
params(1) = New MySqlParameter("#p2", MySqlDbType.Int32)
params(1).Value = 4583
dbcon.Open()
Dim rows = MySqlHelper.ExecuteNonQuery(dbcon, uSQL, params)
End Using
Again, this is not really any simpler than using a fully configured DataAdapter, which would be simply:
dsSample.Tables("Emps").Rows(1).Item("Middle") = "X"
daSample.Update(dsSample.Tables("Emps"))
I am not exactly sure what value the UpdateDataSet method adds. I think it is the "helper" counterpart for the above, but since it doesn't provide for Parameters, I don't have much use for it. The docs for it are sketchy.
The commandtext would appear to be the SQL for a single row. Note that the DataAdapter.Update method above would add any new rows added, delete the deleted ones and update values for any row with changed values - potentially dozens or even hundreds of db Ops with one line of code.
My funtion gets string query and returns datatable. So u can set dataset.tables .
Public Function mysql(ByVal str_query As String) As DataTable
Dim adptr As New MySqlDataAdapter
Dim filltab As New DataTable
Try
Using cnn As New MySqlConnection("server=" & mysql_server & _
";user=" & mysql_user & ";password=" & mysql_password & _
";database=" & mysql_database & ";Allow User Variables=True")
Using cmd As New MySqlCommand(str_query, cnn)
cnn.Open()
adptr = New MySqlDataAdapter(cmd)
adptr.Fill(filltab)
cnn.Close()
End Using
End Using
Catch ex As Exception
'you can log mysql errors into a file here log(ex.ToString)
End Try
Return filltab
End Function
I am trying to delete the users username from the database when s/he logs out. My code is the following which doesn't work. I Think it may have something to do with the connection string (database, password thing)
Private Sub Button1_Click_1(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim Query As String
Dim con As MySqlConnection = New MySqlConnection("Server=localhost;User ID=root;Password=*Mypassword*;Database=myusers")
con.Open()
Query = "Delete FROM users WHERE name =" + loginuser.Text
'Table = users
'Name = Varchar(20)
'loginuser.text = Name (username)
Dim cmd As MySqlCommand = New MySqlCommand(Query, con)
MsgBox(Query)
Dim i As Integer = cmd.ExecuteNonQuery()
If (i > 0) Then
MsgBox("Record is Successfully Deleted")
Else
MsgBox("Record is not Deleted")
End If
con.Close()
End Sub
You are not enclosing name value with quotes in your sql string,
Ex: Delete FROM users WHERE name = 'abcname'
Change your code to use parameters which is clean and secure way to pass values and you don't have to worry about quotes when working with string parameters
Query = "Delete FROM users WHERE name = #name"
Dim cmd As MySqlCommand = New MySqlCommand(Query, con)
MySqlCommand.Parameters.AddWithValue("#name",loginuser.Text)
String literals need to be enclosed in single quotes so your query should be:
Query = "Delete FROM users WHERE name = '" + loginuser.Text + "'"
Also concatenating a string into a SQL statement opens you up to SQL injection attacks (or even just think about someone putting "O'Brien" as the login name). You should always use parameters instead.