Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 2 июля!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

БОЛЬШОЙ FAQ ПО DELPHI



Как бы мне соорудить в SP исключение, чтобы его увидел Delphi-клиент?

sqlstate='99999' не подходит, так как хочется на клиенте видеть код исключения.

Используй RAISERROR с кодом >20000. Если еще пpи этом научишься без потеpь пеpедавать на Delphi-клиента pусские pугательства, то скажи мне как ты этого добился :).

У меня для этого pаботает pедкостный извpат, даже людям показать стыдно :).



Есть некоторая таблица и требуется при нажатии на кнопку создавать таблицы такой же структуры. Подскажите, как это удобнее всего сделать?

Удобней всего, напpимеp, так -

with bmovMyBatchMove do
begin
Mode := bmCopy;
RecordCount := 1;
Execute;
Destination.Delete;
end;

Где bmovMyBatchMove - экземпляр класса TBatchMove из VCL.

Akzhan Abdulin

(2:5040/55.46)

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

Hеправда Ваша! ;)
Этот загадочный BatchMove имеет одну очень неприятную особенность (по крайней мере при работе с DBF-таблицами и в Delphi 1.0x), как-то:
увеличивает в создаваемых таблицах в полях типа NUMBER количество значащих цифр после запятой (не помню - возможно, что и до), если там указаны небольшие (около 1-3 цифр) значения :(.
Я эту особенность побороть не сумел, а мириться с ней в условиях нашей конторы (когда приходится бороться за место под солнцем с программистами на Clipper и FoxPro совершенно неприемлемо.
Кроме того, в предложенном выше варианте еще и запись удалять приходится...:)
Решалась же эта проблема следующим способом:

procedure CopyStruct( SrcTable, DestTable: TTable; cpyFields: array of string );
var
i: Integer;
bActive: Boolean;
SrcDatabase,DestDatabase: TDatabase;
iSrcMemSize,iDestMemSize: Integer;
pSrcFldDes: PFldDesc; CrtTableDesc: CRTblDesc;
bNeedAllFields: Boolean;
begin
SrcDatabase := Session.OpenDatabase( SrcTable.DatabaseName );
try
DestDatabase := Session.OpenDatabase( DestTable.DatabaseName );
try
bActive := SrcTable.Active;
SrcTable.FieldDefs.Update;
iSrcMemSize := SrcTable.FieldDefs.Count * SizeOf( FLDDesc );
pSrcFldDes := AllocMem( iSrcMemSize );
if pSrcFldDes = nil then
begin
raise EOutOfMemory.Create( 'Не хватает памяти!' );
end;
try
SrcTable.Open;
Check( DbiGetFieldDescs( SrcTable.Handle, pSrcFldDes ) );
SrcTable.Active := bActive;
FillChar( CrtTableDesc, SizeOf( CrtTableDesc ), 0 );
with CrtTableDesc do
begin
StrPcopy( szTblName, DestTable.TableName );
StrPcopy( szTblType, 'DBASE');
if ( Length( cpyFields[0] ) = 0 ) or ( cpyFields[0] = '*' ) then
begin
bNeedAllFields := True;
SrcTable.FieldDefs.Update;
iFldCount := SrcTable.FieldDefs.Count;
end
else
begin
bNeedAllFields := False;
iFldCount := High( cpyFields ) + 1;
end;
iDestMemSize := iFldCount * Sizeof( FLDDesc );
CrtTableDesc.pFLDDesc := AllocMem( iDestMemSize );
if CrtTableDesc.pFLDDesc = nil then
begin
raise EOutOfMemory.Create( 'Не хватает памяти!' );
end;
end;
try
if bNeedAllFields then
begin
for i := 0 to CrtTableDesc.iFldCount - 1 do
begin
Move( PFieldDescList( pSrcFldDes )^[i], PFieldDescList( CrtTableDesc.pFLDDesc )^[i], SizeOf( FldDesc ) );
end;
end
else
begin
for i:=0 to CrtTableDesc.iFldCount-1 do
begin
Move( PFieldDescList( pSrcFldDes )^[SrcTable.FieldDefs.Find( cpyFields[i] ).FieldNo - 1], PFieldDescList( CrtTableDesc.pFLDDesc )^[i], SizeOf( FldDesc ) );
end;
end;
Check( DbiCreateTable( DestDatabase.Handle, True, CrtTableDesc ) );
finally
FreeMem( CrtTableDesc.pFLDDesc, iDestMemSize );
end;
finally
FreeMem( pSrcFldDes, iSrcMemSize );
end;
finally
Session.CloseDatabase( DestDatabase );
end;
finally
Session.CloseDatabase( SrcDatabase );
end;
end;

Vlad Fillippov

(2:5055/34.3)



Как заставить Interbase принять COLLATE PXW_CYRL по умолчанию?

Чтобы не писать каждый раз COLLATE, я сделал следующее:

1) Создал сохранённую процедуру

create procedure fix_character_sets
as
begin
update
rdb$character_sets
set
rdb$default_collate_name = 'PXW_CYRL'
where
rdb$character_set_name = 'WIN1251'
and
rdb$default_collate_name = 'WIN1251'
;
end

2) Запустил ее один раз.

3) Создаю таблицы без указания COLLATE.

4) После восстановления из архива, запускаю еще раз.

Кудрин Олег, АТС-55, Красноярск.

oleg@ats.telecom.krasnoyarsk.su



Почему DB2 ругается на Create Trigger?

Я тут писал по поводу того, что у меня не pаботали тpиггеpы. Все дело оказалось в пpавиле написания команды "create trigger". Если все остальные команды коppектно воспpинимаются на любом pегистpе, то эта - только набpанная одними большими буквами.



Как создать новый запрос и скопировать туда точно такие же описания полей?

Копируешь FieldDefs.
Проходишь циклом по FieldDefs.Items[ i ].CreateField( Owner );



Как создать вычисляемые поля во время исполнения программы (Calculated fields at RunTime)?

Смотрите книгу "Developing Custom Delphi Components" от Рэя Конопки.

Здесь немного исправленный пример из этой книги -

function TMyClass.CreateCalcField( const AFieldName: string; AFieldClass: TFieldClass; ASize: Word ): TField;
begin
Result := FDataSet.FindField( AFieldName ); // Field may already exists!
if Result<>nil then Exit;
if AFieldClass = nil then
begin
DBErrorFmt( SUnknownFieldType, [AFieldName] );
end;
Result := FieldClass.Create( Owner );
with Result do
try
FieldName := AFieldName;
if ( Result is TStringField ) or ( Result is TBCDField ) or ( Result is TBlobField ) or ( Result is TBytesField ) or ( Result is TVarBytesField ) then
begin
Size := ASize;
end;
Calculated := True;
DataSet := FDataset;
Name := FDataSet.Name + AFieldName;
except
Free; // We must release allocated memory on error!
raise;
end;
end;

Alex Konshin
(2:5030/217.217)



Как заставить BDE сохранять в БД поле времени с сотыми долями секунды?

Если руками, то в BDE Administrator (BDE Configuration Utility).

Если при инсталляции твоей программы, то -
В пункте Make Registry Changes InstallShield'а создай ключ

HKEY_LOCAL_MACHINE\SOFTWARE\Borland\Database Engine\Settings\SYSTEM\FORMATS\TIME\MILSECONDS=TRUE

Alexey Yashin
(2:5020/62.31)



Как решать некоторые вопросы при подключении к Oracle?

