Adodb Recordset function .MoveNext Causes Exception "UPDATE permission was denied" - vb.net

I've run into a problem today that has stumped me.
Here is some sample code:
Dim objRec As ADODB.Recordset
Dim oRec As ADODB.Recordset
Dim oRecBuild As New ADODB.Recordset
cmd = New ADODB.Command()
cmd.ActiveConnection = objConn
cmd.CommandText = "SelectAll_PhoneNumbers_ById"
cmd.CommandType = ADODB.CommandTypeEnum.adCmdStoredProc
cmd.NamedParameters = True
cmd.Parameters.Append(cmd.CreateParameter("#ObjId", ADODB.DataTypeEnum.adVarChar, ADODB.ParameterDirectionEnum.adParamInput, 20, objRec.Fields("PK_ProgramID").Value))
oRec = New ADODB.Recordset()
oRec.Open(cmd, , ADODB.CursorTypeEnum.adOpenStatic, ADODB.LockTypeEnum.adLockOptimistic)
If oRec.EOF = False Then
Do Until oRec.EOF
If IsDBNull(oRec.Fields("PhoneType").Value) = False Then
sName = PhoneNumberEdit(oRec.Fields("Phone").Value)
If IsDBNull(oRec.Fields("Extension").Value) = False And Len(oRec.Fields("Extension").Value) > 0 Then
sName = PhoneNumberEdit(oRec.Fields("Phone").Value) & " " & oRec.Fields("Extension").Value
End If
oRecBuild.AddNew(New Object() {"TextPrint", "TextType"}, New Object() {sName, oRec.Fields("PhoneType")})
End If
oRec.MoveNext()
Loop
End If
When I reach the .MoveNext() function the app throws an error that reads like this: The UPDATE permission was denied on the object 'PhoneNumbers', database 'MyDb', schema 'dbo'.
Nothing in this code block is calling an update (the function calls in the loop are just formatting data), does anyone have any idea of what could be causing this?
I should also add that I can run this using SSPI locally, however the code needs to be able to run on a server using a SQL username and PW; I have tested updates with the app on other pages, and it works fine.

This is just a hunch, but I do see one place in that code that might result in an UPDATE commend: oRecBuild.AddNew(). I don't see how it could, but I wonder if calling oRec.MoveNext() is somehow forcing a commit from oRecBuild. Otherwise, I'd take a longer look at the SelectAll_PhoneNumbers_ById procedure, and any possible triggers that might be attached to tables or views it uses.
And if that fails, I'd do this:
Public Class PhoneNumber
Public Number As String
Public PhoneType As String
End Public
'...
Dim result As New List(Of PhoneNumber)
Using cn As New SqlConnection("connection string"), _
cmd As New SqlCommand("SelectAll_PhoneNumbers_ById", cn)
cmd.CommandType = CommandType.StoredProcedure
'Is this really an integer?
cmd.Parameters.Add("#ObjId", SqlDbType.NVarChar, 20).Value = objRec.Fields("PK_ProgramID").Value
cn.Open()
Using rdr As SqlDataReader = cmd.ExecuteReader()
While rdr.Read()
Dim phone As String = ""
If Not IsDbNull(rdr("PhoneType"))
phone = PhoneNumberEdit(rdr("Phone"))
End If
If Not IsDbNull(rdr("Extension"))
phone = phone & " " & PhoneNumberEdit(rdr("Extension"))
End If
result.Add(New PhoneNumber With {.Number = phone, .PhoneType = rdr("PhoneType")})
End While
End Using
End Using
While I'm at it... anytime you're reading data from one recordset into another there's a very strong chance that the whole thing could be done entirely in the database, with no client code at all.

To close this question, I wanted to go ahead and say that I finally was able to get this resolved. The issue was that dynamic sql being executed with sp_executesql explicitly checks the select, insert, or update privileges of the SQL user; execute privs are NOT enough.
So, either the dynamic SQL has to be done away with or the select/update/insert/delete privs must be granted.
Thank you all for the help and time.

Related

Converting VBA function to VB.net to get sql data

