BDE - referential integrity and val

It is easy to set tables referential integrity and validation using BDE database desktop. But how does one do it in codes. This will be helpful if tables with their related files such as *.PX, *.VAL are removed/corrupted and needs to be regenerated by the application.
LVL 1
samchanAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

dotanCommented:
Check out this procedure...
you can find help about all Dbi functions in Delphi help

procedure AddRI(Master, Detail: TTable; RIName: string; ModOp, DelOp: RINTQual);
var
  MasterProps, DetailProps: CURProps;
  hDb: hDBIDb;
  TableDesc: CRTblDesc;
  Op: CROpType;
  RInt: RINTDesc;
  MIndex, DIndex: IDXDesc;
  MNo, DNo: Word;
begin
  if Master.Active = False then
    raise EDatabaseError.Create('Master table: ' + Master.TableName +
                                ' is not opened');
  if Detail.Active = False then
    raise EDatabaseError.Create('Detail table: ' + Detail.TableName +
                                ' is not opened');
  if Master.Exclusive = False then
    raise EDatabaseError.Create('Table: ' + Master.TableName +
                                ' must be opened exclusively');
  if Detail.Exclusive = False then
    raise EDatabaseError.Create('Table: ' + Detail.TableName +
                                ' must be opened exclusively');
  // Make sure the tables are opened with an index and get their descriptors...
  FillChar(DIndex, sizeof(DIndex), 0);  FillChar(MIndex, sizeof(MIndex), 0);
  Check(DbiGetIndexDesc(Detail.Handle, 0, DIndex));
  Check(DbiGetIndexDesc(Master.Handle, 0, MIndex));
  // Get the table properties to determine table type...
  Check(DbiGetCursorProps(Master.Handle, MasterProps));
  Check(DbiGetCursorProps(Detail.Handle, DetailProps));
  // If the table is not a Paradox table, raise an error...
  if (MasterProps.szTableType <> szPARADOX) and
     (MasterProps.szTableType <> szDBASE) then
     raise EDatabaseError.Create('Master table: ' + Master.TableName +
                                 ' must be a Paradox or dBASE table type');
  if (DetailProps.szTableType <> szPARADOX) and
     (DetailProps.szTableType <> szDBASE) then
     raise EDatabaseError.Create('Detail table: ' + Detail.TableName +
                                 ' must be a Paradox or dBASE table type');
  if MasterProps.szTableType <> DetailProps.szTableType then
     raise EDatabaseError.Create('Master and Detail tables must be of same type');
  // Blank out the structures...  FillChar(TableDesc, sizeof(TableDesc), 0);
  FillChar(RInt, sizeof(RInt), 0);
  //  Get the database handle from the table's cursor handle...
  Check(DbiGetObjFromObj(hDBIObj(Master.Handle), objDATABASE, hDBIObj(hDb)));
  // Put the table name in the table descriptor...
  StrPCopy(TableDesc.szTblName, Detail.TableName);
  // Put the table type in the table descriptor...
  TableDesc.szTblType := MasterProps.szTableType;
  // Set the operation type...
  Op := crADD;
  TableDesc.pecrRintOp := @Op;
  // Set the amount of new RI descriptors...
  TableDesc.iRintCount := 1;
  // Connect the table descriptor to the RI descriptor...
  TableDesc.printDesc := @RInt;
  // Setup the RI descriptor...
  // Put in the name of the RI...
  StrPCopy(RInt.szRintName, RIName);
  // Do the restructure on the dependent (detail) table...
  RInt.eType := rintDEPENDENT;
  // Add the master table name...
  StrPCopy(RInt.szTblName, Master.TableName);
  // Modify operations will be restricted (this can be changed to rintCASCADE)...
  RInt.eModOp := ModOp;
  // Delete operations will be restricted (NOTE: rintCASCADE will not work)...
  RInt.eDelOp := DelOp;
  // Only one field in link...
  RInt.iFldCount := 1;
  // If the tables are Paradox, then put the associated field numbers in the descriptor...
  if (MasterProps.szTableType = szPARADOX) then
  begin    //
    if RInt.eDelOp = rintCASCADE then
      raise EDatabaseError.Create('Cannot use cascading delete RI with Paradox tables');
    // Put the detail field index in the array...
    RInt.aiThisTabFld := DIndex.aiKeyFld;
    // Put the master field index in the array...
    RInt.aiOthTabFld := MIndex.aiKeyFld;  end;
  // If the tables are dBASE, then put the sequence number in the descriptor...
  if MasterProps.szTableType = szDBASE then
  begin
    Check(DbiGetIndexSeqNo(Master.Handle, MIndex.szName, MIndex.szTagName, 0, MNo));
    Check(DbiGetIndexSeqNo(Detail.Handle, DIndex.szName, DIndex.szTagName, 0, DNo));
    // Put the detail field index in the array...
    RInt.aiThisTabFld[0] := DNo;
    // Put the master field index in the array...
    RInt.aiOthTabFld[0] := MNo;
  end;

  try
    Master.Close;
    Detail.Close;
    Check(DbiDoRestructure(hDb, 1, @TableDesc, nil, nil, nil, FALSE));
  finally
    Master.Open;
    Detail.Open;
  end;
  end;

