Syntax error in update statement using an MS Access database - sql

I have inserted my record successfully with the OleDbCommand and parameters, but if I update the record I get the following error:
Syntax Error!
I have tried put the square brackets around the Field Name, and it still does not work. But if I copy the command to update with Access query, it works.
Code:
Public Function update() As Boolean
Dim STATE As Boolean = False
Dim cmd As New OleDbCommand
cmd.Connection = cn
cmd.CommandType = CommandType.Text
cmd.CommandText = "UPDATE [GUEST_DATA_TBL] SET [USD]=#USD, [RIEL]=#RIEL, [EURO]=#EURO, [BAHT]=#BAHT, [AUSD]=#AUSD, [GIFT]=#GIFT, [MEMO]=#MEMO WHERE [ID]=#ID"
AssignParams(cmd)
cmd.ExecuteNonQuery()
Return STATE
End Function
'the parameters
Private Sub AssignParams(cmd As OleDbCommand)
cmd.Parameters.AddWithValue("#ID", ID)
cmd.Parameters.AddWithValue("#USD", USD)
cmd.Parameters.AddWithValue("#RIEL", RIEL)
cmd.Parameters.AddWithValue("#BAHT", BAHT)
cmd.Parameters.AddWithValue("#EURO", EURO)
cmd.Parameters.AddWithValue("#AUSD", AUSD)
cmd.Parameters.AddWithValue("#GIFT", GIFT)
cmd.Parameters.AddWithValue("#MEMO", MEMO)
End Sub

First it's good to see that you are already using parameters but with MS Access, the order of the parameters is important not the names. I use the ? placeholder within my SQL command when using parameters. I also specify the data type so consider using the OleDbParameter Constructor (String, OleDbType) to add your parameters.
I would also consider implementing Using:
Managed resources are disposed of by the .NET Framework garbage collector (GC) without any extra coding on your part. You do not need a Using block for managed resources. However, you can still use a Using block to force the disposal of a managed resource instead of waiting for the garbage collector.
You could implement a check for the value returned by ExecuteNonQuery() to see how many rows were effected.
Lastly, with VB.NET by not specifying a modifier here; Private Sub AssignParams(cmd As OleDbCommand), the compiler by default will use ByVal:
Specifies that an argument is passed in such a way that the called procedure or property cannot change the value of a variable underlying the argument in the calling code.
You should be using ByRef:
Specifies that an argument is passed in such a way that the called procedure can change the value of a variable underlying the argument in the calling code.
Your code would look something like this:
Public Function update() As Boolean
Dim STATE As Boolean = False
Using con As OleDbConnection = cn,
cmd As New OleDbCommand("UPDATE [GUEST_DATA_TBL] SET [USD]=?, [RIEL]=?, [EURO]=?, [BAHT]=?, [AUSD]=?, [GIFT]=?, [MEMO]=? WHERE [ID]=?", con)
con.Open()
AssignParams(cmd)
Dim rowsAffected As Integer = cmd.ExecuteNonQuery()
If rowsAffected > 0 Then
STATE = True
End If
End Using
Return STATE
End Function
Private Sub AssignParams(ByRef cmd As OleDbCommand)
cmd.Parameters.Add("#USD", OleDbType.[Type]).Value = USD
cmd.Parameters.Add("#RIEL", OleDbType.[Type]).Value = RIEL
cmd.Parameters.Add("#EURO", OleDbType.[Type]).Value = EURO
cmd.Parameters.Add("#BAHT", OleDbType.[Type]).Value = BAHT
cmd.Parameters.Add("#AUSD", OleDbType.[Type]).Value = AUSD
cmd.Parameters.Add("#GIFT", OleDbType.[Type]).Value = GIFT
cmd.Parameters.Add("#MEMO", OleDbType.[Type]).Value = MEMO
cmd.Parameters.Add("#ID", OleDbType.[Type]).Value = ID
End Sub
Note that I have used OleDbType.[Type]. You will want to replace [Type] with the data type you've used on your database.