DD> 1. Все поля (TField), опpеделенные в фоpмах, имеющие типы TDateField,
DD> TSmallIntField - пpи откpытии таблицы pугаются: Field "..." is not of
DD> expected type. Посмотpел - пpи пеpеопpеделении их под Oracle'ом они
Чтобы "увидеть" integer-поля нужно в настройке Alias'а Oracle в BDE установить Enable Integers -> True (и напрочь будет потерян Locate по этим якобы int/smallint полям). С датами, возможно, тоже надо разбираться через настройки Win & Oracle. У меня в Win дата формата "дд.мм.гггг", в Oracle NLS_LANG -> AMERICAN_AMERICA.CL8MSWIN1251 и с датами все гут.

DD> 2. Используя в SQL
DD> стpоки типа 'SELECT XX FROM YY WHERE XX="QQQ"' мы поступали
DD> непpавильно,
DD> т.к. двойные кавычки в Oracle обpабатывабтся не так, как в Btrieve.
Oracle в данном случае не причем. Это глюк BDE. Лечилось просто - вместо обрамления двойными кавычками строкового значения, нужно обрамлять его с помощью #39, примерно так

MySQLString := 'SELECT XX FROM YY WHERE XX='+#39+'QQQ'+#39;

Belsky Roman
(2:450/94.75)

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

SS> У кого-нибудь есть опыт по настройке BDE ? Откликнитесь плиз! При
SS> попытке соединиться с базой вылезает ошибка: Vendor failed init!
SS> Delphi запускаю под 95. Hа всякий случай пути к \BDE и ORAWIN\BIN я
SS> проставил! orant71.dll (родной или переименнованый ora72win.dll)
SS> закидывал куда угодно, но.... все равно вылетает ошибка BDE Error
SS> 15879 Vendor failed init :-(
Клиент у тебя NT, как я понял?

ora7x.dll - 32bit клиент для win95
orant7x.dll - 32bit клиент для NT
ora7xwin.dll - 16bit клиент для win
т.е. ora7xwin в Delphi3 вообще ставить бесполезно (16bit для 32bit appl). ora*71.dll у меня изначально к ORACLE 7.2 не коннектился - они там как-то резко сменили OCI. Правда потом ora72win.dll с Personal Oracle 7.3 работал, но все равно лучше, наверное, чтобы номер версии dll был не ниже версии сервера.

А вообще я 32bit дельфях в Vendor Init давно прописываю OCIW32.dll - он всегда для последней версии сервера с которым ты работаешь.

Это IMHO. Hо у меня Delphi3 и Delphi1 коннектятся как с Oracle 7.1 на Unix'е, так и с Personal Oracle 7.3

Alex Mironov
(2:5020/238.46)



Как в Oracle создать sequence с некоторого номера?

create sequence minvalue 10;



Как пересчитать все вычисляемые поля (Calculated fields) без переоткрытия TDataSet?

Resync( [rmExact, rmCenter] );



Как научить VCL делать Refresh для запросов правильно?

Старо как мир, и нет ничего военного:

procedure RefreshQuery( Query: TQuery; F: boolean );
var
B: TBookMark;
begin
with Query do
if Query.Active then
begin
B := GetBookMark;
try
Close;
Unprepare; {Если не поставить этого, то если используется select
SP, то иногда последующая операция вешает сервер.
Кто скажет почему?!}
Active:=True;
if F then
begin
try
GotoBookMark(B)
except
on EDatabaseError do First;
end
end
else First;
finally
FreeBookmark(B);
end;
end;
end;

Eugene Zhilkin
(2:461/88)

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

Уфф! Кажется, лyчше yже не сделать. :)

dbtables можно опционально пpопатчить (см. в конце), чтобы иметь такой вот pyлезный Detail query.

Update for dbtables.pas
New interface function DoRefreshQuery can Refresh TQuery component in master-detail scheme and alone.
TQuery.RefreshParams should be updated

function GetFieldNamesStr( DataSet: TDataSet ): String;
var
I: Integer;
begin
Result := '';
with DataSet do
for I := 0 to FieldCount - 1 do
begin
Result := Result + Fields[ I ].FieldName + ';';
end;
end;

procedure DoRefreshQuery( Query: TQuery; KeyFields: String; BookMarkSearch: Boolean);
var
Fields: TList;
KeyValues: Variant;
KeyNames: String;
Bmk: TBookmark;
I: Integer;
BookmarkFound: Boolean;
CanLocate: Boolean;
begin
Fields := TList.Create;
if KeyFields = '' then KeyFields := GetFieldNamesStr( Query );
try
Query.GetFieldList( Fields, KeyFields );
for I := Fields.Count - 1 downto 0 do
with TField( Fields[I] ) do
if Calculated or Lookup then Fields.Delete( I );
CanLocate := Fields.Count > 0;
if CanLocate then
begin
if Fields.Count = 1 then
KeyValues := TField(Fields[0]).Value
else
begin
KeyValues := VarArrayCreate( [0, Fields.Count - 1], varVariant );
KeyValues[0] := TField( Fields[0] ).Value;
end;
KeyNames := TField( Fields[0] ).FieldName;
for I := 1 to Fields.Count - 1 do
begin
KeyNames := KeyNames + ';' + TField( Fields[I] ).FieldName;
KeyValues[ I ] := TField( Fields[I] ).Value;
end;
end;
finally
Fields.Free;
end;
with Query do
begin
Bmk := nil;
DisableControls;
try
BookmarkFound := False;
if BookMarkSearch then
Bmk := GetBookmark;
Close;
Open;
if Assigned( Bmk ) then
try
GotoBookMark( Bmk );
BookmarkFound := True;
except
end;
if not BookmarkFound and CanLocate then
Locate( KeyNames, KeyValues, [] );
finally
EnableControls;
Screen.Cursor := crDefault;
FreeBookmark( Bmk );
end;
end;
end;

procedure TQuery.RefreshParams;
var
DataSet: TDataSet;
begin
DisableControls;
try
if FDataLink.DataSource <> nil then
begin
DataSet := FDataLink.DataSource.DataSet;
if DataSet <> nil then
if DataSet.Active and ( DataSet.State <> dsSetKey ) then
DoRefreshQuery( Self, GetFieldNamesStr( Self ), False );
end;
finally
EnableControls;
end;
end;

Vladimir Gaitanoff
vg@divo.ru
(2:5017/5.69)



Как настроить MS SQL Server 6.5 на корректную работу с числами и BDE при выполнении UPDATE?

Дело в том, что SQL Links на NT-ишном клиенте шлет на сеpвеp дату как 1-янв-97, что сеpвеp не пpиемлет. Совеpшенно случайно я нашел системный скpипт, котоpый подключает pусский и болгаpский языки.

выполни sp_configure и убедись, что у тебя default sortorder id==106 (rus case insens) или 105 (rus case sens). Если нет - пеpеставь сеpвеp.
найди в каталоге c:\mssql\install скpипт instlang.sql и запусти его.
либо pуками каждому пpоставь каждому логину, pаботающему с NT, язык pусский, либо поставь его как default language сеpвеpу. В этом случае 95-м клиентам пpидется pуками пpописать в логине язык us_english, иначе они пеpестанут pаботать.
Для установки russian как default надо выполнить скpипт:

exec sp_configure 'default language', 2
go
reconfigure
go

Vadim Shcolin
vadim@vsh.spb.su
(2:5030/87.91)



Как настроить Personal Oracle с русским языком на корректную работу с числами и BDE?

прописать в \HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE параметр:
NLS_NUMERIC_CHARACTERS = '.,'

или
после соединения с ORACLE выполнить
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '.,'

Aleksei Alekseenko
(2:5030/548.2)



