1st off I am new to Delphi so this may be a "mundane detail" that's being over looked. [sorry in advance]
I am getting an 'Invalid Transaction Object' error when I attempt to run a transaction through a datasnap server connected to an Oracle 11g DB.
Due to the system details and the companies business plan we have elected not to use ClientDataSets to handle our transactions. Instead we are attempting to make the Snap server very generic and only handle data access by receiving queries and returning native types.
With that being said here is some sample code that is giving me fits:
function TSnapMethods.TransUpdate: boolean;
var
dbx: TDBXTransaction;
params:TParams;
begin
SqlCon.Open;
dbx := SQLCon.DBXConnection.BeginTransaction(TDBXIsolations.ReadCommitted);
try
params:= TParams.Create(self);
with Params.AddParameter do
begin
name:= 'param';
DataType:= ftWideString;
ParamType:= ptInput;
asString:= 'Bugsville';
end;
with Params.AddParameter do
begin
name:= 'var';
DataType:= ftWideString;
ParamType:= ptInput;
asString:= 'ZZZTOP';
end;
sqlcon.Execute('Update Name set City=:param Where Abrv=:var',params);
SQLcon.CommitFreeAndNil(dbx);//Breaks here...
result:= true;
except
Sqlcon.RollbackFreeAndNil(dbx);//Breaks here also...
result:= false;
end;
end;
By calling SQLCon.DBXConnection.BeginTransaction(), you're bypassing the setting up of internal TTransactionItem which is checked when the transaction is committed when you call SQLcon.CommitFreeAndNil() on the SQLConnection object. Notice that you're starting the transaction on the DBXConnection object but not committing it likewise.
Replace
SQLCon.DBXConnection.BeginTransaction()
with
SQLCon.BeginTransaction()
From another source I got this helpful information:
http://codeverge.com/embarcadero.delphi.ide/record-not-found-or-changed-by-another/1061559
For start transaction:
transaction:=Datamodule.SqlConection.BeginTransaction(TDBXIsolations.ReadCommitted);
For commit:
DataModule1.SqlConection.CommitFreeAndNil(Transacao);
To rollback:
DataModule1.SqlConection.RollbackIncompleteFreeAndNil(Transacao)
And use
RollbackIncompleteFreeAndNil
instead
RollbackIncompleteFreeAndNil
like referenced by:
http://docwiki.embarcadero.com/Libraries/Tokyo/en/Data.SqlExpr.TSQLConnection.RollbackIncompleteFreeAndNil
Please try this and report the results.
Related
I have a stored procedure in which a linked server is addressed only under certain conditions.
Especially on slow network connections, it becomes clear that the execution of the stored procedure is slowed down simply by naming (and not using) the linked server (runtime greater about 5 seconds).
DROP PROC IF EXISTS dbo.slowMystery;
GO
CREATE PROC dbo.slowMystery
AS
BEGIN
DECLARE #innerTimestamp DATETIME = GETDATE();
IF 1 = 0
BEGIN -- this will never be executed!
SELECT TOP 1
*
FROM
[remoteSqlInstance].someDb.dbo.someTable;
END;
ELSE
BEGIN
PRINT 'Hello world!';
END;
PRINT CONCAT('inner runtime: ', DATEDIFF(MILLISECOND, #innerTimestamp, GETDATE()));
END;
GO
Now, give it a try:
DECLARE #outerTimestamp DATETIME = GETDATE();
EXEC dbo.slowMystery;
PRINT CONCAT('outer runtime: ', DATEDIFF(MILLISECOND, #outerTimestamp, GETDATE()));
Result:
Hello world!
inner runtime: 0
outer runtime: 3733
As you can see in the example, the code regarding the linked sever is never executed.
The (inner) runtime of the stored procedure is therefore 0 milliseconds. However, the period for the entire execution is around 4 seconds (connection to the linked server via a slow vpn), although the linked server is de facto not addressed.
If you remove the SELECT-statement, it quickly becomes clear that the outer runtime is not determined by other factors:
...
IF 1 = 0
BEGIN -- this will never be executed!
PRINT 'This will never be executed!'
END;
...
Result:
Hello world!
inner runtime: 0
outer runtime: 0
I think this is because the local server checks if the linked server is available before executing the stored procedure.
Is there an option to turn off this check or some other way to get around these delays?
Thanks for any input.
Robert
Please excuse my ignorance as I am a total beginner.
I have created a basic RESTful Web Services Using PL/SQL, sample code below(which works perfectly fine)
ords.define_template(
p_module_name => 'restDemo',
p_pattern => 'updateClient');
ords.define_handler(
p_module_name => 'restDemo',
p_pattern => 'updateClient',
p_method => 'POST',
p_source_type => ords.source_type_plsql,
p_source => 'declare
w_clob clob := :body_text;
begin
ws.ws_interface.update_client(w_clob);
exception
when others then
package.ws_interface.g_result_rec.result_message := SQLERRM;
end;');
When ws_interface.update_client is initiated via the post request, in the initialization of ws_interface package, I am calling a routine thats determines if the client can be updated or not (based on access of the user logged in to the DB).
If the user does not have access to execute the updateClient service - I want to terminate the service call, which I have no idea on how to go about do this.
So my question is:
How do I go about terminating the current active updateClient webservice call ?
In your ws_interface package, if the client can not be updated, then you just need to return so that the PL/SQL block in the POST definition will complete. Your code can look something like this:
---Beginning of package
...
procedure update_client (some_clob clob) is
begin
--The client can not be updated so do no more processing.
if can_update_client = false then
--Some sort of error message could also be added here
return;
end if;
--Rest of processing for updating the client
end;
...
---End of package
I want to get data from webservices from oracle database.
But I'm looking for it unfortunately I can't anything .
Please give me some hints.I want to accesses it with username and password in pl/sql code.
How can I do it?
Thank you..
I solve it like following code.I think it will be useful .
declare
t_http_req utl_http.req;
t_http_resp utl_http.resp;
t_request_body varchar2(30000);
t_respond varchar2(30000);
t_start_pos integer := 1;
t_output varchar2(2000);
begin
/*Construct the information you want to send to the webservice.
Normally this would be in a xml structure. But for a REST-
webservice this is not mandatory. The webservice i needed to
call excepts plain test.*/
t_request_body := 'the data you want to send to the webservice';
/*Telling Oracle where the webservice can be found, what kind of request is made
and the version of the HTTP*/
t_http_req:= utl_http.begin_request( 'http://urlofwebservice'
, 'GET'
, 'HTTP/1.1');
/*In my case the webservice used authentication with a username an password
that was provided to me. You can skip this line if it's a public webservice.*/
utl_http.set_authentication(t_http_req,'username','password');
/*Describe in the request-header what kind of data is send*/
utl_http.set_header(t_http_req, 'Content-Type', 'text/xml charset=UTF-8');
/*Describe in the request-header the lengt of the data*/
utl_http.set_header(t_http_req, 'Content-Length', length(t_request_body));
/*Put the data in de body of the request*/
utl_http.write_text(t_http_req, t_request_body);
/*make the actual request to the webservice en catch the responce in a
variable*/
t_http_resp:= utl_http.get_response(t_http_req);
/*Read the body of the response, so you can find out if the information was
received ok by the webservice.
Go to the documentation of the webservice for what kind of responce you
should expect. In my case it was:
<responce>
<status>ok</status>
</responce>
*/
utl_http.read_text(t_http_resp, t_respond);
dbms_output.put_line(t_respond);
/*Some closing?1 Releasing some memory, i think....*/
utl_http.end_response(t_http_resp);
end;
You might want to look into the UTL_HTTP package
I need to refresh dbgrid constantly, in real time. Close and open dataset works fine, but blink the dbgrid. What can I do to avoid this?
I'd like a solution like Ajax, that update only the necessary.
Thanks
Have you tried to use Disable- & EnableControls?
DataSet.DisableControls;
try
DataSet.Close;
DataSet.Open;
finally
DataSet.EnableControls;
end;
Furthermore, it should be possible to just call DataSet.Refresh instead of closing and opening to get the same result.
I use this in my app
DataSet.MergeChangeLog;
DataSet.ApplyUpdates(-1);
DataSet.Refresh;
The above code is in an action named actRefreshData, in the ActionManager
When I need to use I just call it like
actRefreshData.Execute;
Hope this helps.
Hint: you can add a Timer and automate this
Look here:
type THackDataSet=class(TDataSet); // a nice "hack" so we can access
//protected members
THackDBGrid=class(TDBGrid);
procedure {tdmdb.}refreshgrid(grid : tdbgrid);
var row, recno : integer;
ds : tdataset;
b : tbookmark;
begin
Row := THackDBGrid(grid).Row;// or THackDataSet(ds).ActiveRecord
ds := grid.datasource.dataset;
RecNo := ds.RecNo;
b := ds.GetBookmark;
try
ds.close;
ds.Open;
finally
if (b<>nil) and ds.BookMarkValid(b) then
try
// ds.GotoBookMark(b);
ds.CheckBrowseMode;
THackDataSet(ds).DoBeforeScroll;
THackDataSet(ds).InternalGotoBookmark(b);
if THackDataSet(ds).ActiveRecord <> Row - 1 then
THackDataSet(ds).MoveBy(Row - THackDataSet(ds).ActiveRecord - 1);
ds.Resync([rmExact{, rmCenter}]);
THackDataSet(ds).DoAfterScroll;
finally
ds.FreeBookMark(b);
end
else if (recno<ds.RecordCount) and (recno<>ds.RecNo) then
begin
ds.First;
ds.MoveBy(Max(0, recno-1));
end;
end;
end;
In Delphi you can create a standalone Windows VCL Forms application. You can also create a Windows service application.
Is it possible to combine the two in a single application that can run as a standalone application and can also be installed as a Windows service?
Totally possible. The trick is to edit the .dpr to create main form when you want to run as an application and the service form when you want to run as a service. Like this:
if SvComFindCommand('config') then begin
//When run with the /config switch, display the configuration dialog.
Forms.Application.Initialize;
Forms.Application.CreateForm(TfrmConfig, frmConfig);
Forms.Application.Run;
end
else begin
SvCom_NTService.Application.Initialize;
SvCom_NTService.Application.CreateForm(TscmServiceSvc, scmServiceSvc);
SvCom_NTService.Application.Run;
end;
The code above uses SvCom to run the service but exactly the same effect could be achieved using the standard TService.
I wrote an article about that for The Delphi Magazine many years ago. You can read it here: Many Faces Of An Application.
It'll be hard to explain but I will try :)
I've done it in my project like that (Delphi 5):
program TestSvc;
uses SvcMgr,
SvcMain, //the unit for TTestService inherited from TService
...
;
var
IsDesktopMode : Boolean;
function IsServiceRunning : Boolean;
var
Svc: Integer;
SvcMgr: Integer;
ServSt : TServiceStatus;
begin
Result := False;
SvcMgr := OpenSCManager(nil, nil, SC_MANAGER_CONNECT);
if SvcMgr = 0 then Exit;
try
Svc := OpenService(SvcMgr, 'TestService', SERVICE_QUERY_STATUS);
if Svc = 0 then Exit;
try
if not QueryServiceStatus(Svc, ServSt) then Exit;
Result := (ServSt.dwCurrentState = SERVICE_RUNNING) or (ServSt.dwCurrentState = SERVICE_START_PENDING);
finally
CloseServiceHandle(Svc);
end;
finally
CloseServiceHandle(SvcMgr);
end;
end;
begin
if (Win32Platform <> VER_PLATFORM_WIN32_NT) or FindCmdLineSwitch('S', ['-', '/'], True) then
IsDesktopMode := True
else begin
IsDesktopMode := not FindCmdLineSwitch('INSTALL', ['-', '/'], True) and
not FindCmdLineSwitch('UNINSTALL', ['-', '/'], True) and
not IsServiceRunning;
end;
if IsDesktopMode then begin //desktop mode
Forms.Application.Initialize;
Forms.Application.Title := 'App. Title';
ShowTrayIcon(Forms.Application.Icon.Handle, NIM_ADD); // This function for create an icon to tray. You can create a popupmenu for the Icon.
while GetMessage(Msg, 0, 0, 0) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
ShowTrayIcon(Forms.Application.Icon.Handle, NIM_DELETE); // for delete the tray Icon
end else begin // Service mode
SvcMgr.Application.Initialize;
SvcMgr.Application.CreateForm(TTestService, TestService);
SvcMgr.Application.Run;
end;
end.
Another almost simpler option is available at http://cc.embarcadero.com/item/19703, you just need to include a unit and change your DPR to something like:
begin
if CiaStartService('SERVICE NAME') then begin
CiaService.CreateForm(TMain, Main);
CiaService.Run;
Exit;
end;
Application.Initialize;
Application.Title := 'SERVICE NAME';
Application.CreateForm(TMain, Main);
Application.Run;
end.
While this example is now quite dated, the technique is simple enough that it still works, even with Delphi XE2. With this in place, your application will continue to operate as a non-service until you use the "/install" parameter (on an elevated command prompt). After which it will operate as a service until you use the "/uninstall" parameter (also on an elevated command prompt).
There is a solution for this problem without writing a single line of code. It depends a little on your application, but generally it is achievable. Try this: http://iain.cx/src/nssm. Don't forget to start all services that you application depends on BEFORE you start your application as a service. Google around for info on how to do that.
It is possible but in that case you cannot use the normal TServiceApplication and TService. You should implement all the service specific code yourself.
We had a similat problem and made two frame applications: one for the sand alone exe and one for the service. Now we can create a single BPL/DLL that is embedded in both containers.
If you want to spend some money: you should look at SvCOM, I think they have a solution to the problem.