Private Sub AssignParams(cmd As OleDbCommand)
cmd.Parameters.AddWithValue("#USD", USD)
cmd.Parameters.AddWithValue("#RIEL", RIEL)
cmd.Parameters.AddWithValue("#EURO", EURO)
cmd.Parameters.AddWithValue("#BAHT", BAHT)
cmd.Parameters.AddWithValue("#AUSD", AUSD)
cmd.Parameters.AddWithValue("#GIFT", GIFT)
cmd.Parameters.AddWithValue("#MEMO", MEMO)
cmd.Parameters.AddWithValue("#ID", ID)
End Sub

Related

How to extract data from database access from a form and display on another form as receipt when button print is clicked in vb.net [duplicate]

I've heard that "everyone" is using parameterized SQL queries to protect against SQL injection attacks without having to vailidate every piece of user input.
How do you do this? Do you get this automatically when using stored procedures?
So my understanding this is non-parameterized:
cmdText = String.Format("SELECT foo FROM bar WHERE baz = '{0}'", fuz)
Would this be parameterized?
cmdText = String.Format("EXEC foo_from_baz '{0}'", fuz)
Or do I need to do somethng more extensive like this in order to protect myself from SQL injection?
With command
.Parameters.Count = 1
.Parameters.Item(0).ParameterName = "#baz"
.Parameters.Item(0).Value = fuz
End With
Are there other advantages to using parameterized queries besides the security considerations?
Update: This great article was linked in one of the questions references by Grotok.
http://www.sommarskog.se/dynamic_sql.html
The EXEC example in the question would NOT be parameterized. You need parameterized queries (prepared statements in some circles) to prevent input like this from causing damage:
';DROP TABLE bar;--
Try putting that in your fuz variable (or don't, if you value the bar table). More subtle and damaging queries are possible as well.
Here's an example of how you do parameters with Sql Server:
Public Function GetBarFooByBaz(ByVal Baz As String) As String
Dim sql As String = "SELECT foo FROM bar WHERE baz= #Baz"
Using cn As New SqlConnection("Your connection string here"), _
cmd As New SqlCommand(sql, cn)
cmd.Parameters.Add("#Baz", SqlDbType.VarChar, 50).Value = Baz
Return cmd.ExecuteScalar().ToString()
End Using
End Function
Stored procedures are sometimes credited with preventing SQL injection. However, most of the time you still have to call them using query parameters or they don't help. If you use stored procedures exclusively, then you can turn off permissions for SELECT, UPDATE, ALTER, CREATE, DELETE, etc (just about everything but EXEC) for the application user account and get some protection that way.
Definitely the last one, i.e.
Or do I need to do somethng more extensive ...? (Yes, cmd.Parameters.Add())
Parametrized queries have two main advantages:
Security: It is a good way to avoid SQL Injection vulnerabilities
Performance: If you regularly invoke the same query just with different parameters a parametrized query might allow the database to cache your queries which is a considerable source of performance gain.
Extra: You won't have to worry about date and time formatting issues in your database code. Similarly, if your code will ever run on machines with a non-English locale, you will not have problems with decimal points / decimal commas.
You want to go with your last example as this is the only one that is truly parametrized. Besides security concerns (which are much more prevalent then you might think) it is best to let ADO.NET handle the parametrization as you cannot be sure if the value you are passing in requires single quotes around it or not without inspecting the Type of each parameter.
[Edit] Here is an example:
SqlCommand command = new SqlCommand(
"select foo from bar where baz = #baz",
yourSqlConnection
);
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "#baz";
parameter.Value = "xyz";
command.Parameters.Add(parameter);
Most people would do this through a server side programming language library, like PHP's PDO or Perl DBI.
For instance, in PDO:
$dbh=pdo_connect(); //you need a connection function, returns a pdo db connection
$sql='insert into squip values(null,?,?)';
$statement=$dbh->prepare($sql);
$data=array('my user supplied data','more stuff');
$statement->execute($data);
if($statement->rowCount()==1){/*it worked*/}
This takes care of escaping your data for database insertion.
One advantage is that you can repeat an insert many times with one prepared statement, gaining a speed advantage.
For instance, in the above query I could prepare the statement once, and then loop over creating the data array from a bunch of data and repeat the ->execute as many times as needed.
Your command text need to be like:
cmdText = "SELECT foo FROM bar WHERE baz = ?"
cmdText = "EXEC foo_from_baz ?"
Then add parameter values. This way ensures that the value con only end up being used as a value, whereas with the other method if variable fuz is set to
"x'; delete from foo where 'a' = 'a"
can you see what might happen?
Here's a short class to start with SQL and you can build from there and add to the class.
MySQL
Public Class mysql
'Connection string for mysql
Public SQLSource As String = "Server=123.456.789.123;userid=someuser;password=somesecurepassword;database=somedefaultdatabase;"
'database connection classes
Private DBcon As New MySqlConnection
Private SQLcmd As MySqlCommand
Public DBDA As New MySqlDataAdapter
Public DBDT As New DataTable
Public BindSource As New BindingSource
' parameters
Public Params As New List(Of MySqlParameter)
' some stats
Public RecordCount As Integer
Public Exception As String
Function ExecScalar(SQLQuery As String) As Long
Dim theID As Long
DBcon.ConnectionString = SQLSource
Try
DBcon.Open()
SQLcmd = New MySqlCommand(SQLQuery, DBcon)
'loads params into the query
Params.ForEach(Sub(p) SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value))
'or like this is also good
'For Each p As MySqlParameter In Params
' SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value)
' Next
' clears params
Params.Clear()
'return the Id of the last insert or result of other query
theID = Convert.ToInt32(SQLcmd.ExecuteScalar())
DBcon.Close()
Catch ex As MySqlException
Exception = ex.Message
theID = -1
Finally
DBcon.Dispose()
End Try
ExecScalar = theID
End Function
Sub ExecQuery(SQLQuery As String)
DBcon.ConnectionString = SQLSource
Try
DBcon.Open()
SQLcmd = New MySqlCommand(SQLQuery, DBcon)
'loads params into the query
Params.ForEach(Sub(p) SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value))
'or like this is also good
'For Each p As MySqlParameter In Params
' SQLcmd.Parameters.AddWithValue(p.ParameterName, p.Value)
' Next
' clears params
Params.Clear()
DBDA.SelectCommand = SQLcmd
DBDA.Update(DBDT)
DBDA.Fill(DBDT)
BindSource.DataSource = DBDT ' DBDT will contain your database table with your records
DBcon.Close()
Catch ex As MySqlException
Exception = ex.Message
Finally
DBcon.Dispose()
End Try
End Sub
' add parameters to the list
Public Sub AddParam(Name As String, Value As Object)
Dim NewParam As New MySqlParameter(Name, Value)
Params.Add(NewParam)
End Sub
End Class
MS SQL/Express
Public Class MSSQLDB
' CREATE YOUR DB CONNECTION
'Change the datasource
Public SQLSource As String = "Data Source=someserver\sqlexpress;Integrated Security=True"
Private DBCon As New SqlConnection(SQLSource)
' PREPARE DB COMMAND
Private DBCmd As SqlCommand
' DB DATA
Public DBDA As SqlDataAdapter
Public DBDT As DataTable
' QUERY PARAMETERS
Public Params As New List(Of SqlParameter)
' QUERY STATISTICS
Public RecordCount As Integer
Public Exception As String
Public Sub ExecQuery(Query As String, Optional ByVal RunScalar As Boolean = False, Optional ByRef NewID As Long = -1)
' RESET QUERY STATS
RecordCount = 0
Exception = ""
Dim RunScalar As Boolean = False
Try
' OPEN A CONNECTION
DBCon.Open()
' CREATE DB COMMAND
DBCmd = New SqlCommand(Query, DBCon)
' LOAD PARAMS INTO DB COMMAND
Params.ForEach(Sub(p) DBCmd.Parameters.Add(p))
' CLEAR PARAMS LIST
Params.Clear()
' EXECUTE COMMAND & FILL DATATABLE
If RunScalar = True Then
NewID = DBCmd.ExecuteScalar()
End If
DBDT = New DataTable
DBDA = New SqlDataAdapter(DBCmd)
RecordCount = DBDA.Fill(DBDT)
Catch ex As Exception
Exception = ex.Message
End Try
' CLOSE YOUR CONNECTION
If DBCon.State = ConnectionState.Open Then DBCon.Close()
End Sub
' INCLUDE QUERY & COMMAND PARAMETERS
Public Sub AddParam(Name As String, Value As Object)
Dim NewParam As New SqlParameter(Name, Value)
Params.Add(NewParam)
End Sub
End Class