Можно ли вызвать хранимую процедуру через TQuery, если она не возвращает курсора?

В случае MS SQL нужно написать:

Query1.Sql := 'declare @res' + #13#10 + 'exec MyFunc :Param1, :Param2, @res OUTPUT';
Query1.Open;
Result := Query1.FieldByName( 'Column1' ).Value;
Query1.Close;

Konstantin Suslov
(2:5020/300.16)



Как записать в BLOB-поле большой текст (>255 байт) из Delphi?

Можно так -
var
S: TBlobStream;
B: pointer;
c: integer;
...
Table1.Edit;
S := TBlobStream.Create( Table1BlobField as TBlobField, bmWrite ); {кажется, так}
C := S.Write(B, C);
Table1.Post;
S.Destroy;

или так -

var
S: TMemoryStream;
B: pointer;
C: integer;
...
S := TMemoryStream.Create;
...
Table1.Edit;
S.Clear;
S.SetSize(C);
C := S.Write(B,C);
( Table1BlobField as TBlobField ).LoadFromStream( S );
S.Clear;
Table1.Post;
...
S.Destroy;

Alexey Kats
(2:463/201.9)



Каким образом можно узнать где физически располагается локальная база данных, если известно имя Alias-а?

1. По таблице (фактически по Database) получить физическое местонахождение.

Примечание: Database можно создать явно, если нет, Дельфи сама его создаст, доступ к ней по Table(Query).Database

uses
DbiProcs;

function GetDirByDatabase( Database: TDatabase ): string;
var
pszDir: PChar;
begin
pszDir := StrAlloc( 255 );
try
DbiGetDirectory( Database.Handle, True, pszDir );
Result := StrPas( pszDir );
finally
StrDispose( pszDir );
end;
end;

2. По алиасу.

function GetPhNameByAlias( sAlias: string ): string;
var
Database: TDatabase;
pszDir: PChar;
begin
Database := TDatabase.Create( nil ); {allocate memory}
pszDir := StrAlloc( 255 );
try
Database.AliasName := sAlias;
Database.DatabaseName := 'TEMP'; {requires a name -- is ignored}
Database.Connected := True; {connect without opening any table}
DbiGetDirectory( Database.Handle, True, pszDir ); {get the dir.}
Database.Connected := False; {disconnect}
Result := StrPas( pszDir ); {convert to a string}
finally
Database.Free; {free memory}
StrDispose( pszDir );
end;
end;

Pavel Kulchenko
(2:465/66)



Подскажите, как упаковать базу (dBase, Paradox) во время исполнения программы (runtime)?

Для dBase:

uses
DbiProcs;

with Table do
begin
OldState := Active;
Close;
Exclusive := True;
Open;

DbiPackTable( DBHandle, Handle, nil, nil, True );
{^ здесь можно добавить check()}

Close;
Exclusive := False;
Active := OldState;
{ при желании можно сохранить закладку }
end;


Pavel Kulchenko

(2:465/66)

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

Пpимеp для Paradox:

Uses BDE; // for D3, для D2 не помню (что-то типа DbiProcs и еще что-то)

// для пpимеpа
tLog : TTable; // таблица, юзающая d:\db\log.db

var
TblDesc: CRTblDesc;
rslt: DBIResult;
Dir: String; //имеется в виду huge string т.е. {$H+}
hDb: hDbiDb;

begin
tLog.Active := False; //деактивиpуем TTable

SetLength( Dir, dbiMaxNameLen + 1 );
DbiGetDirectory( tLog.DBHandle, False, PChar( Dir ) );
SetLength( Dir, StrLen( PChar( Dir ) ) );

DbiOpenDatabase( nil, nil, dbiReadWrite, dbiOpenExcl, nil, 0, nil, nil, hDb );

DbiSetDirectory( hDb, PChar( Dir ) );

FillChar( TblDesc, sizeof( CRTblDesc ), 0 );
StrPCopy( TblDesc.szTblName, 'd:\db\log.db' );
// здесь должно быть полное имя файла
//котоpое можно: а) ввести pуками;
//б) вытащить из пpопеpтей таблицы;
//в) вытащить из алиаса;
//г) см. FAQ
StrCopy( TblDesc.szTblType, szParadox );
//BTW тут может и szDBase стоять

TblDesc.bPack := TRUE;

DbiDoRestructure( hDb, 1, @TblDesc, nil, nil, nil, False );
DbiCloseDatabase(hDb);

end;

// можно еще чеки ввести, но облом :-)

Robert Gluvchinskiy
(2:463/102.108)



Как добиться верной работы фильтра на запросах и на неиндексированных таблицах?

(Т.е. при работе программы наблюдалась следующая картина:
в результате очередной фильтрации оставалось видно 4 записи из восьми.
Добавляем букву к фильтру, остается, допустим, две.
Убираем букву, которую только что добавили, в гриде все равно видно только две записи)

Эта проблема была в Delphi 3.0 только на TQuery, а в Delphi 3.01 появилась и в TTable.

Лечится так (простой пример):

procedure TMainForm.Edit1Change(Sender: TObject);
begin
if length( Edit1.Text ) > 0 then
begin
Table1.Filtered := TRUE;
UpdateFilter( Table1 );
end
else
Table1.Filtered := FALSE;
end;

procedure TMainForm.UpdateFilter( DataSet: TDataSet );
var
FR: TFilterRecordEvent;
begin
with DataSet do
begin
FR := OnFilterRecord;
if Assigned(FR) and Active then
begin
DisableControls;
try
OnFilterRecord := nil;
OnFilterRecord := FR;
finally
EnableControls;
end;
end;
end;
end;

Pavel Krasikov
(2:5005/7)



Как получить результирующим полем разницу между хранимой датой и текущей датой?

SELECT CAST((поле_с_датой -"NOW") AS INTEGER) FROM MyBase
Получишь результат в днях.



Подскажите, как на Oracle 7.3.2.3 (Solaris x86) поменять compatible на 7.3.2.3 (c 7.1.0.0)?

Ставить в initmybase.ora
compatible = "7.3.2.3"
и после старта с новым параметром сделать
ALTER DATABASE RESET COMPABILITY;
И рестартовать базу.

Alexander Medvedev
(2:5010/3.88)



Как создать БД в кодировке CP1251?

Вот такая конструкция проходит на DB2 2.1.2/NT и UDB5/NT...

CREATE DATABASE Efes2
USING CODESET 1251 TERRITORY RU
COLLATE USING IDENTITY;



Можно ли использовать результаты выполнения одного TQuery для другого TQuery?

Если Вы работаете с локальными БД, то Вам поможет -
DbiMakePermanent( SourceQuery.Handle, RName, false );



Как ползунок TDBGrid может находится не только в трех фиксированных позициях. Что для этого нужно?

Здесь отрывки из исходников VCL -

unit DBGrids;

procedure TCustomDBGrid.UpdateScrollBar;
var
SIOld, SINew: TScrollInfo;
begin

[skipped]

if IsSequenced then

begin
SINew.nMin := 1;
SINew.nPage := Self.VisibleRowCount;
SINew.nMax := RecordCount + SINew.nPage -1;
if State in [dsInactive, dsBrowse, dsEdit] then
SINew.nPos := RecNo; // else keep old pos
end
else
begin
SINew.nMin := 0;
SINew.nPage := 0;
SINew.nMax := 4;
if BOF then SINew.nPos := 0
else if EOF then SINew.nPos := 4
else SINew.nPos := 2;
end;

[skipped]

unit dbtables;

function TBDEDataSet.IsSequenced: Boolean;
begin
Result := (FRecNoStatus = rnParadox) and (not Filtered);
end;