I am trying to convert VBA code into VB.net and I have made it to a point but I can't convert resultset into vb.net. RS was 'dim as resultset' in VBA, thought i could just change it to dataset but am getting errors with the '.fields' and other options?
Function GetG(sDB As String, sServ As String, sJob As String) As String
'sDB = Database name, sServ = Server\Instance, path = job.path
Dim conString As String = ("driver={SQL Server};server = " &
TextBox1.Text & " ; uid = username;pwd=password:database = " &
TextBox2.Text)
Dim RS As DataSet
Dim conn As SqlConnection = New SqlConnection(conString)
Dim cmd As SqlCommand
conn.Open()
'This is where my problems are occuring
cmd = New SqlCommand("SELECT [ID],[Name] FROM dbo.PropertyTypes")
Do While Not RS.Tables(0).Rows.Count = 0
If RS.Fields(1).Value = sJob Then
GetG = RS.Fields(0).Value
GetG = Mid(GetG, 2, 36)
Exit Do
End If
DataSet.MoveNext
Loop
conn.Close
End Function
Based on my understanding and some guesswork, here is what I came up with for what I think you're wanting.
As I stated in my comment above, it appears you can just use a WHERE clause to get the exact record you want (assuming a single instance of sJob appears in the name column).
Build the connectionstring off the input arguments, not controls on your form. That is after all why you allow for arguments to be passed along. Also note that there is a SqlCommandBuilder object that may be of interest. But for now
Function GetG(sDB As String, sServ As String, sJob As String) As String
'we'll pretend your connectionstring is correct based off of the sDB and sServ arguments
Dim conStr As String = ("driver={SQL Server};server = " & sServ & " ; uid = username;pwd=password:database = " & sDB)
'Create a connection and pass it your conStr
Using con As New SqlConnection(conStr)
con.Open() 'open the connection
'create your sql statement and add the WHERE clause with a parameter for the input argument 'sJob'
Dim sql As String = "SELECT [ID], [Name] FROM dbo.PropertyTypes WHERE [Name] = #job"
'create the sqlCommand (cmd) and pass it your sql statement and connection
Using cmd As New SqlCommand(sql, con)
'add a parameter so the command knows what #job holds
cmd.Parameters.Add(New SqlParameter("#job", SqlDbType.VarChar)).Value = sJob
'Now that have the command built, we can pass it to a reader object
Using rdr As SqlDataReader = cmd.ExecuteReader
rdr.Read()
'i admin i'm a little confused here on what you are
'trying to achieve so ID may not be what you are
'really wanting to get a substring of.
Return rdr("ID").ToString.Substring(2, 36)
End Using
End Using
End Using
End Function
An example to see if this is working could be to call a messagebox do display the result. For this example, I'm going to pretend that TextBox3 holds the sJob you're wanting. With that knowledge, you could simply do:
MessageBox.Show(GetG(TextBox2.Text, TextBox1.Text, TextBox3.Text))
This should then produce the result in a messagebox.
It seems that you're not filling your DataSet. So, when you try to loop through it, it's uninitialized or empty.
Check this answer to see an example: Get Dataset from DataBase

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

SQLite update query works in console, not in .Net

I am using an SQLite database in my application. For some reason the query:
UPDATE USERS SET PASSWORD = 'test' WHERE USERNAME = 'Admin'
works in console (updates the PASSWORD field), but when I run it via code:
Dim exec As String = "UPDATE USERS SET PASSWORD = '" & password & "' WHERE USERNAME = '" & username & "'"
Dim returnCode As [Boolean] = True
Try
ExecuteNonQuery(exec)
Catch
returnCode = False
End Try
Return returnCode
The function always returns false, and the database is not updated. Here is the ExecuteNonQuery method:
Public Shared Function ExecuteNonQuery(sql As String) As Integer
Dim cnn As New SQLiteConnection(dbConnection)
cnn.Open()
Dim mycommand As New SQLiteCommand(cnn)
mycommand.CommandText = sql
Dim rowsUpdated As Integer = mycommand.ExecuteNonQuery()
cnn.Close()
Return rowsUpdated
End Function
What am I doing wrong here? My code for creating a new row works fine, but updating doesn't.
Yes, I am aware this is unsafe, I originally had a bunch more safety checks, but I need to at least get it to work before doing so.
For some reason, sqlite3.dll did not get moved to the output folder along with the application, causing weird behavior and ultimately for the query to fail.
When I placed a copy of the dll into the output folder, everything worked fine.

Check for Object reference not set to an instance of an object

I have a website I am building with asp.net. I am trying to incorporate a username check based off their username of their pc. I get this
Object reference not set to an instance of an object.
When a new user that is not in my SQL Server database tries to access the site. I would like to add an "if" statement that would redirect them to my error page.
What do I put in my if statement to catch that error instead of getting the error page.
Here is part of my code so you can know what names I am using
Dim sqlConnection1 As New SqlConnection("randomserverstuff")
Dim cmd As New SqlCommand
Dim returnValue As Object
cmd.CommandText = "SELECT RESTRICTED FROM USERS WHERE Username='" & Replace(Page.User.Identity.Name, "domain\", "") & "'"
cmd.CommandType = CommandType.Text
cmd.Connection = sqlConnection1
sqlConnection1.Open()
returnValue = cmd.ExecuteScalar()
sqlConnection1.Close()
If Session(returnValue)!= Null) Or returnValue.ToString = "Y" Then
that last line, the first part of the is statement is what I'm trying to build and cant seem to catch that error in it. Any ideas?
This is wrong:
If Session(returnValue)!= Null)
Really, is this vb or c# - neither one!!
Considering that returnValue can be Nothing the right way would be:
If returnValue IsNot Nothing AndAlso Session(returnValue) IsNot Nothing Then
Declare returnValue as String, VB will convert it for you implicitly.
So after no luck I decided to check for row count before running my statement and if rows = 0 to return y and if rows is greater to run a seperate sql statement to determine the result of the restricted column.

Inserting variables into a query string - it won't work!