Self learning on vb.net

Currently I'm trying to understand and learn new code commands for vb.net. i have came across three codes while researching which is
"SELECT staff_id,pass_word FROM userlogin WHERE staff_id = #staff_id AND pass_word = #pass_word")
Second code:
Dim uName As New OleDbParameter("#staff_id", SqlDbType.VarChar)
Third and last:
uName.Value = txtstaffid.Text
myCommand.Parameters.Add(uName)
What are the uses of #pass_word code when you have already typed the pass_word column, Oledbparameter, and Parameters.Add?
The following code shows a bit more complete picture of what the code is doing. The Using...End Using blocks ensure that your objects are closed and disposed even if there are errors. Of course, in a real application, passwords would never be stored as plain text (too easy to hack). They would be salted and hashed but that is for another day.
Private Sub CheckPassword()
'This line assigns a Transact SQL command to a string variable.
'It will return a record with 2 columns. The #staff_id and #pass_word are parameter placeholders.
'The use of parameters limits the possibilit of SQL injection with malicious input be the user
'typing in the text box.
Dim strSQL = "SELECT staff_id,pass_word FROM userlogin WHERE staff_id = #staff_id AND pass_word = #pass_word;"
Using cn As New SqlConnection("Your connection string")
'Pass the command string and the connection to the constructor of the command.
Using cmd As New SqlCommand(strSQL, cn)
'It is unneccessary to create a command variable.
'The .Add method of the commands Parameters collection will create a parameter.
cmd.Parameters.Add("#staff_id", SqlDbType.VarChar).Value = txtstaffid.Text
cmd.Parameters.Add("#pass_word", SqlDbType.VarChar).Value = txtPassword.Text
cn.Open()
Using dr As SqlDataReader = cmd.ExecuteReader
'All we really need to know is whether we returned a row.
If dr.HasRows Then
MessageBox.Show("Login Successful")
Else
MessageBox.Show("Login Failed")
End If
End Using
End Using
End Using
End Sub