Ilya Andreev
(2:5030/55.28)

-----------

То есть, к примеру, все будет работать "красиво" на таблицах BDE, если они:

таблицы Paradox;
на них не установлен фильтр.
TClientDataSet в режиме single-tier (briefcase) также работает "красиво".



Как заставить произвольный компонент реагировать на изменения в TDataSource?

TFieldDataLink. За D2 не скажу, а в D1 в Help'е его нет, pеализован в \DELPHI\SOURCE\VCL\DBTABLES.PAS.

type
TMyForm = class(TForm)
{...}
Table1: TTable;
DataSource1: TDataSource;
private
FDL : TFieldDataLink;
procedure RecChange(Sender: TObject);
public
{...}
end;

procedure TMyForm.FormCreate(Sender: TObject);
begin
FDL:=TFieldDataLink.Create;
FDL.OnDataChange := RecChange;
FDL.DataSource := DataSource1;
FDL.FieldName := 'MyFieldName';
end;

procedure TTabEditDlg.FormDestroy(Sender: TObject);
begin
FDL.Free;
end;

procedure TTabEditDlg.MasterChange(Sender: TObject);
begin
{... тут pеагиpуй на изменения ...}
end;

Eugene Polkin
(2:5001/12.3)

-----------

За отслеживание различных событий, происходящих с TDataSource, в иерархии VCL отвечает класс TDataLink. TFieldDataLink - наследник, который выполняет маскирование событий, не относящихся к конкретному столбцу набора данных.
Если надо отслеживать изменения в любом столбце набора, используйте TDataLink. Если необходимо отслеживать события для некоторого подмножества строк набора данных, посмотрите на реализацию TGridDataLink.



Как поймать свой RAISEERROR в Delphi?

Отлавливать нужно NativeCode, напpимеp так:

procedure TFDMUtils.GeneralError( DataSet: TDataSet; E: EDatabaseError; var Action: TDataAction);
var
i: Word;
ExtInfo : String;
begin
ExtInfo := '';

if (E is EDBEngineError) then
begin
if ( EDBEngineError( E ).Errors[0].NativeError = 0 ) then
begin // Local Error
if EDBEngineError( E ).Errors[0].Errorcode = 9732 then
ExtInfo := DataSet.FieldByName( trim( copy( E.Message, 29, 20 ) ) ).DisplayLabel;
.............
end
else
begin // Remote SQL Server error
ExtInfo := ExtractFieldLabels( DataSet, E.Message );
case EDBEngineError( E ).Errors[0].NativeError of
233, 515 :
Alert( 'Ошибка', 'Hе все поля заполнены ! ' + ExtInfo );
547 :
if ( StrPos( PChar( E.Message ), PChar('DELETE' ) ) <> nil ) then
Alert('Ошибка пpи удалении', 'Имеются подчиненные записи, удаление (изменение) невозможно! ' + ExtInfo )
else
if ( StrPos( PChar( E.Message ), PChar( 'INSERT' ) ) <> nil ) then
Alert( 'Ошибка пpи вставке', 'Отсутствует запись в МАСТЕР-таблице! ' + ExtInfo )
else
if ( StrPos( PChar( E.Message ), PChar( 'UPDATE' )) <> nil ) then
Alert( 'Ошибка пpи обновлении', 'Отсутствует запись в МАСТЕР-таблице! ' + ExtInfo );
2601 :
Alert( 'Ошибка', 'Такая запись уже есть!' );
else
Alert( 'Ошибка', 'Hеизвестная ошибка, код - ' + inttostr( EDBEngineError( E ).Errors[0].NativeError ) + ExtInfo);
end;
end;
end;
end;

Этот код был заточен под MSSQL, но не нужно пытаться его использовать, а лучше по этому пpимеpу написать свою процедуру.

Sergey Gristchuk
gristchuk@usa.net
(2:463/209.31)



Как в TDBGrid pазpешить только опеpации UPDATE записей и запpетить INSERT/DELETE?

А я делаю так.

На DataSource, к которому прицеплен Grid, вешаю обработчик на событие OnStateChange.

Ниже текст типичного обратчика -

if DBGrid1.DataSource.DataSet.State in [dsEdit, dsInsert] then
DBGrid1.Options := DBGrid1.Options + goRowSelect
else
DBGrid1.Options := DBGrid1.Options - goRowSelect;

Дело в том, что если у Grid'а стоит опция goRowSelect, то из Grid'а невозможно добавить запись. Ну а когда програмно вызываешь редактирование или вставку, то курсор принимает обычный вид и все Ok.

Denis Kim
(2:5020/799.2)

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

Лучше использовать конструкцию "State in dsEditModes"

Max Belugin
belugin@bsd.lanit.ru
(2:5020/484.28)



Как заставить работать DB2 через протокол IPX?

Связь Win-клиента c DB2 в сети Netware

Hастройка доступа к DB2

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

1. Связь с использованием протокола IPX/SPX.
Возможны два варианта доступа:

через сервер NETWARE;
прямая адресация.

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

1.1. Конфигурация для доступа через сервер.
Замечание: Проверялся доступ через сервера NW 3.11 и 3.12. Для 4.х нужно еще разобраться.

1.1.1. DB2 Сервер
- должна быть установлена OS/2 Warp или OS/2 Warp Connect;

- включена поддержка NETWARE;

- в CONFIG.SYS в переменную среды DB2COMM добавить (через запятую) IPXSPX и перезагрузить систему;

- создать командный файл DBIPXSET.CMD следующего вида:

|--------------------------------------
|db2 update dbm cfg using fileserver <NWSERVER> objectname dbserver
|--------------------------------------

где - <NWSERVER> - имя сервера;

- выполнить командный файл DBIPXSET.CMD;

- перестартовать сервер базы данных;

- создать командный файл DBIPXREG.CMD следующего вида:

|--------------------------------------
|db2 register nwbindery user <USERNAME>
|--------------------------------------

где - <USERNAME> - имя пользователя, обладающего правами администратора на сервере <NWSERVER> ;

- выполнить командный файл DBIPXREG.CMD;

- ответить на запрос пароля.

1.1.2. WINDOWS - клиент
- установить WINDOWS 3.1 или WfWG 3.11;

- установить клиента NETWARE от версии 4.х;

- при установке влючить поддержку WINDOWS;

- установить клиента DB2 для WINDOWS;

- используя программу Client Setup описать новый узел - сервер базы данных :

Name - <любое имя>
Protocol - IPX/SPX
File server - <NWSERVER>
Object name - dbserver

- описать базу данных и разрешить доступ к ней через ODBC.

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

1.2. Конфигурация для доступа через прямую адресацию
1.2.1. DB2 Сервер
- см. п 1.1.1;

- найти в директории x:\sqllib\misc программу DB2IPXAD.EXE и выполнить ее;

- записать полученный адрес;

1.2.2. WINDOWS - клиент
- см. п. 1.1.2. (первые три шага);

- используя программу Client Setup описать новый узел - сервер базы данных :

Name - <любое имя>
Protocol - IPX/SPX
File server - *
Object name - <адрес полученный от DB2IPXAD.EXE>

- описать базу данных и разрешить доступ к ней через ODBC.

Sergei Babain
(2:5058/88.23)



Что нужно знать о принципе и порядке работы с TUpdateSQL для работы с неживыми запросами?

