VBScript / ADODB Syntax Issue with adArray? - sql-server

I'm hoping somebody could provide me with some fresh eyes on my vb script. The main purpose of this script is to execute a stored procedure using some parameters.
The error I get is
'Expected end of statement'
I haven't done much VB scripting but from what I have found so far - this error has been down to some kind of syntax issue. I've looked over this script for many hours and can't see anything obvious. I can only assume it's down to the declaration of adArray (which doesn't look right in my eyes but I haven't been able to find ANY examples of this being declared). This error has only been introduced when I started adding more parameters.
Code:
Const adVarChar = 200
Const adParamInput = 1
Const adArray = 0x2000
Dim cmd
Dim sp
Dim intCode
Dim addIn
Dim groupCode
Dim screens
Dim arrScreens
arrScreens=split(LF06,",")
Set cmd=CreateObject("ADODB.Command")
sp="vfile_dev.dbo.vfp_groupReorder"
Set intCode=CreateObject("ADODB.Parameter")
intCode.Direction=adParamInput
intCode.name="#p_intCode"
intCode.Size=100
intCode.Type=adVarChar
intCode.Value=LF03
Set addIn=CreateObject("ADODB.Parameter")
addIn.Direction=adParamInput
addIn.name="#p_addIn"
addIn.Size=100
addIn.Type=adVarChar
addIn.Value=LF04
Set groupCode=CreateObject("ADODB.Parameter")
groupCode.Direction=adParamInput
groupCode.name="#p_groupCode"
groupCode.Size=100
groupCode.Type=adVarChar
groupCode.Value=LF05
Set screens=CreateObject("ADODB.Parameter")
screens.Direction=adParamInput
screens.name="#p_screens"
screens.Size=100
screens.Type=adArray
screens.Value=arrScreens
With cmd
.ActiveCOnnection = "Provider='sqloledb';Data source='xxx';Integrated Security='SSPI';"
.CommandType = 4
.CommandText = sp
.Parameters.Append intCode
.Parameters.Append addIn
.Parameters.Append groupCode
.Parameters.Append screens
.Execute
End With
Set cmd = Nothing
Set sp = Nothing
Set intCode = Nothing
Set addIn = Nothing
Set groupCode = Nothing
Any help would be much appreciated. Thanks.
EDIT:
For those interested - my solution was to ditch adArray and pass my data through as a comma delimited varchar. I then handle this data in my stored procedure with a simple split function.

I'm fairly sure adArray although defined in the ADO constants is not supported by ADO and was added for future compatibility.
From MSDN - ADO API Reference - DataTypeEnum
A flag value, always combined with another data type constant, that indicates an array of the other data type. Does not apply to ADOX.
This definition suggests it should only be used with another ADO DataTypeEnum constant value to denote an Array of that Data Type.
Although there is some suggestion that ORing the value with another DataTypeEnum constant should work, I found this from the 12 years ago.
From the ADO public newsgroup (microsoft.public.ado)
Discussion: how to use AdArray data type with sql server 7 and stored procedures
Date: 2003-09-29 19:24:10 UTC
Hi David,
adArray is not supported in ADO and was created for future compatibility.
What do you need to achieve? Maybe we could help with another solution
--
Val Mazur
Microsoft MVP
Check Virus Alert, stay updated
http://www.microsoft.com/security/incident/blast.asp
Useful Links
How to use ADODB parameterized query with adArray data type in VBScript? - Suggestion that using adArray is possible.
Carl Prothman - Data Type Mapping - My go to resource for ADO data Type Mapping.

Related

How to call SQL Server stored procedure with datetime parameter from VBA?

