ADO Oracle Operation is not allowed when the object is closed - oracle

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

Related

VBScript unaltered Date and Time for database migration

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

Parameterized query in VBScript referring OracleDB

I have a parameterized query which is giving
"ORA-01008: not all variables bound" error.
Dim Conn
Dim Cmd
Dim RS
Dim strID
Dim param
strID = Request.QueryString("id")
Set Conn = Server.CreateObject("ADODB.Connection")
Conn.Open strConnect
Set Cmd = Server.CreateObject("ADODB.Command")
Cmd.CommandText = "SELECT column_name FROM table WHERE (id = :id)"
Set param = Cmd.CreateParameter("id", adVarChar , adParamInput ,50 , strID)
Cmd.Parameters.Append param
Cmd.CommandType = adCmdText
Set Cmd.ActiveConnection = Conn
Set RS = Cmd.Execute()
I'm trying to modify in syntax in several ways, then it is giving
ORA-00936: missing expression
Please help me to get out of this. For your information, there is no problem with connection as i am able to connect with normal query.
a few things to check:
1) try hard coding a value for strID, so instead of:
strID = Request.QueryString("id")
try
strID = 100
2) double check your column definitions and make sure you're selecting from a varchar(50) field
3) make sure you have adovbs.inc referenced on your page for the ADO constants definitions
Thanks #Lankymart, luckily i got solution for this as below. It is working fine for me and sorry for the delay in posting the answer, my issue resolved 2 hours ago.
Dim Conn
Dim Cmd
Dim RS
Dim strID
Dim param
strID = Request.QueryString("id")
Set Conn = Server.CreateObject("ADODB.Connection")
Conn.Open strConnect
Set Cmd = Server.CreateObject("ADODB.Command")
With Cmd
.CommandText = "SELECT column_name FROM table WHERE id = ?"
.Parameters.Append .CreateParameter(,200, 1 ,50 ,strID)
Set .ActiveConnection = Conn
End With
Set RS = Cmd.Execute()

ADO recordset not populated by ADO command execute method

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

Stored procedure returned (out) null

I try to get return value of this Oracle stored procedure with VBScript, but everytime returned NULL (nothing), not populate cmd("retVal"). Where is my mistake?
If this stored procedure works fine it must return a integer value like 1 or -2 parameters are passed and change db data, but not return any value in OUT param in stored procedure.
Set connORA = Server.CreateObject("ADODB.Connection")
connORA.ConnectionString = "conninfo"
connORA.Open
Set cmd = Server.CreateObject("ADODB.Command")
Set cmd.ActiveConnection = connORA
cmd.CommandText = "{call procedureName(486954,100002335,'0','0',0,'12-03-2015','X','IPTAL_EDILDI',443,'SAT',?)}"
cmd.Prepared = True
cmd.Parameters.Append cmd.CreateParameter("retVal", 131, 2)
cmd.Execute
Response.Write cmd("retVal")
Set oCmd = Nothing
connORA.Close
Set connORA=nothing

PL/SQL stored procedure out cursor to VBA ADODB.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"

Resources