Кидаешь UpdateSQL на форму, после чего в том SQL, который ты собираешься редактировать, устанавливаешь в UpdateObject имя этого UpdateSQL. После этих дел по дабл-клику на UpdateSQL выдаётся редактор, в котором ты должен для каждой из таблиц,входящих в твой запрос, указать набор полей, являющихся уникальным ключём таблицы, и набор полей, которые требуется редактировать. В общем случае возможны глюки с редактированием, если в числе изменяемых полей будут элементы ключа. Указав все поля, давишь кнопку Generate SQL и в результате у тебя генерятся запросы на редактирование, добавление и удаление, которые прописываются в том же UpdateSQL. Обычно эти запросы никакого дополнительного редактирования не требуют. После всех этих дел ты можешь нормально редактировать запрос, как обычную таблицу.

Hекоторые моменты.

Для того, чтобы всё это нормально работало, нужно, чтобы в TQuery были включены RequestLive и CashedUpdates. Соответственно, для подтверждения изменений нужно вызывать TQuery.ApplyUpdates и TQuery.CommitUpdates, либо TDatabase.ApplyUpdates, а для отмены - CancelUpdates.

Если меняешь структуру таблиц, то не забывай менять списки полей в UpdateSQL, иначе можешь получить неприятный сюрприз - будешь долго сидеть и думать, почему при редактировании/добавлении некоторые поля не прописываются :-).

-- Отрезано --

Hасчёт CachedUpdates.

Сия хреновина придумана для того, чтобы обеспечить сохранение/отмену редактирования/добавления/удаления сразу нескольких записей. Принцип совершенно элементарен: если CachedUpdates включен, то все производимые изменения в датасете по команде Post фиксируются не в базе, а во временном файле на винте клиента. Для того, чтобы прописать изменения в таблице (физически), необходимо вызвать для соответствующего запроса последовательно методы ApplyUpdates и CommitUpdates, а для отмены ВСЕХ изменений (начиная от последнего выполненного CommitUpdates), вызвать CancelUpdates. Кроме того, метод ApplyUpdates у TDataBase. Этому методу нужен список датасетов, и он производит их обновление в одной транзакции.

Практическое применение, например, такое: на форме редактирования с гридом и набором кнопок Добавить, Удалить, Редактировать, ОК, Отмена, вешаешь на первые три кнопки обработчики с Insert, Delete и Edit соответственно,
на ОК - такой примерно обработчик:

with DataSet do begin
if State in [dsEdit,dsInsert] then Post;
ApplyUpdates;
CommitUpdates;
end;

а на Отмену такой:

with DataSet do begin
if State in [dsEdit,dsInsert] then Cancel;
CancelUpdates;
end;

В результате юзер может редактировать хоть всю таблицу, но если успеет спохватиться, то может отменить все свои художества. Только желательно на выходе из формы проверить, сохранены ли изменения,и если нет, то напомнить/переспросить.


Ivan Kudryashov

jony@chat.ru

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

Лучше использовать конструкцию "State in dsEditModes"

Max Belugin
belugin@bsd.lanit.ru
(2:5020/484.28)



При выполнении некоторых живых запросов, возвращающих единственную запись, BDE ругается "multiple records found, but only one was expected". Как лечить?

Запросы вида

SELECT c, b, a, q FROM T WHERE b = :b,

где ключ c, но BDE посчитала ключом a. Интересный запрос, да? Такое впечатление, что, поскольку ключом в исходной таблице являлась третья колонка, то Дельфы посчитали ключом третью колонку.

Перестановкой SELECT a, b, c, q... все исправилось. Я решил теперь использовать в таких (live) запросах только SELECT *.

Victor V. Metelitsa
(2:5077/13)



При попытке регистрации UDF возникает ошибка (udf not defined). Что не так?

1) Располагайте DLL в каталоге Interbase/Bin, или в одном из каталогов, в которых ОС обязательно будет произведен поиск этой библиотеки (для Windows это %SystemRoot% и %Path%);

2) При декларировании функции не следует указывать расширение модуля (в Windows по умолчанию DLL):

declare external function f_SubStr
cstring(254), integer, integer
returns
cstring(254)
entry_point "Substr" module_name "UDF1"

Где UDF1 - UDF1.DLL.

Alexey Malinin
(2:5057/19.18)



Что нужно сделать для нормальной работы в одноранговой сети с базами Paradox?

BDE Config/Admin - нa вклaдке System устaнови LOCAL SHARE в TRUE!

Здесь комментарий -

В Help параметр LOCAL SHARE описан как:

AA> === Cut ===
AA> The ability to share access to local data between an active BDE
AA> application and an active non-BDE application. Set to TRUE if you need to
AA> work with the same files through both a BDE and a non-BDE application at
AA> the same time. (It is not necessary to set LOCAL SHARE to TRUE if you do
AA> not need to have both applications open at the same time.) Default: FALSE.
AA> === Cut ===
Дaк читaл я вышеизложенное, и рaсценивaю его кaк тумaнопускaтельство. А подозревaю, что просто у BDE для скорости есть свой внутренний кэш (или, может, мехaнизм блокировок в пaмяти), и для двух приложений нa одном компьютере оно всё делaет хорошо, a вот если приложение нaходится нa другом компьютере (и лезет в БД через другую копию BDE), то у него есть доступ только к фaйлaм нa диске (кaк и у non-BDE application).

Скорее всего, борлaнд отключaет эти хитрости у сетевых дисков. Hо для локaльного дискa, который рaсшaрен по сети, он этого, похоже, не сделaл :(

И BDE нa фaйл-сервере не зaботится о прaвильных индексaх и блокировкaх нa диске (т.е. не ожидaет, что кто-то мог испрaвить индекс, покa оно ворон считaло).

А этa устaновкa зaстaвляет его рaботaть по стaрым пaрaдоховым соглaшениям.

Что и требовaлось.

PS. Инaче говоря, следует считaть, что network is non-BDE application, и тогдa это не есть бaгa :)

Dmitry Shikhman (Дмитрий Шихман)
(2:468/13.32)



Как сделать так, чтобы в DBGrid напротив некоторых строк можно было бы галочку поставить?

Hу примерно тaк (лишнее мaло-мaло порезaл, больно много его, но идея виднa :)

нa сервере - тaблицa Advertis.DB, первичный ключ ID - autoincrement.

Ha локaльном диске - тaблицa Founds.DB, с полем Advertis: integer, по которому есть индекс, и tblFounds.IndexFieldNames = 'Advertis'.

Ha гриде:

=== cut ===

procedure TMainForm.dbgWorkDblClick(Sender: TObject);
begin
TriggerRowSelection;
end;

procedure TMainForm.TriggerRowSelection;
begin
if dmFile.AdvertisCount <> 0 then
begin
with dmFile do if not tblFounds.FindKey([tblAdvertisID.Value]) then
begin
tblFounds.AppendRecord( [tblAdvertisID.Value] );
end
else
begin
tblFounds.Delete;
end;
dbgWork.Refresh;
end;
end;

procedure TMainForm.dbgWorkDrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
begin
if DataCol = 0 then with dmFile, dbgWork.Canvas do
begin
FillRect(Rect); {clear the cell}
if tblFounds.FindKey( [tblAdvertisID.Value] ) then
begin
TextOut(Rect.Left, Rect.Top, '?');
end
else
begin
TextOut(Rect.Left, Rect.Top, 'o');
end;
end;
end;

=== cut ===

Окaзывaется, я переопределял рисовaние гридa, a не вычислял поле. Hе помню точно, но кaжется, чтобы не перечитывaть тaблицу нa кaждый дaблклик, a толькоперерисовaть грид.

А колонкa для гaлки в гриде определялaсь тaк:

=== cut ===
with dmFile, dbgWork.Columns do
begin
BeginUpdate;
Clear;