I have a SQL Server stored procedure that accepts two input parameters of datetime datatype. I want to call it from Excel VBA.
VBA doesn't have a datetime type so I've tried something like this:
Dim spCommand As ADODB.Command
Set spCommand = New ADODB.Command
spCommand.ActiveConnection = cnn
spCommand.CommandText = "myProcedureName"
spCommand.CommandType = adCmdStoredProc
spCommand.Parameters.Refresh
spCommand.Parameters(1).Value = "6/1/2016" 'DateValue("2016-06-01") + TimeValue("00:00:00")
spCommand.Parameters(2).Value = "6/1/2016" 'DateValue("2016-06-01") + TimeValue("23:59:59")
spCommand.Execute
But I get this error:
How can I solve it?
EDIT 1
After followeing what #Joe suggested, I opened my debugger and get this, but error is still there:
Use the DateSerial function:
spCommand.Parameters(1).Value = DateSerial(2016,1,6)
and, if you need it, optionally the TimeSerial function:
spCommand.Parameters(2).Value = DateSerial(2016,1,6) + TimeSerial(23,59,59)
VBA doesn't have a datetime type
Yes, it does:
Dim dtDateTime as Date
dtDateTime = DateSerial(2016,1,6) + TimeSerial(23,59,59)
UPDATE
Following your edit, I see that the Parameters collection has three items. The first is a return parameter, and the second and third are input parameters of type adDBTimeStamp.
Also the Command.NamedParameters property is false, so you probably can't use named parameters as you seem to be attempting to do in the screenshot accompanying your edit.
UPDATE 2
VBA collections are indexed starting at 1, not 0, so you should be specifying your parameters as:
spCommand.Parameters(2).Value = ...
spCommand.Parameters(3).Value = ...
I think the above is wrong. Standard VB/VBA collections are indexed, starting at 1. I believe the rationale, mistaken in my view, was that 1-based indexes are "more intuitive".
When ADO was developed, I believe Microsoft realized that 1-based collections had been a mistake, and decided to make ADO collections 0-based. This makes them inconsistent with standard collections, but "more intuitive" to, say, javascript developers. What an inconsistent mess.
If I'm right (and it's a long time since I used ADO, and I haven't tested this), then you need:
spCommand.Parameters(1).Value = ...
spCommand.Parameters(2).Value = ...
which should eliminate your 3265 error, but doesn't solve your original problem. Your debug trace appears to show that the parameters have been set to the expected values. I can only suggest you try to generate an MCVE, including a simplified version of the Stored Procedure in question.

Access pass through query