DataAdapter is disposed before reaching "End Using"

I know that I should always dispose DataAdapter instances. In most cases I'm disposing it immediately after closing the connection, but in cases like when user will be modifying DataTable items (displayed in ListBox or DataGridView) I create the DataAdapter, use it to fill the DataTable, but don't dispose it until the user clickes Save which calls DataAdapter.Update(DataTable)... not my main question but is this the right approach?
Back to the main question, I have these two functions:
Public Function LoadCompaniesDT(ByRef dtCompanies As DataTable) As Boolean
Using daCompanies As MySqlDataAdapter = Nothing
Return LoadCompaniesDT(daCompanies, dtCompanies)
End Using
End Function
Public Function LoadCompaniesDT(ByRef daCompanies As MySqlDataAdapter, ByRef dtCompanies As DataTable) As Boolean
Dim sql As String = "SELECT * FROM companies"
Return LoadDT(daCompanies, dtCompanies, sql, Res.CompaniesFailedMsgBody)
End Function
They're used to call LoadDT which fills the DataTable so I have the choice to pass a DataAdapter or not.
Now I'm confused about something: When using the first LoadCompaniesDT function, daCompanies is disposed before reaching End Using.. like this:
Public Function LoadCompaniesDT(ByRef dtCompanies As DataTable) As Boolean
Using daCompanies As MySqlDataAdapter = Nothing
Dim tmp As Boolean = LoadCompaniesDT(daCompanies, dtCompanies)
Console.WriteLine(daCompanies Is Nothing) ' ==> True!!
Return tmp
End Using
End Function
Note: if I use Dim daCompanies instead of Using daCompanies then daCompanies Is Nothing will return False.
LoadDT function code:
Private Function LoadDT(ByRef da As MySqlDataAdapter, ByRef dt As DataTable,
ByVal sqlQuery As String,
ByVal errorText As String) As Boolean
Dim connStr As String = String.Format("server={0}; port={1}; user id={2}; password={3}; database={4}",
DbServer, DbServerPort, DbUserName, DbPassword, DatabaseName)
Dim conn As MySqlConnection = New MySqlConnection(connStr)
Dim cmd As MySqlCommand = New MySqlCommand
Try
conn.Open()
cmd.CommandType = CommandType.Text
cmd.CommandText = sqlQuery
cmd.Connection = conn
da = New MySqlDataAdapter(cmd)
dt = New DataTable
da.Fill(dt)
Return True
Catch ex As Exception
MessageBox.Show(errorText, Res.ServerError, MessageBoxButtons.OK, MessageBoxIcon.Error)
Return False
Finally
cmd.Dispose()
cmd = Nothing
conn.Close()
conn.Dispose()
End Try
End Function
Update: you're right, you don't get an initialized MySqlDataAdapter back from the methods if the ByRef passed instance is used in a Using-statement. Those variables are readonly. In C# you get this meaningful compiler error:
Error CS1657 Cannot pass 'daCompanies' as a ref or out argument
because it is a 'using variable'
It's documented here:
Compiler Error CS1657
Cannot pass 'parameter' as a ref or out argument because 'reason''
This error occurs when a variable is passed as a ref or out argument
in a context in which that variable is readonly. Readonly contexts
include foreach iteration variables, using variables, and fixed
variables.
In VB.NET you can do that(so the compiler ignores it which is almost a bug) but the variable is not initialized afterwards. But as mentioned below, you should not use this approach anyway.
According to the the other question:
If you look at the sample on MSDN you'll see that microsoft also doesn't dispose the dataadapter. So it not really necessary. Having said that, it's always best practise to use the Using statement on anything that implements IDisposable.
A DataAdapter is not an expensive object and it does not hold unmanaged resources(like the connection). So it doesn't hurt to create a new instance from it whereever you need one. And you don't need to dispose it, but that's an implementation detail that might change in future or in a different implementation of DbDataAdapter, so it's still best practise to dispose it, best by using the Using-statement.
I wouldn't use your approach because you are passing the sql-string to the method which often leads to a sql injection vulnerabiliy. Instead use sql parameters.
For example:
Private Function LoadDT() As DataTable
Dim tbl As New DataTable()
'Load connection string from app.config or web.config
Dim sql As String = "SELECT * FROM companies" ' don't use * but list all columns explicitely
Using conn As New MySqlConnection(My.Settings.MySqlConnection)
Using da = New MySqlDataAdapter(sql, conn)
da.Fill(tbl)
End Using
End Using
Return tbl
End Function
Instead of passing an errorText ByRef i'd use a logging framework like log4net.