Basically i have a query string that when i hardcode in the catalogue value its fine. when I try adding it via a variable it just doesn't pick it up.
This works:
Dim WaspConnection As New SqlConnection("Data Source=JURA;Initial Catalog=WaspTrackAsset_NROI;User id=" & ConfigurationManager.AppSettings("WASPDBUserName") & ";Password='" & ConfigurationManager.AppSettings("WASPDBPassword").ToString & "';")
This doesn't:
Public Sub GetWASPAcr()
connection.Open()
Dim dt As New DataTable()
Dim username As String = HttpContext.Current.User.Identity.Name
Dim sqlCmd As New SqlCommand("SELECT WASPDatabase FROM dbo.aspnet_Users WHERE UserName = '" & username & "'", connection)
Dim sqlDa As New SqlDataAdapter(sqlCmd)
sqlDa.Fill(dt)
If dt.Rows.Count > 0 Then
For i As Integer = 0 To dt.Rows.Count - 1
If dt.Rows(i)("WASPDatabase") Is DBNull.Value Then
WASP = ""
Else
WASP = "WaspTrackAsset_" + dt.Rows(i)("WASPDatabase")
End If
Next
End If
connection.Close()
End Sub
Dim WaspConnection As New SqlConnection("Data Source=JURA;Initial Catalog=" & WASP & ";User id=" & ConfigurationManager.AppSettings("WASPDBUserName") & ";Password='" & ConfigurationManager.AppSettings("WASPDBPassword").ToString & "';")
When I debug the catalog is empty in the query string but the WASP variable holds the value "WaspTrackAsset_NROI"
Any idea's why?
Cheers,
jonesy
alt text http://www.freeimagehosting.net/uploads/ba8edc26a1.png
I can see a few problems.
You are using concatenation in a SQL statement. This is a bad practice. Use a parameterized query instead.
You are surrounding the password with single quotes. They are not needed and in fact, I'm surprised it even works assuming the password itself does not have single quotes.
You should surround classes that implement IDisposable with a Using block
You should recreate the WASP connection object in GetWASPcr like so:
Public Sub GetWASPAcr()
Dim username As String = HttpContext.Current.User.Identity.Name
Dim listOfDatabaseConnectionString As String = "..."
Using listOfDatabaseConnection As SqlConnection( listOfDatabaseConnectionString )
Using cmd As New SqlCommand("SELECT WASPDatabase FROM dbo.aspnet_Users WHERE UserName = #Username")
cmd.Parameters.AddWithValue( "#Username", username )
Dim dt As New DataTable()
Using da As New SqlDataAdapter( cmd )
da.Fill( dt )
If dt.Rows.Count = 0 Then
WaspConnection = Null
Else
Dim connString As String = String.Format("Data Source=JURA;Initial Catalog={0};User Id={1};Password={2};" _
, dt.Rows(0)("WASPDatabase") _
, ConfigurationManager.AppSettings("WASPDBUserName") _
, ConfigurationManager.AppSettings("WASPDBPassword"))
WaspConnection = New SqlConnection(connString);
End If
End Using
End Using
End Using
End Sub
In this example, listOfDatabaseConnectionString is the initial connection string to the central database where it can find the catalog name that should be used for subsequent connections.
All that said, why would you need a class level variable to hold a connection? You should make all your database calls open a connection, do a sql statement, close the connection. So, five database calls would open and close a connection five times. This sounds expensive except that .NET gives you connection pooling so when you finish with a connection and another is requested to be opened, it will pull it from the pool.
Your string passed into the constructor for this SqlConnection object will be evaluated when the class is instantiated. Your WASP variable (I'm assuming) won't be set until the method you have shown is called.
Might want to quit looking one you have found your database:
For i As Integer = 0 To dt.Rows.Count - 1
If dt.Rows(i)("WASPDatabase") Is DBNull.Value Then
WASP = ""
Else
WASP = "WaspTrackAsset_" + dt.Rows(i)("WASPDatabase")
break
End If
Next
[link text][1]You are building your string on the fly by adding the value of a column to a string. So, for the row in question for the column "WASPDatabase" was tacked on to your string. So you got what it had. On another note, your earlier query of "select ... from ... where ..." where you are manually concatinating the string of a variable makes you WIDE OPEN to SQL-Injection attacks.
Although this link [1]: how to update a table using oledb parameters? "Sample query using parameterization" is to a C# sample of querying with parameterized values, the similar principles apply to most all SQL databases.
At the time you're creating the new connection, WASP is holding the value you want it to be holding? It is a string data type? Try adding .ToString after WASP and see if that helps anything.
Interesting problem. =-)
The problem is, as Paddy already points out, that the WaspConnection object gets initialized before you even have the chance to call GetWASPAcr. Try this:
Public Sub GetWASPAcr()
'[...]
End Sub
Dim _waspConnection As SqlConnection
Public Readonly Property WaspConnection As SqlConnection
Get
If _waspConnection Is Nothing Then
GetWASPAcr()
_waspConnection = New SqlConnection("Data Source=JURA;Initial Catalog=" & WASP & ";User id=" & ConfigurationManager.AppSettings("WASPDBUserName") & ";Password='" & ConfigurationManager.AppSettings("WASPDBPassword").ToString & "';")
End If
Return _waspConnection
End Get
End Property