0
BoRiSCommented:
samchan

check out the TUtil stuff they allow you to reindex, pack , fix key violations etc...

I forget the url now but just do a search on DSP

http://SunSITE.icm.edu.pl/delphi/

Later
BoRiS
0
karouriCommented:
Listening..
0
Starting with Angular 5

Learn the essential features and functions of the popular JavaScript framework for building mobile, desktop and web applications.

mhervaisCommented:
samchan, when you use a proper database engine (I mean something that comes with log facilities ... sqlanywhere, sqlserver, oracle ... and even interbase) you do not need to care with such question. because the database system is almost always able to reconstruct from log. and if you add some backup planning you are almost safe in every circumstances.

I am using SQLAnywhere for several years now, and I never had to reconstruct anything.

When you are using something less robust, let's say BDE or ACCESS, you will sometimes will have to use utilities that will try to put the things back togeather.

I suggest you use another engine, specially if you plan to have many datas in your database.

I consider SQLANYwhere studio as a good buy (if you want to buy something) because you can write java procedures with it. Otherways take Interbase


If You want to stick on BDE, I suggest you get the tools Boris Told you about.

cheers,

Marc

BUT IN NO CASE IT IS YOUR JOB TO WRITE SUCH UTILITIES. Because it is your provider's jub not yours, and because you know nothing about the internal file structures of your database.  
0
kretzschmarCommented:
hi samchan,

from my paqs, modified dbiapi-samples

---------- Validation

For setting a validation-check in paradox

//Set a Valcheck, valcheck may not exist
procedure SetValCheck(Tbl: TTable; Field: TField; MinVal, MaxVal,
           DefVal: Pointer; MinSize,MaxSize,DefSize : Integer; Required: Bool);
var
  hDb: hDbiDb;
  TblDesc: CRTblDesc;
  VChk: pVChkDesc;
  Dir: String;
  NumVChks: Word;
  OpType: CROpType;

