Reputation: 12951
We have this utility method in a VB6 COM library for executing parameterised SQL:
Public Sub ExecSQL(ByVal strSQL As String, ParamArray varParams() As Variant)
'snip - ADODB data access
End Sub
Where varParams is a two dimensional array of SQL parameter information. Example usage would be:
ExecSQL("SELECT * FROM People WHERE Name = ?", Array("@p1", adVarChar, 10, "Smith"))
This is tried and tested code and works fine in normal usage. I am now in an unusual situation where the SQL string is a configurable value and could contain any number of parameters, so what I need to do is pass an unknown number of arguments in to the ParamArray. My attempt so far (simplified) is:
Function ExecConfigurableSql(sqlString As String, parameterValues() As String)
Dim parameters() As Variant
ReDim parameters(UBound(parameterValues)) As Variant
For i = 0 To UBound(parameterValues)
parameters(i) = Array("@p" + CStr(i), adVarChar, 1000, parameterValues(i))
Next
ExecSQL(sqlString, parameters) 'Type Mismatch
End Function
The attempt to execute the SQL throws a Type Mismatch error. Is there a way to pass an Array in to a function which expects a ParamArray? Or am I making an altogether separate mistake somewhere?
This is what the parameters look like with a dynamically built up array:
And this is what they look like when passed with comma-separated ParamArray syntax (which works):
The structure looks the same to me.
Upvotes: 2
Views: 5291
Reputation: 1134
First, you never needed ParamArray in ExecSQL() in the first place as you're always passing one argument on the stack in addition to strSQL, i.e. an array of variants, which is
Array("@p1", adVarChar, 10, "Smith")
in the second listing. ParamArray is used to pass an undefined number or parameters on the stack, i.e. to be able to make calls like:
ExecSQL "SELECT * FROM People WHERE Name = ?", "@p1", adVarChar
ExecSQL "SELECT * FROM People WHERE Name = ?", "@p1", adVarChar, 10, "Smith"
ExecSQL "SELECT * FROM People WHERE Name = ?", "@p1"
ParamArray just take all the arguments passed on the stack and put them in an array for you.
So, you could have defined ExecSQL() as follow and it would have been the same thing provided your code get adapted to one-less Array() layer around varParams:
Public Sub ExecSQL(ByVal strSQL As String, varParams() As Variant)
' snip - ADODB data access '
End Sub
That being said:
Currently, the code in ExecConfigurableSql() transforms an array of strings (I presume field names), into the format expected by ExecSQL(), except that the (outer) array can (and will) contain more than one element of the sort Array("@p1", adVarChar, 10, "Smith").
Can ExecSQL() handle this? => Problem #1
[by the way, are all the fields 1000-char long??]
Problem #2: parameters is fine when you look at it from within ExecConfigurableSql(), but once you pass if to ExecSQL(), the ParamArray wraps it inside another array, so you really end up with (once in ExecSQL()) with something like this:
Now, you have to put the (unknown number of) parameters in an array, 'cause, well, you can't pass them on the stack to ParamArray since you don't know in advance the number of them. So you cannot remove the extra Array() wrapping from there.
You could get rid of the ParamArray in ExecSQL(), but that would break your existing ExecSQL() calls (for which varParams would only be wrapped once in an Array() instead of twice).
Knowing all this, you have two choices:
(1) Keep the declares as-is, and have ExecConfigurableSql() make multiple ExecSQL() calls within a For loop (btw, you declared it as a Sub so I presume it doesn't return any value); e.g.
Function ExecConfigurableSql(sqlString As String, parameterValues() As String)
For i = 0 To UBound(parameterValues)
Call ExecSQL(sqlString, Array("@p" + CStr(i), adVarChar, 1000, parameterValues(i))
Next
End Function
or
(2) Do the other way around, to improve the logic & consistency
Function ExecConfigurableSql(sqlString As String, varParamsArray() As Variant)
Dim varParams() As Variant
For i = 0 To UBound(varParamsArray)
varParams = varParamsArray(i)
' snip - ADODB data access '
Next i
End Function
Take the code from within ExecSQL() and put it in ExecConfigurableSql() where indicated - IMPORTANT: You have to update your code to account for the fact that parameters have one less Array() wrapped around them.
For ExecSQL(), remove the ParamArray keyword and treat the method as a special case of ExecConfigurableSql() where only one member is provided, i.e.:
Function ExecSQL(sqlString As String, varParams() As Variant)
Call ExecConfigurableSql(sqlString, Array(varParams))
End Function
Upvotes: 1