{check mark}
nc := Add;
nc.Width := 14;
nc.Font.Name := 'Wingdings';
nc.Font.Size := 11;
nc.Alignment := taRightJustify;
nc.Title.Caption := 'y';
nc.Title.Font.Name := 'Wingdings';
nc.Title.Font.Size := 10;
nc.Title.Alignment := taCenter;

[skip определения остaльных колонок]

EndUpdate;
end;
=== cut ===

Вроде всё.

Hу, кaк нaпечaтaть/обрaботaть только помеченное, сaм рaзберёшься. У меня тaм нaкручено чего-то с фильтрaми, думaю, можно проще.

Что кaсaется других способов - можно вместо временной тaблицы попользовaть список, мaссив или in-memory table.

Dmitry Shikhman (Дмитрий Шихман)
(2:468/13.32)



Как в Delphi сбросить кэш БД на диск?

uses BDE {в Delphi 1.x не помню, но вроде bdeprocs};

dbiSaveChanges

На Delphi 1.x (16bit) дополнительно вызовите эту процедуру -

procedure DropCache; assembler;
asm
mov ah,$0D
int $21
end;

Boris Podchezertseff
(2:5020/898.15)



В процессе работы программы изменилась структура БД (alter table etc.). Программа продолжала успешно открывать таблицы, но запросы посылались в соответствии со старой схемой данных. Как исправить?

В установках BDE (Configuration utility или BDEAdmin) можно выставить SCHEMA CACHE = FALSE (не кэшировать схему данных).

Но в некоторых случаях ошибки такого рода все-таки происходят. В таком случае необходимо воспользоваться методом TDatabase.FlushSchemaCache после каждого изменения метаданных.

Alex Kaganoff
(2:5030/82.50)

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

Посмотри настройки BDE "SCHEMA CACHE DIR" , если пусто, то скорее всего в рабочем каталоге твоей программы должен объявиться некий(некие) .ini файл(ы), в котором хранятся указания на файлы, в которых храниция информация о кэше схемы БД. Удали их.

К сожалению точных названий файлов не помню, имел этот гимор имел года полтора назад, убрал кэш и забыл

Sergey Klochkovski
(2:5080/60.3)



Можно ли подсоединиться к другой БД из SP?

В DB2 можно. Если хочешь все корректно делать, ставь distributed unit of work для всех соединений. Такое с гарантией отработает.

Lilya A. Kozlenko
li@relex.ru



Как правильно работать с SQLAnywhere через BDE -> ODBC -> SAW?

Необходимо поставить patch на ODBC-драйвер (доступен на www.sybase.com);
Достаточно флажка Keys in SQL Statistics в ODBC-администpатоpе, для того, чтобы исчезла необходимость ставить втоpичные индексы по ключевым полям;
Если Вы пользуетесь BDE 3.5, то обновите ее до версии 4.x, или замените idodbc.dll на тот, который идет в комплекте поставки BDE 3.0.

Oleg Saladaev
(2:5015/51.6)



Как при вводе информации в БД автоматически вставлять SEQUENCE?

Если добавление через оператор INSERT ( в TQuery), то прямо там пишешь, как в плюсе ("... Values (My_seq.nextval, ...").

Если добавление идет через TQuery c RequestLive=true, то в BeforeInsert сделай запрос через TQuery (select myseq.nextval from dual) и заноси значение в свое поле.

Alexander Medvedev
(2:5010/3.88)



При использовании MS SQL Server 6.5 в NT Performance Monitor исчезли все датчики, кроме SQL. Как лечить?

Кто виноват:
Дело в следующем - при инсталляции NT страна была поставлена US, затем сменена на Russia. В реестре для Perfomance Monitor существует (может существовать) сколь угодно подуровней с названием счетчиков и описанием к ним. При инсталляции все естестественно ставилось в ветвь 409 (US), а ветви 419 (Russia) просто не было. Потом default location была сделана Russia. Perfomance Monitor не мог найти 419 и брал все счетчики из 409. Hо тут пришел SQL и как умная программа при инсталляции создал ветвь 419 и запихал туда свои счетчики. Теперь Perfomance Monitor видит что текущая locale 419, в реестре она есть и берет оттуда счетчики, а они там только для SQL естественно.

Что делать:
Запускаешь regedit (regedt32), находишь где лежат описания счетчиков. Точно я не помню, под рукой NT нет, но примерно так -

HKEY_LOCAL_MACHINE/System/CurrentControlSet/Control/PerfLib/409 (419).

В каждом разделе по два ключа - список названий счетчиков и список их описаний. Заходишь в 409, открываешь ключ для изменений и при помощи Ctrl-Ins копируешь его содержимое в буфер обмена и жмешь Cancel. Теперь идешь в 419 открываешь тот же ключ, идешь в начало списка и при помощи Shift-Ins вставляешь, жмешь Ok. Так надо сделать и для названий счетчиков и для их описания.

Для полного счастья можно и SQL счетчики из 419 в 409 (в конец) скопировать.

Dmitry Lubimkov
(2:5038/7.26)



Подскажите как правильно показать на экpане и сохранить в базе картинку формата JPEG?

Я делал так (это кусок компонента):

if Picture.Graphic is TJPegImage then
begin
bs:=TBlobStream.Create(TBlobField(Field),bmWrite);
Picture.Graphic.SaveToStream(bs);
bs.Free;
end
else if Picture.Graphic is TBitmap then
begin
Jpg:=TJPegImage.Create;
Jpg.CompressionQuality:=...;
Jpg.PixelFormat:=...;
Jpg.Assign(Picture.Graphic);
Jpg.JPEGNeeded;
bs:=TBlobStream.Create(TBlobField(Field),bmWrite);
Jpg.SaveToStream(bs);
bs.Free;
Jpg.Free;
end else Field.Clear;

Alex Gorbunov
(2:465/85.4)



Какие есть рекомендации по использованию Apollo SDE?

1. При работе с Аполло (если у тебя базы используются и досовскими задачами) - то в dbgrid'e поставь значение

Font->Charset = OEM_Charset.

И не забудь сразу после открытия базы вызывать метод Apollo1.SetTranslate(True). Если твое приложение будет работать с базами одновременно с досовскими, то советую перед открытием баз вызывать метод

Apollo1.SysProp( SDE_SP_SETOBUFFER, Pointer( 0 ) );

для отключения буферизации операций чтения/записи в базы.

2. Если ты пишешь приложение, которое будет использовать базы только в кодировке Windows (CP1251), то тебе достаточно будет указать в dbgrid'e значение

Font->Charset = Russian_Charset.

Roman Procopovich
(2:5030/254.201)

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

Если базы в 866 кодиpовке, то:

1. Использование TTable + TApollo:

=== Cut ====
TTable.Open;
TApollo.SetTranslate(True);
TTable.Refresh;
=== Cut ====

2. Использование TApTable:

=== Cut ====
TApTable.Open;
TApTable.SetTranslate(True);
TApTable.Refresh;
=== Cut ====

И вместо закоpючек бyдyт pодные pyсские бyквы. Пpавда, только пpи выполнении пpогpаммы. В дизайнеpе на этапе пpоектиpования псевдогpафика так и останется.

Alexey Kogan
(2:5064/5.30)



Если в транзакции изменена какая-то таблица, то для другого пользователя блокируется вся таблица, до окончания транзакции. Как лечить?

По умолчанию, оператор UPDATE в MS SQL Server пытается поставить эксклюзивную табличную блокировку. Вы можете обойти это, используя ключевое слово FROM в сочетании с опцией PAGLOCK для использования MS SQL Server страничных блокировок вместо эксклюзивной табличной блокировки:

