Solved

Loosing Paradox data without CachedUpdates

Posted on 1998-06-01
23
590 Views
Last Modified: 2010-04-06
Hi all,

my situation can be reproduced with a little example:

An app with 1 Paradox table (CachedUpdate=False) I have a button to append/post 1 record each click.
If Windows or power goes off with the app opened, I loose all the records appended since the last application close.
It doesn't seem to happen with Interbase or others Databases I work with.

Am I doing smth wrong ? If not, is there any BDE API call to force a disk write ?
I cannot tell my customers that the technical solution is a no-break ;(

TIA,
Itamar
0
Comment
Question by:itamar
  • 6
  • 6
  • 5
  • +2
23 Comments
 
LVL 7

Expert Comment

by:BlackMan
ID: 1349656
The problem with Paradox is that it does some internal caching for the indexfiles. This does that the data is not written to disk immediatly. You can use the FlushBuffers call to write the buffers to disk.
0
 
LVL 5

Expert Comment

by:ronit051397
ID: 1349657
From Lloyd's Help File:
Q:  How do I flush the data buffer to the table?  (i.e. hard write to disk)

A:  This will allow you to commit changes to disk without incurring the performance hit of LocalShare.

Table1.UpdateCursorPos;
Check(dbiSaveChanges(Table1.Handle));
Table1.CursorPosChanged;

{This code came from Lloyd's help file!}
0
 
LVL 7

Expert Comment

by:BlackMan
ID: 1349658
I'm not quite sure if that will work with Paradox tables. I had some trouble with them myself, and the only solution I found was the FlushBuffers
0
 
LVL 5

Expert Comment

by:ronit051397
ID: 1349659
Perheps you are right, I didn't check the code.
0
 
LVL 4

Author Comment

by:itamar
ID: 1349660
Hi Ronit & BlackMan,

I'll try both solutions tomorrow.
For FlushBuffers I guess that the correct code would be smth like
dbiFlushBuffers(Table1.DBHandle); am I right ? Well, I'll try.

Thanks a lot !

c.u.
Itamar
0
 
LVL 3

Expert Comment

by:rickpet
ID: 1349661
Hahaha your both right...from dbtables.pas

procedure TBDEDataSet.FlushBuffers;
begin
  CheckBrowseMode;
  Check(DbiSaveChanges(Handle));
end;

0
 
LVL 4

Author Comment

by:itamar
ID: 1349662
Hi all,

thanks for the comments/answers ! If I could, I'd share the points among you three. All the comments were usefull.

I'll wait a bit more for some interesting comments and choose one to grade.

To BlackMan: Don't be upset. I'm just rejecting for now to make the question more visible to others experts. After 1 or 2 days I'll ask for an answer submission.

Thanks
Itamar
0
 
LVL 4

Author Comment

by:itamar
ID: 1349663

In Delphi 3 help file, FlushBuffers seems to be very obvious. The problem is that I'm using Delphi 2 (OK, I know I didn't say it....) So dbiSaveChanges seems to be the better solution.
0
 
LVL 5

Expert Comment

by:ronit051397
ID: 1349664
This is from the NewsGroups, there is also the option to set in the BDE:
LOCALSHARE=True

...
I am quite new to Delphi database development and I use Flushbuffers
in every AfterPost of my tables to write the cach. Is it good or not ?
What's the difference between LocalShare = true and the use of
DbiSaveChanges?

>If you are using local tables and you have Local share set to true
>Post will write out the records to disk
>>So if there are a power cut directly after, FAT's etc
>>have been updated so there will be no corrupted files??
>Paradox tables seem to hold up pretty well if the power is cut after a
>post but dbase tables seem to loose records.

...
>I was told to set LOCAL SHARE to TRUE to fix the following problem. This
>setting only fixes the problem when the program is halted unintentionally.
>But if the the computer halts (e.g. power failure), all changes made to
>database during the session are lost.
if you are loosing records after a power outage then you have some
kind of write caching in effect that BDE has no control over.

...
Well known bug. dbiSaveChanges works sometimes, using idle time works
sometimes, FlushBuffers works sometimes. Only thing that works all the time is
closing the TDatabase (you should use at least one for each app). That forces
the write to disk.

...
As far as I know, the BDE will always force writes to disk if you call
DbiSaveChanges or have Local Share set to True. I have never experienced
anything that makes me doubt that. Note that Windows or whatever operating
system you are using may still be caching writes. If you want to ensure
that changes are written to disk turn Windows write caching off.
0
 
LVL 1

Expert Comment

by:Marcius
ID: 1349665
Sorry to muscle in on the action, but here is a cure all solution for this type thing. Brace yourselves...

1. add dbiProcs to your uses statement

2. Create the following procedure

procedure DoIdle(Sender:TObject; var Done: Boolean);
begin
  dbiUseIdleTime;
  Done := true;
end;

3. Add to the OnCreate event of your MAIN form only

  Application.OnIdle := DoIdle;

This will use any idle time to carry out all database maintenance, including writing to disk. It is very reliable and has solved this problem for me. Much more favourable than adding the other solutions to each OnPost event, neh ?
0
 
LVL 4

Author Comment

by:itamar
ID: 1349666
Hi Marcius,

interesting approach, but could you elaborate more about "dbiUseIdleTime", what does it do ?

Another thing: I have a TBase class that comes (inherited)  from TTable, so I can't have a solution based on TApplication events. Any ideas ?

About LOCALSHARE: What is the syntax for stting it to true ?
Many thanks !

Itamar
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 4

Author Comment

by:itamar
ID: 1349667
Hi all,

Where can I find BDE API documentation ?

Bye,
Itamar
0
 
LVL 1

Expert Comment

by:Marcius
ID: 1349668
Why can't you use TApplication based event ? I don't understand why using a class which inherits TTable would stop that from working.

dbiUseIdleTime is a little bit of a mystery to me, I know that it works ! The dbiProcs unit is very poorly documented, so I cannot give you much more information. You can rest assured that it will save your cached Paradox data to disk and you will not suffer from disappearing records anymore.
0
 
LVL 3

Expert Comment

by:rickpet
ID: 1349669
Marcius & Itamar...
Taken from BDE help

DbiUseIdleTime
C syntax

DBIResult DBIFN DbiUseIdleTime (VOID);

Delphi syntax

function DbiUseIdleTime: DBIResult stdcall;

Description

This function is no longer supported. Use DbiSaveChanges instead.

So you should use DbiSaveChanges(Table1.handle) instead...
(from BDE Help)
Description

DbiSaveChanges forces all updated records associated with hCursor to disk.


Rick Peterson
0
 
LVL 1

Expert Comment

by:Marcius
ID: 1349670
Hey, what can I tell you, it still works !!
0
 
LVL 3

Expert Comment

by:rickpet
ID: 1349671
But it may not in future upgrades of the BDE...

what happens if they remove the feature in the next upgrade of the bde...

One of your customer upgrades...because he has to have the latest and greatest...whamo...unknowning to you or him...disaster is waiting just around the corner, because Borland stops supporting the api call.

Rick
0
 
LVL 1

Expert Comment

by:Marcius
ID: 1349672
That is true, but a little dramatic ;) I still think that dbiUseIdleTime is a good solution, and since everyone is falling over themselves to support backward compatibility, I wouldn't worry.
0
 
LVL 3

Expert Comment

by:rickpet
ID: 1349673
Your probably right as Delphi 3 is concerned...but Delphi 4 is a whole new ball game...(if you use it you may want to put a Great BIG comment warning that it may be gone in Delphi 4)

Rick
0
 
LVL 1

Expert Comment

by:Marcius
ID: 1349674
Okay, you are correct. But since I don't use Paradox any longer, this doesn't effect me too much. I am now using Oracle, which causes me an entirely new set of headaches :)
0
 
LVL 5

Expert Comment

by:ronit051397
ID: 1349675
The BDE Help file is located on:
C:\Program Files\Borland\Common Files\BDE\Bde32.hlp
Do you need a code or just explenation how to set localshare?
0
 
LVL 5

Expert Comment

by:ronit051397
ID: 1349676
to set LOCALSHARE manually enter the BDE/Configuration\System\INIT.
to set it by code see the Borland example below:

Set a particular value in the IDAPI.CFG configuration file (32-Bit Only; All Versions).

NOTE: You must use this procedure version if you are using BDE v4.50 and earlier

This exmaple uses the following inupt: SetConfigParameter2(LOCALSHARE, 'TRUE')
NOTE: Param (in this case LOCALSHARE) must be a string that contains the path to the
node and the node item separated by a semi-colon. At the bottom of this page are some
of the more popular paths and items that are declared as constants for use with all
these examples.

           procedure SetConfigParameter2(Param: string; Value: string);
           var
             hCfg: hDBICfg;
             Config: SYSConfig;
             Path, Option: string;
             ParamCount, I: word;
             pFields, pFld: pFLDDesc;
             pRecBuf, pRec: pBYTE;
             Found, SelfInitialized: boolean;
             rslt: DBIResult;

           begin
             {$Ifdef WIN32}
             hCfg := nil; pFld := nil; pRec := nil; Found := False; SelfInitialized := False;
             try
               if Pos(';', Param) = 0 then
                 raise EDatabaseError.Create('Invalid parameter passed to function.  There must ' +
                    'be a semi-colon delimited sting passed');
               Path := Copy(Param, 0, Pos(';', Param) - 1);
               Option := Copy(Param, Pos(';', Param) + 1, Length(Param) - Pos(';', Param));

               rslt := DbiGetSysConfig(Config);
               if rslt <> DBIERR_NONE then
               begin
                 if rslt = DBIERR_NOTINITIALIZED  then // Engine not initialized error...
                 begin
                   SelfInitialized := True;
                   DbiInit(nil);
                   Check(DbiGetSysConfig(Config));
                 end
                 else
                   Check(rslt);
               end;
               (* DbiOpenConfigFile is defined as such:
                   function DbiOpenConfigFile (            { Open/Create configuration }
                         pszDirPath    : PChar;            { Directory }
                         bCreate       : Bool;             { TRUE to create/overwrite }
                   var   hCfg          : hDBICfg           { Handle to config }
                                      ): DBIResult stdcall; *)
               Check(DbiOpenConfigFile(Config.szIniFile, FALSE, hCfg));

               (* DbiCfgGetRecord is defined as such:
                   function DbiCfgGetRecord (              { Get a record }
                         hCfg          : hDBICfg;          { Config Handle/NULL }
                         pszCfgPath    : PChar;            { Path }
                   var   iFields       : Word;             { Returned nbr of fields }
                         pfldDesc      : pFLDDesc;         { Field descriptors }
                         pRec          : Pointer           { Field values }
                                      ): DBIResult stdcall; *)
               { Call it without the field and record buffer to get the count... }
               Check(DbiCfgGetRecord(hCfg, PChar(Path), ParamCount, nil, nil));

               pFields := AllocMem(ParamCount * sizeof(FLDDesc));
               pFld := pFields;
               pRecBuf := AllocMem(10000);
               pRec := pRecBuf;

               { Get the node values... }
               Check(DbiCfgGetRecord(hCfg, PChar(Path), ParamCount, pFields, pRecBuf));

               for I := 0 to ParamCount - 1 do
               begin
                 if pFields^.szName = Option then
                 begin
                   StrPCopy(PChar(pRecBuf), Value);

                   (* DbiCfgModifyRecord is defines as such:
                        function DbiCfgModifyRecord (           { Modify a record }
                              hCfg          : hDBICfg;          { Config Handle/NULL }
                              pszCfgPath    : PChar;            { Path }
                              iFields       : Word;             { Nbr of fields }
                              pfldDesc      : pFLDDesc;         { Field descriptors }
                              pRec          : Pointer           { Data values }
                           ): DBIResult stdcall; *)
                   Check(DbiCfgModifyRecord(hCfg,  PChar(Path), ParamCount, pFld, pRec));

                   Found := True;
                 end;
                 Inc(pFields);
                 Inc(pRecBuf, 128);
               end;
               if Found = False then
                 raise EDatabaseError.Create(Param + ' entry was not found in configuration file');

             finally
               if pFld <> nil then
                 FreeMem(pFld);
               if pRec <> nil then
                 FreeMem(pRec);
               if hCfg <> nil then

               (* DbiCloseConfigFile is defined as such:
                      function DbiCloseConfigFile (           { Close the config file }
                      var   hCfg          : hDBICfg;          { Handle }
                            bSave         : Bool;             { To save the changes }
                            bDefault      : Bool;             { To make this file the default }
                            bSaveAs16     : Bool              { To save as a 16-bit config file }
                         ): DBIResult stdcall; *)
                 { Close and save the config file... }
               Check(DbiCloseConfigFile(hCfg, TRUE, TRUE, FALSE));
               if SelfInitialized = True then
                 DbiExit;
             end;
             {$Else}
                raise EDatabaseError.Create('Non supported function in 16 bit');
             {$EndIf}
           end;
0
 
LVL 4

Author Comment

by:itamar
ID: 1349677
Hi all,

Oh, This thread is getting BIG !

I'm done with it. All the solutions are great and have advantages. My particular approach leads me to use ronit's first comment. That's the one I put in my component.

Ronit please, post your comment as an answer so I can grade it.

To Marcius: the problem concerning the TApplication event is that I have to put the code in my component. The programmer wil use this component without writting anything else in his application, of course it wouldn't stop it from working, but it must be transparent.

Many thanks to everybody
0
 
LVL 5

Accepted Solution

by:
ronit051397 earned 200 total points
ID: 1349678
OK.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

705 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now