adding sqlparameter without name

I'm trying to create a sub to add as many parameters as I want, I used to do this in vb6 but here in vb.net it requires me to provide the parameter name (.add(#parameter, value)). I need to find a way to do it without knowing the parameter name, i used to send the parameters using the parameter order in the stored procedure, here the code:
Public Sub EjecutarSP(ByVal SP As String, ByVal ParamArray Parametros() As Object)
Dim cnn As New SqlConnection(ConfigurationSettings.AppSettings("connString").ToString)
Dim cmd As New SqlCommand(SP, cnn)
Dim i As Integer
Dim Param As SqlParameter
Try
For i = 0 To UBound(Parametros)
Param = New SqlParameter("str", Parametros(i))
cmd.Parameters.Add(Param)
Next
cmd.CommandTimeout = 0
cmd.CommandType = CommandType.StoredProcedure
If cmd.Connection.State <> ConnectionState.Open Then cmd.Connection.Open()
cmd.ExecuteNonQuery()
cmd = Nothing
Catch ex As Exception
Err.Raise(1000, "EjecutarSP", ex.Message)
End Try
End Sub
thanks
AFAIK the SqlClient doesn't support nameless parameters
From here:
Nameless, also called ordinal, parameters are not supported by the .NET Framework Data Provider for SQL Server.
However, instead of explicitly creating the parameters, you could fetch the parameters for the Proc with DeriveParameters, once you are connected.
Assuming you wish to blindly wish to trust the order of the parameters to match the parameters of the Proc, simply set the .Value of the parameters at the given index.
Dim cmd As New SqlCommand(SP, cnn)
...
SqlCommandBuilder.DeriveParameters(cmd)
For i = 0 To UBound(Parametros)
cmd.Parameters(i).Value = Parametros(i)
Next
Just reset the name to nothing (null) once the parameter has been added, something like this:
For i = 0 To UBound(Parametros)
Param = New SqlParameter("str", Parametros(i))
cmd.Parameters.Add(Param)
Param.ParameterName = Nothing
Next
You don't have to explicity name or type parameters. There are actually a couple of ways to execute stored procs without naming each parameter. Here is one way using Microsoft's old EnterpriseLibrary (which I still like in 2019).
NB: in VB.NET the size of the parameter array is one less than the number of parameters in the stored proc.
Dim dt As System.Data.DataTable
Dim arrParameters(6) As Object
arrParameters(0) = "Hello"
arrParameters(1) = "World"
arrParameters(2) = "Lorem"
arrParameters(3) = "Ipsum"
arrParameters(4) = "Dolor"
arrParameters(5) = "Blabla"
arrParameters(6) = 12345
Try
Dim db As Microsoft.Practices.EnterpriseLibrary.Data.Database = Microsoft.Practices.EnterpriseLibrary.Data.DatabaseFactory.CreateDatabase("TheNameOfYourConnectionStringInWebConfig")
dt = db.ExecuteDataSet("TheNameOfYourStoredProcedure", arrParameters).Tables(0)
Catch ex As Exception
End Try