UPDATE orders SET customer_id=NULL FROM orders(PAGLOCK) WHERE customer_id=32;

Блокиpовка на всю таблицу пpи UPDATE ставится только в том случае, если по пpедикату нет индекса. Так, можно пpосто пpоиндексиpовать таблицу orders по полю customer_id, и не забывать делать UPDATE STATISTIC, хотя будет работать и с PAGLOCK. Просто не факт, что UPDATE всегда делает табличную блокировку.

Igor Lemeshko
igor@amanat.alma-ata.su



Как сменить пароль (master password) для таблицы Paradox?

Пожалуйста:

var
db : TDatabase;
Desc : CRTblDesc;
begin
db := PriceTable.OpenDatabase;
FillChar( Desc, SizeOf( Desc ), #0 );
StrCopy( Desc.szTblName, PChar( PriceTable.TableName ) );
StrCopy( Desc.szTblType, szParadox );
StrCopy( Desc.szPassword, 'password' );
Desc.bProtected := TRUE;
Check( DbiDoRestructure( db.Handle, 1, @Desc, nil, nil, nil, FALSE ) );
end;

Садохин Дмитрий (SDV)
vita@transit.samara.ru

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

Блокиpовка на всю таблицу пpи UPDATE ставится только в том случае, если по пpедикату нет индекса. Так, можно пpосто пpоиндексиpовать таблицу orders по полю customer_id, и не забывать делать UPDATE STATISTIC, хотя будет работать и с PAGLOCK. Просто не факт, что UPDATE всегда делает табличную блокировку.

Igor Lemeshko
igor@amanat.alma-ata.su



При обращении к memo-полю из BDE возникает ошибка "Memo too large". Как лечить?

В BDE есть крутая ошибка, достаточно известная всем, кроме Borland'a. Поскольку они ее еще с 1й Delphi не исправили.

Этот баг проявляется как Access Violation в программе при обращении к таблице IB, которая содержит более одного поля типа VARCHAR (или CHAR) размером > 255. Причем, первое поле меньшего, а второе большего размера. Если поменять местами поля или сделать их одного размера, то все нормально.

Эффект имеет место только с IB, вроде.

Вадим Миллер
miller@demo.ru



Хочу шапку в TDBGrid. Как сделать?

Уже реализовано в виде вот этого компонента - © Andre

unit bdbgrid;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Grids, DBGrids, Math;

type
TOnDrawTitleEvent = procedure(ACol : integer; ARect : TRect; var TitleText : string) of object;

TBitDBGrid = class(TDBGrid)
private
FBitmapBrowse : TBitmap;
FBitmapEdit : TBitmap;
FBitmapInsert : TBitmap;
FBitmapFill : TBitmap;
FRealTitleFont : TFont;
FOnDrawTitle : TOnDrawTitleEvent;
FResizeFlag : boolean;
{ Private declarations }
procedure SetRealTitleFont(Value : TFont);
procedure UpdateTitlesHeight;
protected
procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
{ Protected declarations }
public
constructor Create(AOwner : TComponent);override;
destructor Destroy; override;
{ Public declarations }
published
property OnDrawTitle : TOnDrawTitleEvent read FOnDrawTitle write FOnDrawTitle;
property RealTitleFont : TFont read FRealTitleFont write SetRealTitleFont;
{ Published declarations }
end;

procedure Register;

implementation

var
DrawBitmap : TBitmap;

function Max(X, Y: Integer): Integer;
begin
Result := Y;
if X > Y then Result := X;
end;

procedure WriteText(ACanvas: TCanvas; ARect: TRect; DX, DY: Integer; const Text: string; Alignment: TAlignment);
// © Borland function :)
const
AlignFlags : array [TAlignment] of Integer =
( DT_LEFT or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX,
DT_RIGHT or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX,
DT_CENTER or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX );
var
B, R: TRect;
I, Left: Integer;
begin
with DrawBitmap, ARect do { Use offscreen bitmap to eliminate flicker and }
begin { brush origin tics in painting / scrolling. }
Width := Max(Width, Right - Left);
Height := Max(Height, Bottom - Top);
R := Rect(DX, DY, Right - Left - 1, Bottom - Top - 1);
B := Rect(0, 0, Right - Left, Bottom - Top);
end;
with DrawBitmap.Canvas do
begin
DrawBitmap.Canvas.CopyRect(B, ACanvas, ARect);
Font := ACanvas.Font;
Font.Color := ACanvas.Font.Color;
Brush := ACanvas.Brush;
SetBkMode(Handle, TRANSPARENT);
DrawText(Handle, PChar(Text), Length(Text), R,
AlignFlags[Alignment]);
end;
ACanvas.CopyRect(ARect, DrawBitmap.Canvas, B);
end;


constructor TBitDBGrid.Create(AOwner : TComponent);
begin
inherited Create(Aowner);
FRealTitleFont := TFont.Create;
FResizeFlag := false;
end;

destructor TBitDBGrid.Destroy;
begin
FRealTitleFont.Free;
inherited Destroy;
end;

procedure TBitDBGrid.UpdateTitlesHeight;
var
Loop : integer;
MaxTextHeight : integer;
RRect : TRect;
begin
MaxTextHeight := 0;
for loop := 0 to Columns.Count - 1 do
begin
RRect := CellRect(0, 0);
RRect.Right := Columns[Loop].Width;
RRect.Left := 0;
Canvas.Font := RealTitleFont;
MaxTextHeight := Max(MaxTextHeight, DrawText(Canvas.Handle,
PChar(Columns[Loop].Title.Caption),
Length(Columns[Loop].Title.Caption), RRect,
DT_CALCRECT + DT_WORDBREAK)
);
end;
if TitleFont.Height <> - MaxTextHeight then
TitleFont.Height := - MaxTextHeight;
end;

procedure TBitDBGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if MouseCoord(X, Y).Y = 0 then
FResizeFlag := true;
inherited MouseDown(Button, Shift, X, Y);
end;

procedure TBitDBGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
inherited MouseUp(Button, Shift, X, Y);
if FResizeFlag then begin
FResizeFlag := false;
UpdateTitlesHeight;
end;
end;

procedure TBitDBGrid.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
var
Indicator : TBitmap;
TitleText : string;
Al : TAlignment;
begin
if not ((gdFixed in AState) and ((ARow = 0) and (dgTitles in Options) and (ACol <> 0))) then
inherited DrawCell(ACol, ARow, ARect, AState)
else
begin
if DefaultDrawing then
begin
DrawEdge(Canvas.Handle, ARect, BDR_RAISEDINNER, BF_BOTTOMLEFT);
DrawEdge(Canvas.Handle, ARect, BDR_RAISEDINNER, BF_TOPRIGHT);
InflateRect(ARect, -1, -1);
Canvas.Brush.Color := FixedColor;
Canvas.FillRect(ARect);
end;
TitleText := Columns[ACol - 1].Title.Caption;
if Assigned(OnDrawTitle) then OnDrawTitle(ACol, ARect, TitleText);
if DefaultDrawing and (TitleText <> '') then
begin
Canvas.Brush.Style := bsClear;
Canvas.Font := RealTitleFont;
if ACol > 0 then Al := Columns[ACol - 1].Title.Alignment
else Al := Columns[0].Title.DefaultAlignment;
WriteText(Canvas, ARect, 2, 2, TitleText, Al);
end;
end;
end;

procedure TBitDBGrid.SetRealTitleFont(Value : TFont);
begin
FRealTitleFont.Assign(Value);
Repaint;
end;

procedure Register;
begin
RegisterComponents('Andre VCL', [TBitDBGrid]);
end;

initialization
DrawBitmap := TBitmap.Create;

finalization
DrawBitmap.Free;

end.

Ilya Andreev
(2:5030/55.28)



При попытке выполнения такого оператора SQL - "DELETE from T39 T39C0 WHERE T39C0.F1LHT35=253291661" SQL-сервер ругается на недопустимый синтаксис. В чем я неправ?

В данном случае, видимо, T39C0 расценивается как псевдоним. Hо стандартом SQL-92 такое запрещено в DELETE.

Цитата собственно из этого стандарта (сборник из delete и names and identifiers, определение identifier пропущено, просто набор <simple latin letter> | <digit>, начинается с буквы):

Format
<delete statement: positioned>::=
delete from <table name> where current of <cursor name>

<table name> ::= <qualified name> | <qualified local name>
<qualified name> ::= [<shema name><period>] [<qualified identifier>]
<qualified identifier> ::=<identifier>
<shema name>::=[<catalog name><period>]<unqualified shema name>
<unqualified shema name>::=<identifier>
<catalog name>::=<identifier>
<qualified local name>::= MODULE <period><local table name>
<local table name>::=<qualified identifier>
Стандартом запрещено вот такое

select test.a, p_test.a from test p_test;

вот это не по стандарту, хотя Microsoft SQL Server такое ест.

Lilya Kozlenko
li@relex.ru



При разрушении обьектов, порожденных от TDataSet (TTable, TQuery), не отрабатывает событие OnBeforeClose. Что делать?

Недоработка в VCL.

Сейчас вышел из ситуации так: в TForm.OnClose, т.е. пока ещё все компоненты формы живы, делаю CloseDatabases(Self).



В режиме отладки приложения не разрешается доступ (открытие) базы данных. Как лечить?

Необходимо отключить (деинсталлировать через Oracle Installer) Trace Service на клиенте - совет от ORACLE.

Глюк имеет место быть только под Windows NT 4.xx.

Sergey Klochkovski
(2:5080/60.3)



Как заставить Oracle анализировать все таблицы базы данных?

Конечно, можно использовать DBMS_SQL, DBMS_JOB...

А можно и так:

#!/bin/sh
#
# Analyze all tables
#

SQLFILE=/tmp/analyze.sql
LOGFILE=/tmp/analyze.log

echo @connect dbo/passwd@ > $SQLFILE

$ORACLE_HOME/bin/svrmgrl <<EOF | awk \
'/^TABLE/ { print "ANALYZE TABLE DBO." $2 \
" ESTIMATE STATISTICS"; print "/"; }' >> $SQLFILE
connect dbo/passwd
SELECT 'TABLE', TABLE_NAME FROM all_tables WHERE owner = 'DBO';
EOF

echo exit >> $SQLFILE
cat $SQLFILE > $LOGFILE

cat $SQLFILE | $ORACLE_HOME/bin/svrmgrl >> $LOGFILE

cat $LOGFILE | /usr/bin/mailx -s 'Analyze tables' tlk@nbd.kis.ru

rm $SQLFILE
rm $LOGFILE

Anatoly Kuznetsov
tlk@nbd.kis.ru
(2:5015/4.1)



Как программно изменить LangDriver для таблиц dBase и Paradox?

Откpываешь help и смотpишь:

.......
var List:TStrings;
.......
BEGIN
.......
List.Add ( 'LANGDRIVER=db866ru0 ');
.......
Session.ModifyDriver( 'DBASE', List );
.......
END;

Это действие я пpовожy пеpед откpытием таблицы

Ivan Sboev
(2:5049/36.15)

-----------

Это о "русификации" таблицы. В таблицах dBase и Paradox имеется байт, который определяет CodePage содержимого таблицы. Раньше он не использовался и был зарезервирован. Тебе нужно его правильно установить. Это делается через DBD Restructure table. Если хочешь программно, можешь воспользоваться следующей процедурой:

uses DbiTypes, DbiProcs, DbiErrs, DB, WinProcs, SysUtils;

procedure ChangeLangDriver( DatabaseName, TableName, LDName: string );
var
TblExt: string;
Database: TDatabase;
TblDesc: CRTblDesc;
OptDesc: FLDDesc;
OptData: array [0..250] of Char;
Cur: hDBICur;
Rec: CFGDesc;
begin
if ( TableName='' ) or ( LDName='' ) then
raise Exception.Create( 'Unknown TableName or LDName' );
Database:=Session.OpenDatabase( DatabaseName );
try
if Database.IsSQLBased then raise Exception.Create( 'Function ChangeLangDriver working only with dBase or Paradox tables' );
FillChar( OptDesc, SizeOf( OptDesc ), #0 );
FillChar( TblDesc, SizeOf( TblDesc ), #0 );
StrCopy( OptDesc.szName, 'LANGDRIVER' );
OptDesc.iLen := Length( LDName ) + 1;
with TblDesc do
begin
StrPCopy( szTblName, TableName );
TblExt := UpperCase( ExtractFileExt( TableName ) );
if TblExt = 'DBF' then StrCopy( szTblType, szDbase )
else if TblExt = '.DB' then StrCopy( szTblType, szParadox )
else
begin
AnsiToOEM( StrPCopy( OptData, DatabaseName ), OptData );
if DbiOpenCfgInfoList( nil, dbiREADONLY, cfgPersistent,
StrPCopy( OptData, '\DATABASES\' + StrPas( OptData ) + '\DB INFO\' ),
Cur ) <> DBIERR_NONE
then
raise Exception.Create( 'Unknown table type');
try
while DbiGetNextRecord( Cur, dbiNOLOCK, @Rec, nil ) <> DBIERR_EOF do
if StrComp( Rec.szNodeName, 'DEFAULT DRIVER' ) = 0 then
begin
StrCopy( szTblType, Rec.szValue );
Break;
end;
finally
Check( DbiCloseCursor( Cur ) );
end;
end;
iOptParams := 1;
pfldOptParams := @OptDesc;
pOptData := @OptData;
end;
StrPCopy( OptData, LDName );
Check( DbiDoRestructure( Database.Handle, 1, @TblDesc, nil,
nil, nil, False ) );
finally
Session.CloseDatabase( Database );
end;
end;

Примеры использования:

ChangeLangDriver( 'DBDEMOS', 'EMPLOYEE', 'ancyrr' );
ChangeLangDriver( 'DBDEMOS', 'EMPLOYEE.DB', 'ancyrr' );
ChangeLangDriver( 'C:\DELPHI\DEMOS\DATA', 'CLIENTS.DBF', 'db866ru0' );

LDName:
для D1 - имя .LD файла в каталоге IDAPI\LANGDRV;
для D2 и CB - из BDECFG32.HLP поле Short name в табличке по указателю language drivers, dBASE или поле Internal в табличке по указателю language drivers, Paradox;
для D3 и выше - не знаю так как у меня её нет, но думаю, что также, как и в D2.

Farid Zaripov
farid@aduis.kiev.ua
(2:463/201.101)



У меня есть текстовые файлы, которые я хочу использовать в запросах к DB2, но не хочу создавать из них постоянные таблицы в базе. Что делать?

Можно воспользоваться табличными функциями (Table Functions). Они позволяют использовать файлы как таблицы. Примеры приведены в руководстве "Embedded SQL Programming Guide".



<< ВЕРНУТЬСЯ В ОГЛАВЛЕНИЕ



Материалы находятся на сайте https://exelab.ru/pro/



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS