Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

Loosing Paradox data without CachedUpdates

Posted on 1998-06-01
23
594 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
Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 
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
 
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

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
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…
With Secure Portal Encryption, the recipient is sent a link to their email address directing them to the email laundry delivery page. From there, the recipient will be required to enter a user name and password to enter the page. Once the recipient …
The Email Laundry PDF encryption service allows companies to send confidential encrypted  emails to anybody. The PDF document can also contain attachments that are embedded in the encrypted PDF. The password is randomly generated by The Email Laundr…

840 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