ExecuteNonQuery() for Insert

Can you please tell me what's wrong with this code?
Do I need to use DataAdapter to insert into a table?
I know the connectionString is ok, because I tested it on the Server Explorer.
Dim mydao As New Connection
Dim connectionString As String = mydao.GetConnectionString()
Dim connection As New SqlConnection(connectionString)
Dim cmd As New SqlCommand
Public Function add(ByVal area As String, ByVal user As String) As Integer
cmd.CommandText = "INSERT into Area (Area, user) VALUES ('" + area + "','" + user + "')"
Try
connection.Open()
Dim cant As Integer = cmd.ExecuteNonQuery()'it throws exception here
connection.Close()
Return cant
Catch ex As Exception
Console.WriteLine(ex.Message)
Return 0
End Try
End Function
The above code fails just after ExecuteNonQuery() and can´t figure why.
TARGET FIELDS (SQL Server 2008):
AREA varchar(100) NOT NULL ,
USER varchar(100) NOT NULL
The exception I receive is: Connection property has not initialized
There's a few issues with this code.
The most significant is that you aren't setting the Command's Connection property, so the command has no way of knowing how to connect to the database.
I would also strongly recommend utilizing using, and also parameterizing your query:
Finally, don't declare the connection and command outside of the function unless you need to. You should only keep the connection and command around for as long as you need them.
So your function would end up looking like:
Public Function add(ByVal area As String, ByVal user As String) As Integer
Dim mydao As New Connection
Using connection As New SqlConnection(mydao.ConnectionString())
Using command As New SqlCommand()
' Set the connection
command.Connection = connection
' Not necessary, but good practice
command.CommandType = CommandType.Text
' Example query using parameters
command.CommandText = "INSERT into Area (Area, user) VALUES (#area, #user)"
' Adding the parameters to the command
command.Parameters.AddWithValue("#area", area)
command.Parameters.AddWithValue("#user", user)
connection.Open()
Return command.ExecuteNonQuery()
End Using ' Dispose Command
End Using ' Dispose (and hence Close) Connection
End Function
Note that currently, you will be returning 0 all the time. Rather than having to check the value returned from the function, the above example will simply throw an exception. This makes for slightly cleaner code (as the caller would have to understand that 0 is an error condition), and, if you needed to handle the exception, simply wrap the call to this function in a Try-Catch block