I have a very easy Stored Procedure in SQL (this is my first one!) and I can run it after researching how to accomplish this. Essentially, it deletes all the current records in a table and appends a new set of records. (I have read that having two queries in the same Stored Procedure may cause the problem I am having.)
This works very well the first time I run it. I make a change to the open table to see if it works the second time. It does not. I can even close the database, reopen and rerun the code and it still doesn't give me what I expect.
What I have figured out is that if I try to open the "CurrentProc" query in the Access window, I get an error that says "Pass-through query with ReturnsRecords property set to True did not return any records." That's fine, because after I hit "OK", the code will work again and give me the expected return.
My question is what can I do with the code below so that I can make whatever change I want to in a table, but have it all reset whenever I call the function without having to open the CurrentProc query and getting the error message. (Yes, I have tried opening the query with OpenQuery, but that didn't work either.)
Please understand that, while I have been an Access developer for (yikes!) nearly 20 years, this is my first time trying to use SQL stored procedures. Running this same query in Access takes about 45 minutes. Using the Stored procedure, it literally takes seconds! I will be adapting this for use with much larger recordsets and rewriting some Access code as SQL stored procedures to leverage this power, so any ideas you can provide will be greatly appreciated.
Dim qdf As dao.QueryDef
Dim dbs As dao.Database
Dim rst As dao.Recordset
Dim sSQL As String
strConnect = "ODBC;DRIVER={SQL Server}" _
& ";SERVER=ourserver\equipment" _
& ";DATABASE=BDS"
Set dbs = CurrentDb
Set qdf = dbs.QueryDefs("CurrentProc")
qdf.Connect = strConnect
qdf.SQL = "exec AppendtoFRPbyModel1"
DoCmd.OpenTable "FRP by Model1"
Set rst = Nothing
Set qdf = Nothing
Set dbs = Nothing
Josetta
Here's what worked:
I changed ReturnsRecords to "no" and that didn't do anything, HOWEVER, when I added "DoCmd.OpenQuery "CurrentProc" I did not receive an error (as I did before when I tried to run it outside of code) and I did get the expected results every time. Thank you!!

Unable to query nVarChar(Max) field in Access 2010

have used Stack Overflow as a resource hundreds of times, but my first time posting a question for some help!
I've got a table in SQL Server 2005 which contains 4 nVarChar(Max) fields.
I'm trying to pull out the data from an Access (2010) VBA Module using ADO 2.8
I'm connecting using SQL driver SQLNCLI10
(I can't use a linked table, as the 'table' I will ultimately be querying is a Table-Valued Function)
When I then print / use the recordset, the data is getting jumbled and concatenated with other fields in the same record - with a bunch of obscure characters thrown in.
The VBA: (various other methods were tried with the same result)
Sub TestWithoutCasting()
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim i As Integer
cn.Open "Data Source=ART;DataTypeCompatibility=80;MARS Connection=True;"
Set rs = cn.Execute("SELECT * FROM JobDetail WHERE JobID = 2558 ORDER BY SeqNo ASC")
Do While Not rs.EOF
For i = 1 To rs.Fields.Count
Debug.Print rs.Fields(i).Name & ": " & rs.Fields(i).Value
Next i
rs.MoveNext
Loop
End Sub
Example Output:
SeqNo: 1
CommandID: 2
Parameter1: 2 Daily Report é [& some other chars not showing on here]
Parameter2: [Null]
Parameter3: [Null]
Parameter4: [Null]
Description: Daily Report
Active: False
Expected Output:
SeqNo: 1
CommandID: 2
Parameter1: SELECT Day_Number ,Day_Text ,Channel_Group_ID [...etc]
Parameter2: [Null]
Parameter3: [Null]
Parameter4: [Null]
Description: Daily Report
Active: False
So, it's grabbing bits of data from other fields instead of the correct data (in this case, it's an SQL statement)
I then tried casting the nvarchar(max) fields as text at source
View Created:
CREATE VIEW TestWithCast
AS
SELECT jd.JobID, jd.SeqNo, jd.CommandID
,cast(jd.Parameter1 as text) as Parameter1
,cast(jd.Parameter2 as text) as Parameter2
,cast(jd.Parameter3 as text) as Parameter3
,cast(jd.Parameter4 as text) as Parameter4
,jd.[Description]
,jd.Active
FROM JobDetail jd
Now, I initially had some luck here - using the same code as above does bring back data - but when I use this code in my main code (which jumps in & out of other procedures); as soon as I've queried the first result of the recordset, it appears to wipe the rest of the records / fields, setting them to Null. I also tried setting the value of each field to a variable whilst the rest of the vba runs before getting the next record - but this doesn't help either.
It almost feels like I need to dump the recordset into a local Access table, and query from there - which is a bazaar workaround for what is already a workaround (by casting as text).
I there something I'm completely missing here, or do I indeed need to cast as text and load to a local table?
Thanks for any help - it's driving me mad!
ps. Hope I've given the right level of detail / info - please let me know if I missed anything key.
EDIT:
Yikes, I think I've done it / found the issue...
I changed the driver to SQLSRV32 (v6.01) - and seems to work fine directly against the text casted field.
So... why would it work with an older driver but not the newer 'recommended' (by various sources I read) as the one to use.
And... will there be a significant drawback in using this over the native client?
EDIT 2:
Ok, I've tried a few drivers on a few machines, in each case with both the TEXT CASTING and Directly to VARCHAR MAX..
[On my windows 7 machine w/ SQLSMS 2008]
SQL Native Client 10.0 - Neither method works reliably with this driver
SQL Server 6.01 - BOTH methods appear to work reliably - further testing needed though
[On our production server w/ SQLS 2005]
SQL Native Client (v2005.90) - Does not work at all with varchar(max), but DOES work with text casting
SQL Server (v2008.86) - BOTH methods appear to work reliably - further testing needed though
This should make deployment interesting!
It's not a real answer, because I did not test it, but ... You are using a "DataTypeCompatibility=80" parameter in your connection. As far as I know, DataTypeCompatibility=80 refers to SQL Server 2000, where the nvarchar(max) field type was still not implemented.
I had the same problem, solved it by converting the field to an nvarchar(1000). Would be an easy, compatible solution for your problem if 1000 chars is enough.

Is this Enough to Secure againts SQL Injections?

I'm trying to secure a older classic asp web site (that has about 1,000 (.asp) pages) using MS SQL 2008 R2 (Express Edition).
I found a code (see below) on how to Parameterized Queries and the code looks to be the easiest for me to understand and use on all of the pages that need to be changed.
If I was to convert all of the ms sql queries (that will look something like the code below) will that be enough to protect against an ms sql injection attack ? or is there more that I will need to add/change ?
set objCommand = Server.CreateObject("ADODB.Command")
strSql = "SELECT * FROM users WHERE username=? AND password=?"
...
cmd1.Parameters(0) = Request.Form("login")
cmd1.Parameters(1) = Request.Form("password")
...
Its been a while since I've seen the old Adodb command syntax, but I think you would want something like:
set objCommand = Server.CreateObject("ADODB.Command")
strSql = "Select * From users where username=#username and password=#password"
objCommand.Parameters.Append.CreateParameter
("#username", adVarChar, adParamInput, 50, Request.Form("login"))
objCommand.Parameters.Append.CreateParameter
("#password", adVarChar, adParamInput, 50, Request.Form("password"))
As always, don't create dynamic sql statements without a type safe parameter encoding, I think even old school ADO provides this via CreateParameter.
Yes, the code you provided in your question is secured against Sql Injections.
However, as noted in this article on mitigating Sql Injection, your code will have "a minor performance issue because ADODB is going to have to made a round-trip to SQL to figure out the parameter type before it can execute the query."
You can resolve this by explicitly specifying the parameter type in your code using CreateParameter

Retrieving varbinary output from a query in sql server into classic ASP

I'm trying to retrieve a varbinary output value from a query running on SQL Server 2005 into Classic ASP. The ASP execution just fails when it comes to part of code that is simply taking a varbinary output into a string. So I guess we gotta handle it some other way.
Actually, I'm trying to set (sp_setapprole) and unset (sp_unsetapprole) application roles for a database connection. First I'd set the approle, then I'd run my required queries and finally unset the approle. During unsetting is when I need the cookie (varbinary) value in my ASP code so that I can create a query like 'exec sp_unsetapprole #cookie'. Well at this stage, I don't have the cookie (varbinary) value.
The reason I'm doing this is I used to get 'sp_setapprole was not invoked correctly' error when trying to set app roles. I've disabled pooling by appending 'OLE DB Services = -2;Pooling=False' into my connection string.
I know pooling helps performance wise but here I'm facing big problems.
Please help me out to retrieve a varbinary value into an classic ASP file or suggest a way to set & unset app roles. Either way solutions are appreciated.
Thanks,
Nandagopal
Maybe you could try casting your VARBINARY datatype to IMAGE, which is old enough that it might be able to be used by "Classic" ASP. Just a thought. Could be wrong.
The easiest way is to store the varbinary value from sp_setapprole in an ADO recordset and keep that value around, not worrying about the actual type (you should be able to store the value in an array of bytes, though).
Then, when you need to place that varbinary value in your final sp_setapprole, you can simply use the recordset value. Do this by invoking sp_setapprole using a ADODB.Command object:
'--untested, syntax could be slightly off'
Set myCommand = Server.CreateObject("ADODB.Command")
With myCommand
.ActiveConnection = myConnection
.CommandType = adCmdStoredProc
.CommandText = "sp_unsetapprole"
.Parameters.Append _
.CreateParameter("#Cookie", adLongVarBinary, adParamInput, fileSize)
.Parameters(str_ParamName).Attributes = adFldLong
.Parameters(str_ParamName).AppendChunk myRecordset.Fields("Cookie").Value
.Execute
End With
Perhaps you can also get some insights from this Stackoverflow question.

Resources