I am doing some clean up to protect from SQL injection attacks happening in a older internal website that uses ASP. Here's the gist of it all in code...
Database connection is setup in a separate asp file named connect.asp
<%
on error resume next
Set DB = Server.CreateObject("ADODB.Connection")
DB.CommandTimeout = 180
DB.ConnectionTimeout = 180
connStr = "Provider=SQLOLEDB;Data Source=xxx-xxxx-xxxxxx;Initial Catalog=xxxx;Persist Security Info=True;User ID=xxxxx;Password=xxxxxxxxxxxx;"
DB.Open connStr
' Check DB connection and go to Error Handler is error exists
if DB.state=0 then
Response.Write "<p>Cannot connect to database.</p>"
TrapError Err.description
Response.end
end if
%>
This works and the db connections is opened.
I have a file named DBFunctions.asp that I use to sort of map functions to stored procedures and their parameters. I am trying to use the function below to return a ADO recordset to another asp front end page.
Function GetFacilityByFID(fid)
set rs = server.CreateObject("ADODB.Recordset")
Set cmd = Server.CreateObject("ADODB.Command")
Set cmd.ActiveConnection = DB
cmd.CommandText = "GetFacilityByFID"
cmd.CommandType = adCmdStoredProc
cmd.Parameters.Append cmd.CreateParameter("#FID", adVarChar, adParamInput, 20)
cmd("#FID") = fid
Set rs = cmd.Execute
Set GetFacilityByFID = rs
End Function
Here is the code from the calling front end asp page, facDetail.asp
<%
Dim FID, FCBI, Error
FID = Request("FID")
FCBI = Request("FCBI")
' Check DB connection and go to Error Handler is error exists
if DB.state=0 then
Response.Write "<p>Cannot connect to database.</p>"
TrapError Err.description
Response.end
else
if FID then
Set RS = GetFacilityByFID(FID)
elseif FCBI then
Set RS = GetFacilityByFCBI(FCBI)
end if
if RS.EOF then
Response.Write "<BR><p class=alert>No record found</p>"
response.End
end if
end if
%>
The calling page is displaying that there are no records returned
but the stored procedure works when executed in SSMS.
Updated code
Here's the SQL Code for the GetFacilityByFID stored procedure.
CREATE PROCEDURE [dbo].[GetFacilityByFID]
#FID varchar(20)
AS
BEGIN
SET NOCOUNT ON;
SELECT [FAC_CBI_NBR]
,[FAC_ID]
,[FAC_TYPE]
,[FAC_SUBTYPE]
,[FAC_REGION]
,[FAC_COST_CENTER]
,[FAC_SUPPLY_CODE]
,[FAC_UPLINE]
,[FAC_SERVICE]
,[FAC_LOCATION_NAME]
,[FAC_LOCAL_ADDR1]
,[FAC_LOCAL_ADDR2]
,[FAC_LOCAL_CITY]
,[FAC_LOCAL_STATE]
,[FAC_LOCAL_ZIP]
,[FAC_MAIL_ADDR1]
,[FAC_MAIL_ADDR2]
,[FAC_MAIL_CITY]
,[FAC_MAIL_STATE]
,[FAC_MAIL_ZIP]
,[FAC_COUNTRY]
,[FAC_PHONE]
,[FAC_FAX]
,[FAC_MANAGER]
,[FAC_CONTACT]
,[FAC_CONTACT_PHONE]
,[FAC_CONTACT_EXT]
,[FAC_CONTACT_EMAIL]
,[FAC_COMMENTS]
,[FAC_CHANGED_BY]
,[FAC_LAST_UPDATE]
,[FAC_MAILOUT]
,[FAC_CONTRACTION]
,[FAC_PROPERTY_CODE]
,[FAC_ATTN_TO]
FROM [cbid].[dbo].[FACILITY]
WHERE [FAC_ID]=#FID
END
GO
Can anyone tell me what is going wrong? I have been looking at this too long and have grown frustrated with it.
Any help will be greatly appreciated!
Edit:
Current Status of issue: getting the following from the ADO provider
Error Number: -2147217904 Error Desc: Procedure or function 'GetFacilityByFID' expects parameter '#FID', which was not supplied. Error Source: Microsoft OLE DB Provider for SQL Server
You need to specify the size.
From CreateParameter Method (ADO)
If you specify a variable-length data type in the Type argument, you
must either pass a Size argument or set the
Size
property of the Parameter object before appending it to the
Parameters collection; otherwise, an error occurs.
cmd.Parameters.Append cmd.CreateParameter("#FID", adVarChar, adParamInput, Len(fid))
Before going any further, the first thing is to confirm if your stored procedure GetFacilityByFID actually returns a Recordset? Most likely it does not. If it only return a single string value, you should modify Function GetFacilityByFID(fid) to something like below:
Function GetFacilityByFID(fid)
Set cmd = Server.CreateObject("ADODB.Command")
Set cmd.ActiveConnection = DB
cmd.CommandText = "GetFacilityByFID"
cmd.CommandType = adCmdStoredProc
cmd.Parameters.Append cmd.CreateParameter("#returnVal", adVarChar, adParamOutput, 255, "")
cmd.Parameters.Append cmd.CreateParameter("#FID", adVarChar, adParamInput)
cmd("#FID") = fid
cmd.Execute
GetFacilityByFID = cmd("#returnVal")
End Function
I rewrote the function using some of the examples I found and some of LankyMart's suggestions. Thanks everyone for your help.
Here's the working code...
Function GetFacilityByFID(fid)
Set rs = server.CreateObject("ADODB.Recordset")
Set cmd = Server.CreateObject("ADODB.Command")
Set cmd.ActiveConnection = DB
cmd.CommandText = "GetFacilityByFID"
cmd.CommandType = adCmdStoredProc
cmd.CommandTimeout = 900
set prm = cmd.CreateParameter("#FID",adVarChar, adParamInput, 20, fid)
cmd.Parameters.Append prm
rs.CursorLocation = adUseClient
rs.Open cmd, , adOpenForwardOnly, adLockReadOnly
Set GetFacilityByFID = rs
On Error GoTo 0
End Function
The way you're setting the parameters looks no right, try this instead:
Dim param
Set param = cmd.CreateParameter("#FID", adVarChar, adParamInput)
cmd.Parameters.Append param
param.Value = fid
You can read more here: https://msdn.microsoft.com/pt-br/library/ms675860(v=vs.85).aspx
Hope it helps
Related
I'm doing a MSSQL to MySQL data migration. My date/time value is exactly how it needs to be in both databases. However, VBScript converts this (by default):
2016-01-06 10:26:30.363
To this (which errors on INSERT):
1/6/2016 10:26:30 AM
I'm aware I can construct the value again with Year(), Month(), etc. Anyone know how to get this date/time value unaltered from the database using VBScript?
UPDATE: Per the "show my code" comment, here's a section:
strSQL = "SELECT * FROM Users"
Set objRS = CreateObject("ADODB.RecordSet")
objRS.Open strSQL, objConn,3,3
Do While Not objRS.EOF
sUserId = objRS("UserId")
sCreatedDate = objRS("CreatedDate") '<-- At this point, it's "converted" already
wscript.echo sCreatedDate '<-- This displays 1/6/2016 10:26:30 AM format
insertSQL = "INSERT INTO northwind.usersetting (UserId,CreatedDate) "
insertSQL = insertSQL & "VALUES ('"&sUserId&"','"&sCreatedDate &"');"
objConn2.Execute = (insertSQL)
objRS.MoveNext
Loop
Just your standard vbscript. I think I can get away with this in MSSQL, but MySQL doesn't like it. In the meantime, I have done this to work around the issue:
Function FormatDate4Insert(date)
FormatDate4Insert = Year(date) & "-" & Month(date) & "-" & Day(date) & " " & Hour(date) & ":" & Minute(date) & ":" & Second(date)
End Function
I would love to do a straight non-conversion, but vbs seems to convert no matter what I do. I tried converting to string and a few other things with no joy...
P.S. I'm not sure what you mean by database import export mechanisms. However, I tried the MySQL migration tool as well as exporting and import mechanisms (to csv...with different delimiters and such... and even a json export, massaging the data with Notepad++ and Excel, etc) and can't get the data to jive with my selective import. I can migrate an entire database without issue for the most part, but simply want to do the data from an individual table. When I kill too much time, I usually just fall back to vbscript or whatever scripting makes sense.
I find both databases more forgiving and finicky in some areas. However, with MSSQL to MySQL, I have to convert empty values to "NULL" and True/False to their bit values (e.g. b'0') and other little tweaks that scripting makes easier (at least for me).
UPDATE 2: The error as requested:
Microsoft OLE DB Provider for ODBC Drivers: [MySQL][ODBC 8.0(w)
Driver][mysqld-8.0.16]Incorrect datetime value: '1/6/2016 10:26:30 AM'
for column 'CreatedDate' at row 1.
I can't reproduce the other error, but I was also getting an error that was something similar to this:
ERROR: You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near '1/6/2016 10:26:30 AM'
That was in reference to the Insert statement.
Try a parameterized query:
' *** ADO ***
'---- CommandTypeEnum Values ----
Const adCmdText = &H0001
'---- DataTypeEnum Values ----
Const adInteger = 3
Const adDate = 7
'---- ParameterDirectionEnum Values ----
Const adParamInput = &H0001
Dim cmd
Dim sSQL
Set cmd = CreateObject("ADODB.Command")
Set cmd.ActiveConnection = cn ' Assumes cn is an actice ADOB.Connection object
cmd.CommandType = adCmdText
sSQL = "INSERT INTO northwind.usersetting (UserId, CreatedDate) VALUES (?, ?);"
cmd.CommandText = sSQL
Set prm = cmd.CreateParameter("UserId", adInteger, adParamInput, , UserId) ' Assuming UserId is a integer, adjust as needed
cmd.Parameters.Append prm
Set prm = cmd.CreateParameter("CreatedDate", adDate, adParamInput, , CreatedDate)
cmd.Parameters.Append prm
cmd.Execute , , adExecuteNoRecords
[Added]
Try the following snippet just to make sure that MySQL accepts a date value:
Set cmd = CreateObject("ADODB.Command")
Set cmd.ActiveConnection = cn ' Assumes cn is an actice ADOB.Connection object
cmd.CommandType = adCmdText
sSQL = "INSERT INTO northwind.usersetting (CreatedDate) VALUES (?);"
cmd.CommandText = sSQL
Set prm = cmd.CreateParameter("CreatedDate", adDate, adParamInput, , Now())
cmd.Parameters.Append prm
cmd.Execute , , adExecuteNoRecords
If this works, than you might want to try the following line in the first sample I posted:
Set prm = cmd.CreateParameter("CreatedDate", adDate, adParamInput, , CDate(CreatedDate))
Note the explicit date conversion with CDate().
The ADO constants at the top of the first code snippet are taken from a file called adovbs.inc, distributed by Microsoft. Unfortunately a quick search didn't bring up a download from MS. But here's a gist with its contents. Scroll down to '---- DataTypeEnum Values ----. There are a few other date related constants, e.g. Const adDBDate = 133. You might try out those and see if it yields the expected result.
[Added 2]
' *** ADO ***
'---- CommandTypeEnum Values ----
Const adCmdText = &H0001
'---- DataTypeEnum Values ----
Const adInteger = 3
Const adDate = 7
'---- ParameterDirectionEnum Values ----
Const adParamInput = &H0001
Dim strSQL
Dim cmd, rs
Set cmd = CreateObject("ADODB.Command")
Set cmd.ActiveConnection = cnMSSQL ' Assumes cnMSSQL is an actice ADOB.Connection object connected to the MS SQL server
cmd.CommandType = adCmdText
' Fill the recordset from a command object connecting to the source MS SQL
strSQL = "SELECT * FROM Users"
cmd.CommandText = strSQL
Set rs = cmd.Execute()
' Now use the Command object to fill the MySQL DB
Set cmd.ActiveConnection = cnMySQL ' ' Assumes cnMySQL is an actice ADOB.Connection object connected to the MySQL server
strSQL = "INSERT INTO northwind.usersetting (UserId, CreatedDate) VALUES (?, ?);"
cmd.CommandText = strSQL
Do While Not rs.EOF
' This assumes that the columns in source MS SQL also are named 'UserId' and 'CreateDate'.
' Adjust the rs.Fields("UserId").Value and rs.Fields("CreateDate").Value as needed
Set prm = cmd.CreateParameter("UserId", adInteger, adParamInput, , rs.Fields("UserId").Value) ' Assuming UserId is a integer, adjust as needed
cmd.Parameters.Append prm
Set prm = cmd.CreateParameter("CreatedDate", adDate, adParamInput, , rs.Fields("CreateDate").Value)
cmd.Parameters.Append prm
cmd.Execute , , adExecuteNoRecords
' Clear/reset the parameter collection
rs.MoveNext
Loop
I have an Excel 2013 VBA macro which needs to call an SQL procedure on an Oracle 12c database. The Oracle procedure is executed (it writes the result into a table) but in Excel I receive the error at Set rs = cmd.Execute:
Operation is not allowed when the object is closed
Below the code:
Dim v_userpw As String
Dim cnn As ADODB.Connection
Dim rs As New ADODB.Recordset
Dim cmd As New ADODB.Command
Dim l_userpw, l_reqid, l_pwhash, l_sighash As New ADODB.Parameter
Dim objErr As ADODB.Error
v_userpw = Cells(7, 1).Value
On Error GoTo err_test
'Set cnn = CreateObject("ADODB.Connection")
Set cnn = New ADODB.Connection
cnn.ConnectionString = "Provider=OraOLEDB.Oracle;Data Source=devdb;User ID=db1;Password=db1;"
cnn.Open
Set cmd = New ADODB.Command
Set cmd.ActiveConnection = cnn
Set l_userpw = cmd.CreateParameter("l_userpw", adVarChar, adParamInput, 1024, v_userpw)
cmd.Parameters.Append l_userpw
Set l_reqid = cmd.CreateParameter("l_reqid", adVarChar, adParamOutput, 1024)
cmd.Parameters.Append l_reqid
Set l_pwhash = cmd.CreateParameter("l_pwhash", adVarChar, adParamOutput, 1024)
cmd.Parameters.Append l_pwhash
Set l_sighash = cmd.CreateParameter("l_sighash", adVarChar, adParamOutput, 1024)
cmd.Parameters.Append l_sighash
'cmd.Properties("PLSQLRSet") = True
cmd.CommandText = "{CALL db1.genheader(?, ?, ?, ?)}"
Set rs = cmd.Execute
'cmd.Properties("PLSQLRSet") = False
Cells(8, 1) = rs.Fields("reqid").Value
Cells(9, 1) = rs.Fields("pwhash").Value
Cells(10, 1) = rs.Fields("sighash").Value
cnn.Close
err_test:
MsgBox Error$
For Each objErr In cnn.Errors
MsgBox objErr.Description
Next
cnn.Errors.Clear
Resume Next
The Oracle procedure looks like this:
create or replace procedure genheader (
l_userpw in varchar2,
l_reqid out varchar2,
--l_pwhash out raw,
--l_sighash out raw
l_vpwhash out varchar2,
l_vsighash out varchar2
)
I need to return the values in the predefined cells.
Does the procedure actually return a resultset? It looks like it just returns data using output parameters, so you wouldn't get the results from a recordset, you'd get them from the command parameters after the command executes.
cmd.Execute
Cells(8, 1) = cmd("l_reqid")
Cells(9, 1) = cmd("l_pwhash")
Cells(10, 1) = cmd("l_sighash")
Try testing the connection state to ensure it is open prior to assigning the connection to the ActiveConnection property of the command object. This can cause unstable behavior. If you don't want to do this in code, you can assign a breakpoint prior to the set line of code and check your locals window for the connection state. Also you need to specify the name of the Oracle Stored Procedure
cmd.Name = "genheader"
Cheers,
Boris
I'm not well versed in ASP, but that's what I've got to work with for the moment. I have a stored procedure that generates a resultset which i'd like to handle in classic ASP. I can see the resultset if I run the SP in SQL Management Studio with the same parameter so I know its working.
The stored procedure takes 1 parameter #part, i've testing with the following code and can see the SP is executed using SQL Profiler, but I get a '500 - Internal server error' returned from ASP instead of a resultset displayed.
How do correctly retrieve the resultset?
Dim fpart, objCommand, objRecordset
fpart = "SMF10320BRNU12"
Set objCommand = Server.CreateObject("ADODB.Command")
With objCommand
.ActiveConnection = db
.CommandText = "sp_movements"
.CommandType = 4
.CommandTimeout = 240
.Parameters.Append .CreateParameter("#part", 200, 1, 20)
.Parameters("#part") = fpart
Set objRecordset = .Execute
End With
for each x in objRecordset.Fields
Response.Write(x.name)
Response.Write(" = ")
Response.Write(x.value & "<br />")
next
Response.Write("<br />")
objRecordset.MoveNext
objRecordset.Close
Set objRecordset = nothing
objCommand.Close
Set objCommand = nothing
So I figured this out on my own. After digging though some logs I saw an error which I hadn't spotted previously:
Object_doesn't_support_this_property_or_method:_'Close'
Turns out I was incorrectly trying to close the command object: objCommand.Close
My working solution is:
Dim fpart, objCommand, objRecordset
fpart = "SMF10320BRNU12"
Set objCommand = Server.CreateObject("ADODB.Command")
With objCommand
.ActiveConnection = db
.CommandText = "sp_movements"
.CommandType = 4
.CommandTimeout = 240
.Parameters.Append .CreateParameter("#part", 200, 1, 20)
.Parameters("#part") = fpart
Set objRecordset = .Execute
End With
Do until objRecordset.EOF
for each x in objRecordset.Fields
Response.Write(x.name)
Response.Write(" = ")
Response.Write(x.value & "<br />")
next
Response.Write("<br />")
objRecordset.MoveNext
loop
objRecordset.Close
Set objRecordset = nothing
Set objCommand = nothing
Note:
This code also has a Do until loop around added to output all the rows from the recordset, but that wasn't strictly necessary to get something output on the page.
EDIT
Edited to reflect comments from Lankymart below, I did need to close the recordset.
To preface this post, I want to say that I am fairly new to Excel 2007 vba macros. I am trying to call an Oracle PL/SQL stored procedure that has a cursor as an output parameter. The procedure spec looks like this:
PROCEDURE get_product
(
out_cur_data OUT SYS_REFCURSOR,
rptid IN NUMBER,
scenario IN VARCHAR2
);
And I have written my macro as:
Sub GetProduct()
Const StartRow As Integer = 4
Dim conn As ADODB.Connection
Set conn = New ADODB.Connection
With conn
.ConnectionString = "<my connection string>"
.Open
End With
Dim cmd As ADODB.Command
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = conn
.CommandType = adCmdText
.CommandText = "{call their_package.get_product({out_cur_data 100},?,?)}"
.NamedParameters = True
.Parameters.Append cmd.CreateParameter("rptid", adNumeric, adParamInput, 0, 98)
.Parameters.Append cmd.CreateParameter("scenario", adVarChar, adParamInput, 4, "decline001")
End With
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
With rs
.CursorType = adOpenStatic
.CursorLocation = adUseClient
.LockType = adLockOptimistic
End With
Set rs = cmd.Execute
Cells(StartRow + 1, 1).CopyFromRecordset rs
rs.Close
conn.Close
End Sub
This does not work obviously, I get a run-time error '-2147217900 (80040e14): One or more errors occurred during processing of command.' So, OK.
I am looking for some guidance/advice on how to bring back that cursor into an ADODB.RecordSet. I don't think I have set up the output cursor correctly for "out_cur_data", but my searches online for any help have come up dry so far. Can any give me a basic working example to help me understand what I am doing wrong?
BTW... I do not have control of the stored procedure at all, it is from an external package.
Any help is really appreciated.
Thanks,
Doran
I think it should be this one:
With cmd
.Properties("PLSQLRSet") = TRUE
.ActiveConnection = conn
.CommandType = adCmdText
.CommandText = "{call their_package.get_product(?,?)}"
.NamedParameters = True
.Parameters.Append cmd.CreateParameter("rptid", adNumeric, adParamInput, 0, 98)
.Parameters.Append cmd.CreateParameter("scenario", adVarChar, adParamInput, 4, "decline001")
End With
...
Set rs = cmd.Execute
cmd.Properties("PLSQLRSet") = FALSE
Note:
Although their_package.get_product() takes three parameters, only two need to be bound because Ref cursor parameters are automatically bound by the provider.
For more information check Oracle documentation: Oracle Provider for OLE DB Developer's Guide - "Using OraOLEDB with Visual Basic"
I am having trouble running a query in Excel 2010 VBA code using Oracle OraOLEDB.Oracle Provider.
Certain queries work fine and return results, while others return no results...
I connect as such:
Set DBConnection = New ADODB.Connection
DBConnection.Provider = "OraOLEDB.Oracle"
DBConnection.CursorLocation = adUseClient
DBConnection.ConnectionString = "Data Source=" & TNSName & ";User Id=" & OraUserName & ";Password=" & OraPassWord & ";"
DBConnection.Open
I then try to query:
command2.ActiveConnection = DBConnection
command2.CommandText = "SELECT COL1,COL2,COL3 FROM table(MySchema.MyPackage.MyFunction('Param1'))"
command2.CommandType = adCmdText
Set QueryRecordSet = New ADODB.Recordset
QueryRecordSet.LockType = adLockReadOnly
QueryRecordSet.CursorType = adOpenDynamic
QueryRecordSet.Open command2
command2.Execute
and I get nothing...any ideas?
If I run a simple query like
select * From my_table
it works fine...it seems joins or other more complex queries don't compile??
Additionally, selecting from views does not work.
select * from my_view
Returns nothing
I'm putting this as an answer only because comment formatting doesn't allow me to add code.
Does the stored procedure work if you run it separately via the command object?
command2.CommandText = "MySchema.MyPackage.MyFunction"
command2.CommandType = adCmdStoredProc
command2.Parameters.Refresh
command2.Parameters.Item(1).Value = "Param1"
command2.Execute
Debug.Print command2.Parameters.Item(0).Value
I am not sure this is what you are looking for. I was looking for another answer and I know this works for me.
Set cmdSum = New adodb.Command
With cmdSum
Set .ActiveConnection = oCon
.Properties("PLSQLRSet") = True
.CommandText = "{CALL StoredProc(?,?)}"
.CommandType = adCmdText
.Parameters.Append .CreateParameter(, adVarChar, adParamInput, 10, Format(CDate(sTerm), "mm/dd/yyyy"))
.Parameters.Append .CreateParameter(, adVarChar, adParamInput, 10, Format(CDate(sEff), "mm/dd/yyyy"))
End With
Set Rs = cmdSum.Execute()
For c = 0 To Rs.Fields.Count - 1
Wk.Cells(3, c + 1) = Rs.Fields(c).Name
Next c
I ended up here after an Excel ADO query to Oracle would not work.
Although it states in the questions' comments that you can use ODBC to work-around this, it took me a while to figure out how to get the ODBC connection string working.
Here is an ODBC connection to Oracle that I got working.
dbConn = "ODBC;Provider=OraOLEDB.Oracle;" & _
"Data Source=odbc_connection_name;" & _
"User Id=user_id;" & _
"Password=user_pwd;" & _
"DBQ=tns_name;"
tns_name is what you named your connection within the tnsnames.ora file.
odbc_connection_name is what you named your odbc connection.
Then you can just use this to connect to Oracle using ADO like normal:
cn.Open dbConn
cn.CommandTimeout = 1000
rs.Open sql, cn
Note I also had to increase the CommandTimeout property, as ODBC's default timeout is relatively short.