I have a script that runs a stored procedure in my SQL server database, the problem is the stored procedure takes a uniqueidentifier parameter. I have a function that grabs a session id from the database (which is an nvarchar), so VBScript makes it a string and I need to convert it to pass it to the stored procedure.
Function GetOpenSession
Set conn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
conn.Open "Provider=SQLOLEDB.1;Integrated Security=SSPI;Data Source=" & Source
rs.CursorLocation = 3
rs.Open "SELECT top 1 OpenSession FROM OpenSessions with (nolock)" , conn, 3, 3
If rs.RecordCount = 0 Then
MsgBox "No Connection"
Else
GetOpenSession = rs.Fields(0).Value
End If
End Function
I get "Invalid character value for cast specification" when I try to execute the stored procedure.
set cnParam = cmd.CreateParameter("#ActiveSession",72,1,,GetOpenSession)
cmd.Parameters.Append cnParam
I can't change anything in the database, so I need a way to overcome this in my script.
I believe VBScript expects GUIDs to be brace terminated.
Is your Session id of the same format as the following {D6CA6263-E8E1-41C1-AEA6-040EA89BF030}
Depending on the data type of the SELECT OpenSession, you may be able to cast/convert it in the query and VBScript may possibly keep the data type as a GUID:
SELECT top 1 CONVERT(uniqueidentifier, OpenSession)
FROM OpenSessions with (nolock)
When you use GetOpenSession or rs.Fields(0).Value, hopefully VBScript will keep it as a GUID.
The other possibility seems to be a Win32 API using CoCreateGuid and StringFromGUID2. An example is found here, but it requires external Win32 functions and a Type for GUID.
I usually change the parameter type of the stored procedure to varchar(38).
SQL Server makes a better job of coercing the string to a GUID when needed.
What is the value of GetOpenSession after you have assigned it? Which datatype? It sounds like it is not compatible with type 72 (GUID), which you are stating in the CreateParameter call.
You could forget about using ADO Commands altogether and just execute the stored procedure using plain SQL.
Set rs = CreateObject("ADODB.Recordset")
rs.Open "EXEC usp_MySP '" & GetOpenSession & "'", cnn
Obviously it's terrible to build SQL commands like this, but it's just for a test, after all...
Related
I am working in Classic ASP. I know there is a record that matches my simple SQL select query. It has the ' character ' in it. The code is as follows:
Fieldname = Replace(trim(Request.form("Fieldname")),"'","'", 1, 10)
'replace the "'" up to 10 times with the ' code to avoid SQL issues, LOL.
SQL = "select id,fieldname from table where fieldname='"&Trim(Fieldname)&"'"
set rs = server.createobject("adodb.recordset")
rs.open SQL, Application("conn"), 1, 1
If not rs.eof then
response.redirect "somepage.asp?QS=Fieldname_Exists_in_DB"
Else
'Sample.hold the value in a hidden input field and pass it to the next page
End If
The problem is, I know for a fact the fieldname and fieldname value is in the MS-SQL 2016 server table. I pull data from it all the time. The value in the database field contains the ' value as does the Replaced FORM Fieldname when it is compared to the SQL database field, so it should NOT pass the IF NOT RS.EOF question. Yet it passes every time.
What am I missing? I'm doing the exact same query in other places on this exact same app and it behaves as one would expect.
Tried to explain in the comments but as the point is being missed, I'll try to give you an example here.
Do not trust user input
Classic ASP server-side code that interacts with the ADODB Library doesn't have any notion of sanitised data. This means that any input that comes from the user via the Request object (like Request.Form("Fieldname")) should not be trusted.
Fieldname = Replace(trim(Request.form("Fieldname")),"'","'", 1, 10)
SQL = "select id,fieldname from table where fieldname='"&Trim(Fieldname)&"'"
This example is open to SQL Injection attacks and is generally bad practise and leads to security flaws that can be easily exploited with script tools readily available on the internet.
Manually sanitising data
Apart from the security flaws introduced, it also makes it harder to query data due to how SQL calls for strings and other data types need to be constructed (which varies from provider to provider). Having to account for the various combinations of characters that could be deemed dangerous or likely to break the query can be a cumbersome task and one seen far too often in the wild when ADODB already has a solution.
Parameterised Queries
The ADODB Library has an in-built object called ADODB.Command which takes all these hassles away.
Using the example in the question the same query can be written without the failings of manually sanitising data or executing SQL directly against user input.
Const adCmdText = 1
Const adVarWChar = 202
Const adParamInput = 1
Dim Fieldname, SQL, cmd, rs,
Fieldname = Trim(Request.Form("Fieldname") & "")
SQL = "SELECT id, fieldname FROM table WHERE fieldname = ?"
Set cmd = Server.CreateObject("ADODB.Command")
With cmd
.ActiveConnection = Application("conn")
.CommandType = adCmdText 'Also can use 1
.CommandText = SQL
Call .Append(.CreateParameter("#fieldName", adVarWChar, adParamInput, 255))
Set rs = .Execute(, Array(Fieldname))
End With
Set cmd = Nothing
If Not rs.EOF then
response.redirect "somepage.asp?QS=Fieldname_Exists_in_DB"
Else
'Sample.hold the value in a hidden input field and pass it to the next page
End If
Useful Links
A: Using METADATA to Import DLL Constants (shows an approach to using Named Constants that doesn't require adding your own Const declarations to the code).
I'm struggling with an old and massive system developed on MS-Access 2007, which I'm in charge of its maintenance (including the implementation of changes).
The system interacts with a SQL-Server (2012).
Currently, I need to implement a change in which a new stored procedure needs to be invoked which returns a simple records set, each record being a string.
I tried to use for the invocation some code that already exists within the application (using ADO and copied into the form I'm working on) but nothing I tried works.
Here is the code I'm trying to use:
glblsqlstrToLabels = "EXEC p_Labels_Print 1 , 2878954 , 'OC9991' , '89029' , 4 , 1 , 'dummy'"
Though I'm using exactly the same connection string as it is being used all over the application, executing the above statement returns with an error message (something like ...not open...).
I'm starting to suspect that there is something wrong in the way I'm invoking the function (e.g. not defining any parameters for it and expecting a behavior similar to a select statement).
Any help will be highly appreciated.
EDIT:
Following are the pieces of code describing what I need to have working:
Dim RS As Recordset
' Connection string is: Provider=Microsoft.Access.OLEDB.10.0;Persist Security Info=True;Data Source=****;User ID=****;Password=****;Initial Catalog=***;Data Provider=SQLOLEDB.1
MyCommand = "EXEC p_Labels_Print 1 , 2878954 , 'OC9991' , '89029' , 4 , 1 , 'asdasd'"
RS.Open MyCommand, CurrentProject.Connection
Do Until RS.EOF
Print <record retrieved>
Loop
RS.Close
Set RS = Nothing
The error I get is: Error: Operation is not allowed when the object is closed.
You need to properly use ADO and commands, you can't just rs.Open a stored procedure.
Use the following Microsoft boilerplate code:
Set Cmd1 = New ADODB.Command
Cmd1.ActiveConnection = Conn1
Cmd1.CommandText = "sp_AdoTest"
Cmd1.CommandType = adCmdStoredProc
Cmd1.Parameters.Refresh
Cmd1.Parameters(1).Value = 10
Set Rs1 = Cmd1.Execute()
Where Conn1 is your ADODB connection, and fill in your parameters and stored procedure name.
I am working on an Access DB, which have ODBC linked SQL Server table, and I have following script to run TSQL query, as you can see I tried to include a value from Access Forms in the query, but it fails to run. The form is opened and filled with data when I execute the script. I am wondering if this is impossible or there is another way of doing it? I am new to TSQL and SQL server, here is my question. Appreicate if someone can help. Thanks a lot.
Function formtest()
Dim qryd As QueryDef
Set qryd = CurrentDb.CreateQueryDef("")
qryd.Connect = "ODBC;DSN=SQLSERVER;"
qryd.SQL = "UPDATE dbo.table1 SET firstname = [Forms]![testform]![datainput]"
qryd.ReturnsRecords = False
qryd.Execute
End Function
The SQL Server doesn't know anything about your forms. You have to send the data with the query. Something like this:
qryd.SQL = "UPDATE dbo.table1 SET firstname = '" & [Forms]![testform]![datainput] & "'"
One thing you have to be aware of though is that if there are any single quotes in your datainput it could invalidate the SQL. It could also be a security issue. Either test for single quotes and raise an error, or replace each of them with two.
The best way to do it is to use a parameterized query. This will absolutely prevent issues SQL injection and also help with performance in many cases. Unfortunately, I don't believe you can create a paramaterized query for SQL Server using DAO. You would have to convert to ADO, which is best suited for sending queries to a SQL Engine other than Jet.
To use ADO you might have to add a reference to Microsoft ActiveX Data Objects by opening the VBA code window and selecting Tools -> References -> and checking the box next to it. Then your code would look something like this:
Dim Conn1 As ADODB.Connection
Dim Cmd1 As ADODB.Command
Dim Param1 As ADODB.Parameter
Rem Create and Open Connection Object.
Set Conn1 = New ADODB.Connection
Conn1.ConnectionString = "ODBC;DSN=SQLSERVER;"
Conn1.Open
Rem Create Command Object.
Set Cmd1 = New ADODB.Command
Cmd1.ActiveConnection = Conn1
Cmd1.CommandText = "UPDATE dbo.table1 SET firstname = ?"
Rem Create Parameter Object.
Set Param1 = Cmd1.CreateParameter(, adVarChar, adParamInput, 25)
Param1.Value = [Forms]![testform]![datainput]
Cmd1.Parameters.Append Param1
Set Param1 = Nothing
Rem Open Recordset Object.
Call Cmd1.Execute
I have the following UDF in excel which uses ADO to connect to my MSSQL server. There it should execute the scalar udf "D100601RVDATABearingAllow".
For some reason the parameters that I try to append are not send to the sql server. At the server only:
SELECT dbo.D100601RVDATABearingAllow
arrives.
MY EXCEL UDF:
Function RVDATA(Fastener) As Long
Dim cnt As ADODB.Connection
Dim rst As ADODB.Recordset
Dim Cmd1 As ADODB.Command
Dim stSQL As String
Const stADO As String = "Provider=SQLOLEDB.1;Data ................"
'----------------------------------------------------------
Set cnt = New ADODB.Connection
With cnt
.ConnectionTimeout = 3
.CursorLocation = adUseClient
.Open stADO
.CommandTimeout = 3
End With
'----------------------------------------------------------
Set Cmd1 = New ADODB.Command
Cmd1.ActiveConnection = cnt
Cmd1.CommandText = "dbo.D100601RVDATABearingAllow"
Cmd1.CommandType = adCmdStoredProc
'----------------------------------------------------------
Set Param1 = Cmd1.CreateParameter("Fastener", adInteger, adParamInput, 5)
Param1.Value = Fastener
Cmd1.Parameters.Append Param1
Set Param1 = Nothing
'----------------------------------------------------------
Set rst = Cmd1.Execute()
RVDATA = rst.Fields(0).Value
'----------------------------------------------------------
rst.Close
cnt.Close
Set rst = Nothing
Set cnt = Nothing
'----------------------------------------------------------
End Function
When I use adCmdStoredProc the whole thing fails and in the vba debugger the properties of the recordset has a lot of "Operation is not allowed when object is closed" (may sound a bit different, the message is translated)
When I don't use adCmdStoredProc I get the message that the variable Fastener was not provided.
I think that maybe something is wrong in the way I open the recordset.
In other treads I read about using the "SET NOCOUNT ON" option, but that did not work either.
Does anyone have a idea?
Regards Lumpi
Ran into this error as well (in my case I am using a Stored Procedure to retrieve some information). I had made some changes which caused the execution to malfunction.
The error disappeared when I put SET NOCOUNT ON as the first statement of the Stored Procedure.
You do not need to SELECT the server side function, just provide its name ("[tra-CAE400-1].dbo.D100601RVDATABearingAllow") in the .CommandText property.
Also you should set the .CommandType property to "stored-procedure" (property reference on w3schools.com).
Then adodb will know that you are talking about calling a function, and not trying to send a plain sql-command.
Chances are that it will then allow you to define the parameters on the command object.
But the parameters you define on the command object should correspond exactly (in name and type) to the ones that are defined as the arguments of the function in the sql server.
An example from microsoft.com on using the command-object with a stored procedure
ADO Reference on microsoft.com
Another possible cause of this is debug statements. I just spent far too long trying to work out why this wouldn't work for me, the Proc on the database worked fine, the data it was supposed to insert was inserted, the VBA code worked fine, but there was nothing in the recordset.
Final solution was to go through the procs that had been built and remove the PRINT statements.
To test if this is the problem, run your proc on SQL Server manually, then look at the messages tab of the results, if there's anything there other than "Command(s) completed successfully." you need to eliminate those messages. "SET NOCOUNT ON" will get rid of the row count messages, but there may be others.
I'm assuming that after 5 years the OP has solved this particular problem, so this is just for anyone like me that finds this while searching for the same problem.
I also ran into this with a stored procedure. Did you SET NOCOUNT = OFF; at the bottom of your code? That is what worked for me after lots of googling. Also, if you have any other code that runs, you have to wrap it in Nocount = on/off, INCLUDING insert and update statements. You would think that an insert statement wouldn't matter but wrapping the code that way is what kept me from committing suicide today.
In our shop we often use lines like this in our stored procedures to assist with debugging:
RAISERROR('Debug message here',0,1) WITH NOWAIT;
This also breaks opening a recordset in Excel vba. I believe the complete answer for this question is, in the stored procedure:
use SET ROWCOUNT OFF
remove all PRINT statements
remove all RAISEERROR statements used for debugging (ie severity of 0)
Stored procedure, and using ADO to connect, i'm having problems with the quotes however..
x = "text'"
If instr(x,"'") then x=replace(x,"'","''")
'x = "text''" at this point
Set Rs = Server.Createobject("adodb.recordset")
Rs.Open "Select_SP #name='" & x & "'"
I thought i was doing this right.. But I guess not, because i'm getting this error:
Microsoft OLE DB Provider for SQL Server error '80040e14'
SELECT ID from Table where Name='text''
Shouldn't it be
Name = 'text'''
Why isn't SQL recognizing the double quotes using ADO?
The Select_SP Uses something like this:
SET #sql = 'SELECT ID from Table where Name='''+#name+''''
Exec(#sql)
Do I have this SP written correctly?
The short answer is, don't call procedures the way you're doing it. Use a Command instead. This is from memory, since I don't have a Windows system in front of me at the moment, but it should work:
Dim cmd
Set cmd = Server.CreateObject("ADODB.Command")
Set Cmd.ActiveConnection = myConnectionVariableHere
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "Select_SP"
'Note: if the above two lines don't work, replace them with the following
'cmd.CommandType = adCmdText
'cmd.CommandText = "Select_CP #name=?"
cmd.Parameters.Append cmd.CreateParameter("name", adVarChar, adParamInput, len(x), x)
Rs.Open cmd
Now, all that said, I can't tell you why your procedure is giving that particular output since you're not showing us the code for it. But I'm fairly certain ADO isn't going to convert a pair of single quotes into a double quote - not sure why you'd expect it to.
Edit after seeing the OP's edit. Don't execute SQL that way unless you absolutely have to. You will only give yourself grief. I don't know what database you're using and that procedure syntax doesn't look familiar, but in Oracle you could write something like:
PROCEDURE sql_test(MyName IN VARCHAR2, MyCursor OUT SYS_REFCURSOR) IS
BEGIN
OPEN MyCursor FOR SELECT id FROM some_table WHERE name = MyName;
END;