begin
  NumVChks := 0;
  SetLength(Dir, dbiMaxNameLen + 1);
  Check(DbiGetDirectory(Tbl.DBHandle, False, PChar(Dir)));
  SetLength(Dir, StrLen(PChar(Dir)));
  VChk := AllocMem(sizeof(VChkDesc));
  try
    FillChar(TblDesc, sizeof(CRTblDesc), #0);

    VChk.iFldNum := Field.Index + 1;
    Tbl.DisableControls;
    Tbl.Close;
    Check(DbiOpenDatabase(nil, nil, dbiReadWrite, dbiOpenExcl,
               nil, 0, nil, nil, hDb));
    Check(DbiSetDirectory(hDb, PChar(Dir)));
    with VChk^ do
    begin
      bRequired := Required;
      if MinVal <> nil then
      begin
        Inc(NumVChks);
        bHasMinVal := True;
        move(MinVal^, aMinVal, MinSize);
      end
      else
        bHasMinVal := False;
      if MaxVal <> nil then

      begin
        Inc(NumVChks);
        bHasMaxVal := True;
        move(MaxVal^, aMaxVal, MaxSize);
      end
      else
        bHasMaxVal := False;
      if DefVal <> nil then
      begin
        Inc(NumVChks);
        bHasDefVal := True;
        move(DefVal^, aDefVal, DefSize);
      end
      else
        bHasDefVal := False;

    end;
    TblDesc.iValChkCount := NumVChks;
    TblDesc.pVChkDesc := VChk;
    OpType := crADD;
    TblDesc.pecrValChkOp := @OpType;

    StrPCopy(TblDesc.szTblName, Tbl.TableName);
    StrCopy(TblDesc.szTblType, szParadox);
    Check(DbiDoRestructure(hDb, 1, @TblDesc, nil, nil, nil, False));
  finally
    Check(DbiCloseDatabase(hDb));
    FreeMem(VChk, sizeof(VChkDesc));
    Tbl.EnableControls;
    Tbl.Open;
  end;
end;

//Help Function to encode bde-date
Function GetBDEDate(Month, Day : Word; Year : SmallInt; var MyDate : DbiDate) : DbiDate;
begin
  Check(DbiDateEncode(Month, Day, Year, MyDate));
  Result := Mydate;
end;


//Table must be opened exclusive,
//a validate check may not exist
procedure TForm1.Button2Click(Sender: TObject);
var
  cmin,cmax,cdef : Double;
  lmin,lmax,ldef : Longint;
  amin,amax,adef : String;
  bdef : Boolean;
  dmin,dmax,ddef : dbiDate;
begin
  //Set validate for numeric Field
  cmin := -2.5;
  cmax := 2.5;
  cdef := 0.01;
  SetValCheck(Table6,Table6.FieldByName('Numeric'),@cmin,@cmax,@cdef,
              SizeOf(cMin),SizeOf(cMax),SizeOf(cdef),True);

  //Set Validate for Integer Field
  lmin := -100;
  lmax := 100;
  ldef := 1;
  SetValCheck(Table6,Table6.FieldByName('Integer'),@lmin,@lmax,@ldef,
              SizeOf(lMin),SizeOf(lMax),SizeOf(ldef),True);

  //Set Validate for Alphanumeric Field
  amin := 'A';
  amax := 'ABCDE';
  adef := 'AB';
  SetValCheck(Table6,Table6.FieldByName('Alpha'),@amin[1],@amax[1],@adef[1],
              length(amin),length(aMax),length(adef),True);

  //Set Validate for Boolean Field (Default only)
  bdef := False;
  SetValCheck(Table6,Table6.FieldByName('Boolean'),Nil,nil,@bdef,
              0,0,SizeOf(bdef),True);

  //Set Validate for DateField
  dMin := GetBDEDate(1,1,1999,dmin);
  dMax := GetBDEDate(12,31,1999,dmax);
  dDef := GetBDEDate(7,1,1999,ddef);
  SetValCheck(Table6,Table6.FieldByName('Date'),@dmin,@dmax,@ddef,
              SizeOf(dMin),SizeOf(dMax),SizeOf(ddef),True);
end;


------------------  Refential integrity

procedure AddRI(Master, Detail: TTable; MFields, DFields : Array of word ;RIName: string; ModOp, DelOp: RINTQual; RINTNum : Word);
var
  MasterProps, DetailProps: CURProps;
  hDb: hDBIDb;
  TableDesc: CRTblDesc;
  Op: CROpType;
  RInt: RINTDesc;
  MIndex, DIndex: IDXDesc;
  MNo, DNo: Word;
  I : Integer;
begin
  if Master.Active = False then
    raise EDatabaseError.Create('Master table: ' + Master.TableName +
                                ' is not opened');
  if Detail.Active = False then
    raise EDatabaseError.Create('Detail table: ' + Detail.TableName +
                                ' is not opened');

  if Master.Exclusive = False then
    raise EDatabaseError.Create('Table: ' + Master.TableName +
                                ' must be opened exclusively');
  if Detail.Exclusive = False then
    raise EDatabaseError.Create('Table: ' + Detail.TableName +
                                ' must be opened exclusively');

  // Make sure the tables are opened with an index and get their descriptors...
  FillChar(DIndex, sizeof(DIndex), 0);
  FillChar(MIndex, sizeof(MIndex), 0);
  Check(DbiGetIndexDesc(Detail.Handle, 0, DIndex));
  Check(DbiGetIndexDesc(Master.Handle, 0, MIndex));

  // Get the table properties to determine table type...
  Check(DbiGetCursorProps(Master.Handle, MasterProps));
  Check(DbiGetCursorProps(Detail.Handle, DetailProps));

  // If the table is not a Paradox table, raise an error...
  if (MasterProps.szTableType <> szPARADOX) and
     (MasterProps.szTableType <> szDBASE) then
     raise EDatabaseError.Create('Master table: ' + Master.TableName +
                                 ' must be a Paradox or dBASE table type');
  if (DetailProps.szTableType <> szPARADOX) and
     (DetailProps.szTableType <> szDBASE) then
     raise EDatabaseError.Create('Detail table: ' + Detail.TableName +
                                 ' must be a Paradox or dBASE table type');
  if MasterProps.szTableType <> DetailProps.szTableType then
     raise EDatabaseError.Create('Master and Detail tables must be of same type');

  // Blank out the structures...
  FillChar(TableDesc, sizeof(TableDesc), 0);
  FillChar(RInt, sizeof(RInt), 0);
  //  Get the database handle from the table's cursor handle...
  Check(DbiGetObjFromObj(hDBIObj(Master.Handle), objDATABASE, hDBIObj(hDb)));
  // Put the table name in the table descriptor...
  StrPCopy(TableDesc.szTblName, Detail.TableName);
  // Put the table type in the table descriptor...
  TableDesc.szTblType := MasterProps.szTableType;
  // Set the operation type...
  Op := crADD;
  TableDesc.pecrRintOp := @Op;
  // Set the amount of new RI descriptors...
  TableDesc.iRintCount := 1;
  // Connect the table descriptor to the RI descriptor...
  TableDesc.printDesc := @RInt;
  // Setup the RI descriptor...
  // Put in the name of the RI...
  StrPCopy(RInt.szRintName, RIName);
  // Do the restructure on the dependent (detail) table...
  RInt.eType := rintDEPENDENT;
  // Add the master table name...
  StrPCopy(RInt.szTblName, Master.TableName);
  // Modify operations will be restricted (this can be changed to rintCASCADE)...
  RInt.eModOp := ModOp;
  // Delete operations will be restricted (NOTE: rintCASCADE will not work)...
  RInt.eDelOp := DelOp;
  // Only one field in link...
  RInt.iFldCount := high(MFields) + 1;
  // If the tables are Paradox, then put the associated field numbers in the descriptor...
  if (MasterProps.szTableType = szPARADOX) then
  begin
    //
    if RInt.eDelOp = rintCASCADE then
      raise EDatabaseError.Create('Cannot use cascading delete RI with Paradox tables');
    // Put the detail field index in the array...
//    RInt.aiThisTabFld := DIndex.aiKeyFld;
    for i := low(DFields) to High(DFields) do
      RInt.aiThisTabFld[I] := DFields[I];
    // Put the master field index in the array...
//    RInt.aiOthTabFld := MIndex.aiKeyFld;
    for i := low(MFields) to High(MFields) do
      RInt.aiOthTabFld[I] := MFields[I];
    RInt.iRintNum := RINTNum;
  end;

  // If the tables are dBASE, then put the sequence number in the descriptor...
  if MasterProps.szTableType = szDBASE then
  begin
    Check(DbiGetIndexSeqNo(Master.Handle, MIndex.szName, MIndex.szTagName, 0, MNo));
    Check(DbiGetIndexSeqNo(Detail.Handle, DIndex.szName, DIndex.szTagName, 0, DNo));
    // Put the detail field index in the array...
    RInt.aiThisTabFld[0] := DNo;
    // Put the master field index in the array...
    RInt.aiOthTabFld[0] := MNo;
  end;

  try
    Master.Close;
    Detail.Close;
    Check(DbiDoRestructure(hDb, 1, @TableDesc, nil, nil, nil, FALSE));

  finally
    Master.Open;
    Detail.Open;
  end;
end;

Master, Detail: TTable -> must be opened exclusive
MFields, DFields : Array of word -> Field Number(s), not zeroBased,
count must be the same
RIName: string -> unique name of the Referential Integrity
ModOp, DelOp: RINTQual -> rintRESTRICT or rintCASCADE (delOp can only be
rintRESTRICT)
RINTNum : Word -> Index of the Referential Integrity (zeroBased) -> can
allways be zero

following restrictions i have checked out

the field(s) on the detailtable must be primary or secondary indexed

if there are more Referential Integrities as one then the other dependend tables must be closed before adding the new Referential Integrity.

----------------------------------------

hope this helps

meikl
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
samchanAuthor Commented:
Thank you. That's is really a lot of coding. Well, although, I have given you the points, I will be even more happy, if u were to give me some examples on how to use the referential integrity procedure.
0
kretzschmarCommented:
hi samchan,

first thanks for accepting,
sample follows, because i've the sample source at home. i will post it in 4-5 hours.

meikl ;-)
0
kretzschmarCommented:
hi again samchan,

here the sample, how to call.
table1 is a producttable and holds a VendorID, DepartmentID, InventoryID, and a OtherID, pointed to tables, which holds additional informations.


procedure TForm1.Button1Click(Sender: TObject);
begin
  AddRI(Table2, Table1, [1], [2], 'RIVendorProduct', RINTRestrict, RINTRestrict,0);
  Table2.Close;
  AddRI(Table3, Table1, [1], [3], 'RIDepartmentProduct', RINTRestrict, RINTRestrict,0);
  Table3.Close;
  AddRI(Table4, Table1, [1], [4], 'RIInventoryProduct', RINTRestrict, RINTRestrict,0);
  Table4.Close;
  AddRI(Table5, Table1, [1], [5], 'RIOtherProduct', RINTRestrict, RINTRestrict,0);
  Table2.Open;
  Table3.Open;
  Table4.Open;
end;

be free to ask for further details

meikl
0
kretzschmarCommented:
appendix,

you must add the unit bde in the uses-clause.
just for your infoemation

meikl
0
samchanAuthor Commented:
Whew! That's really great.
Thank you.
You're really very helpful. You will be on your way to the top of the experts scorer list by continuing giving such fantastic help. Once again, Thank you.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.