Dss v.4.20

Пользовательская документация

 

 

 

Охотников Евгений Анатольевич

г.Гомель, 1998 г.


Оглавление

 

Список сокращений......................................................................................................................

Различия версий Dss....................................................................................................................

Отличия Dss v.4.10 от Dss v.4.00.............................................................................................

Отличия Dss v.4.20 от Dss v.4.10.............................................................................................

Dss v.4.20. Пособие программиста............................................................................................

Назначение.............................................................................................................................

Хранение объектно-ориентированных данных....................................................................

Основные понятия Dss........................................................................................................

Функциональные возможности Dss v.4.20...........................................................................

Схема данных........................................................................................................................

Проектирование хранимых типов........................................................................................

Описание хранимых типов на DssDDL...............................................................................

Регистрация схемы данных в БД........................................................................................

Типичный порядок действий по подготовке и регистрации схемы данных..........................

Модификация схемы данных..............................................................................................

Интерфейс с Dss в языке C++................................................................................................

Открытие БД..........................................................................................................................

Заполнение БД........................................................................................................................

Выделение объектов в БД......................................................................................................

Итерация по типу................................................................................................................

Индивидуальные имена объектов.......................................................................................

Удаление объектов из БД......................................................................................................

Работа с ссылками в Dss.......................................................................................................

Методы..................................................................................................................................

Транзакции.............................................................................................................................

Атрибуты...............................................................................................................................

Работа с идентификаторами...................................................................................................

Информационные функции......................................................................................................

Клонирование типов................................................................................................................

Получение информации о типах базы данных.........................................................................

Инвариантность схемы данных..............................................................................................

Многопользовательские возможности Dss v.4.20...................................................................

Предварительная реализация многопользовательских возможностей в Dss v.4.20.............

Использование нитей в качестве отдельных процессов......................................................

Метод threadAttach.........................................................................................................

Метод threadDetach........................................................................................................

Выделенные нити в случае нескольких БД.....................................................................

Многопользовательский режим работы..............................................................................

Загрузка и выгрузка объектов........................................................................................

Итерации.........................................................................................................................

Транзакции......................................................................................................................

Блокировки......................................................................................................................

Блокировка объектов..................................................................................................

Блокировка клонов......................................................................................................

Блокировка индивидуальных имен объектов...............................................................

Блокировка идентификаторов......................................................................................

Применение специальных средств контроля ошибок......................................................

Управление кэшированием.....................................................................................................

Dss и пространства имен........................................................................................................

Раздельная генерация вспомогательного кода.......................................................................

Одновременная работа с несколькими базами данных......................................................

Большое количество типов в схеме данных.......................................................................

Структура вспомогательного кода.....................................................................................

Утилита dssscg...................................................................................................................

Файл описания размещения вспомогательного кода...........................................................

Пример генерации вспомогательного кода для одновременной работы с двумя базами данных        

Пример генерации вспомогательного кода для сложных схем данных...............................

Пособие по языку DssDDL........................................................................................................

Назначение.............................................................................................................................

Механизм формирования схемы данных................................................................................

Особенности объектной модели Dss и транслятора DssDDL.................................................

Вложенные типы.................................................................................................................

Ограничения на множественное наследование...................................................................

Особенности подтверждения атрибутов.............................................................................

Формат описания на DssDDL.................................................................................................

Комментарии......................................................................................................................

Зарезервированные слова...................................................................................................

Оператор переименования типа..........................................................................................

Оператор переименования атрибута...................................................................................

Декларация нормального типа............................................................................................

Декларация базового типа..................................................................................................

Декларация атрибутов........................................................................................................

Декларация метода.............................................................................................................

Декларация клона...............................................................................................................

Примеры деклараций..........................................................................................................

Связывание DssDDL и C++....................................................................................................

Генерируемый C++ код......................................................................................................

Применение описаний на DssDDL и генерируемого кода в реальных задачах...................

Ограничение.......................................................................................................................

Грамматика языка DssDDL...................................................................................................

Dss v.4.20. Справочное руководство..........................................................................................

Типы Dss v.4.20......................................................................................................................

TDssBool.............................................................................................................................

TDssChar............................................................................................................................

TDssDBError......................................................................................................................

TDssDouble.........................................................................................................................

TDssID...............................................................................................................................

TDssIDType........................................................................................................................

TDssInt...............................................................................................................................

TDssIteratorInterface...........................................................................................................

TDssKernelInterface............................................................................................................

TDssShort............................................................................................................................

TDssSingle...........................................................................................................................

TDssThreadAttach...............................................................................................................

TDssUChar.........................................................................................................................

TDssUInt.............................................................................................................................

TDssUShort.........................................................................................................................

TDssWChar.........................................................................................................................

Константы Dss v.4.20.............................................................................................................

c_bDssFalse........................................................................................................................

c_bDssTrue.........................................................................................................................

c_idDssInvalid......................................................................................................................

c_idtypeDssInvalid...............................................................................................................

c_uiDssDenyReason_Ambiguous..........................................................................................

c_uiDssDenyReason_Nonpublic............................................................................................

c_uiDssMode_ReadOnly......................................................................................................

c_uiDssMode_ReadWrite.....................................................................................................

c_uiDssMode_Unknown.......................................................................................................

c_uiDssMode_WriteShare....................................................................................................

c_uiDssRpLevel1.................................................................................................................

c_uiDssRpLevel2.................................................................................................................

c_uiDssTypeCode_Char.......................................................................................................

c_uiDssTypeCode_Double....................................................................................................

c_uiDssTypeCode_Int..........................................................................................................

c_uiDssTypeCode_Short......................................................................................................

c_uiDssTypeCode_Single.....................................................................................................

c_uiDssTypeCode_UChar....................................................................................................

c_uiDssTypeCode_UInt.......................................................................................................

c_uiDssTypeCode_UShort....................................................................................................

c_uiDssTypeCode_UsrType.................................................................................................

c_uiDssTypeCode_WChar...................................................................................................

c_uiDssTypeSpec_Clone......................................................................................................

c_uiDssTypeSpec_Normal....................................................................................................

c_uiDssTypeSpec_Std..........................................................................................................

Методы класса TDssKernelInterface......................................................................................

attrGetCount........................................................................................................................

attrGetOffset........................................................................................................................

attrGetParams......................................................................................................................

cloneCreate..........................................................................................................................

dbClose................................................................................................................................

dbGetMode..........................................................................................................................

dbOpen................................................................................................................................

dbSetCacheMode.................................................................................................................

dbSwitchMode.....................................................................................................................

idCount................................................................................................................................

idFirstGlobal.........................................................................................................................

idFirstLocal..........................................................................................................................

idGetInfo..............................................................................................................................

idNextGlobal........................................................................................................................

idNextLocal.........................................................................................................................

methodFindByObject............................................................................................................

methodFindByType...............................................................................................................

methodGetCount...................................................................................................................

methodGetParams................................................................................................................

nameCreate.........................................................................................................................

nameFind.............................................................................................................................

nameGetCount.....................................................................................................................

nameGetParams...................................................................................................................

nameRemove.......................................................................................................................

objAllocate...........................................................................................................................

objDeallocate.......................................................................................................................

objFree................................................................................................................................

objLoad................................................................................................................................

objLoadAs...........................................................................................................................

objNonSharedLocation..........................................................................................................

objSave................................................................................................................................

objTypeCast.......................................................................................................................

objUpdate...........................................................................................................................

threadAttach......................................................................................................................

threadDetach......................................................................................................................

transactionBegin.................................................................................................................

transactionEnd....................................................................................................................

transactionTerminate...........................................................................................................

transactionUndo..................................................................................................................

typeEnum...........................................................................................................................

typeEnumBase...................................................................................................................

typeFind.............................................................................................................................

typeGetCount.....................................................................................................................

typeGetIterationParams.......................................................................................................

typeGetObjCount................................................................................................................

typeGetParams...................................................................................................................

typeIsDerivedFrom.............................................................................................................

typeStartIteration................................................................................................................

typeStopIteration.................................................................................................................

Методы класса TDssIteratorInterface...................................................................................

idGoto................................................................................................................................

idNext................................................................................................................................

lastID.................................................................................................................................

lastObjExist........................................................................................................................

objNext..............................................................................................................................

Информационные функции....................................................................................................

IDssInfoGetErrorName.......................................................................................................

IDssInfoGetVersion............................................................................................................

Методы класса TDssThreadAttach.......................................................................................

TDssThreadAttach.............................................................................................................

~TDssThreadAttach...........................................................................................................

isAttached..........................................................................................................................

detach................................................................................................................................

Макросы Dss v.4.20..............................................................................................................

DSS_ADDRESS................................................................................................................

DSS_METHOD_CAST.....................................................................................................

Информирование о непоправимых ошибках..........................................................................

Коды ошибок Dss v.4.20.......................................................................................................

eDBErrorAbstractType.......................................................................................................

eDBErrorAccessDenied.....................................................................................................

eDBErrorAccessLocked.....................................................................................................

eDBErrorAmbiguous..........................................................................................................

eDBErrorBadAddress.........................................................................................................

eDBErrorBadArgument......................................................................................................

eDBErrorCacheModeSelected............................................................................................

eDBErrorDamagePossible..................................................................................................

eDBErrorDestroyLocked....................................................................................................

eDBErrorError...................................................................................................................

eDBErrorFileIDUnknown...................................................................................................

eDBErrorFileNotOpen........................................................................................................

eDBErrorFileOpened..........................................................................................................

eDBErrorFreeID................................................................................................................

eDBErrorInIteration............................................................................................................

eDBErrorInvalidID.............................................................................................................

eDBErrorInvalidObjectName..............................................................................................

eDBErrorInvalidScheme.....................................................................................................

eDBErrorInvalidType..........................................................................................................

eDBErrorInvalidTypeName.................................................................................................

eDBErrorLoaded................................................................................................................

eDBErrorLockDisabled.......................................................................................................

eDBErrorMethodAlreadyDefined........................................................................................

eDBErrorMethodNotImplemented.......................................................................................

eDBErrorNoCompatible......................................................................................................

eDBErrorNoCompatibleOld.................................................................................................

eDBErrorNoMemory..........................................................................................................

eDBErrorNotFound............................................................................................................

eDBErrorNotImplemented..................................................................................................

eDBErrorNotLoaded..........................................................................................................

eDBErrorNotOpen.............................................................................................................

eDBErrorNoTransactions....................................................................................................

eDBErrorObjCopyUnknown................................................................................................

eDBErrorObjInit.................................................................................................................

eDBErrorObjNotInit...........................................................................................................

eDBErrorOffsetUnknown...................................................................................................

eDBErrorOk......................................................................................................................

eDBErrorOpen...................................................................................................................

eDBErrorReadLocked........................................................................................................

eDBErrorReadOnlyMode....................................................................................................

eDBErrorRpItemNotExist...................................................................................................

eDBErrorRpItemNotFinished..............................................................................................

eDBErrorRpItemNotStarted................................................................................................

eDBErrorRpLevel1.............................................................................................................

eDBErrorSameMethodMissing............................................................................................

eDBErrorSameTypeInfoMissing..........................................................................................

eDBErrorSourceIsClone.....................................................................................................

eDBErrorThreadAttached...................................................................................................

eDBErrorThreadNotAttached.............................................................................................

eDBErrorTransactionTerminated.........................................................................................

eDBErrorTypecastDenied...................................................................................................

eDBErrorTypeInfoAlreadyDefined......................................................................................

eDBErrorTypeNotImplemented...........................................................................................

eDBErrorWriteLocked........................................................................................................

Соответствие числовых кодов ошибок и элементов TDssDBError.......................................

Утилиты Dss v.4.20..................................................................................................................

dsschmd: изменение режима базы данных............................................................................

dsscreat: cоздание базы данных............................................................................................

dsspt: печать зарегистрированных типов базы данных..........................................................

dssreg: регистрация типов в базе данных..............................................................................

dssrpr: восcтановление базы данных.....................................................................................

dssscg: раздельная генерация вспомогательного кода..........................................................

dssvid: получение информации об идентификаторе................................................................

dssvnms: просмотр существующих индивидуальных имен объектов....................................

Соотношение Dss v.4.20 и других СУООБД.............................................................................

Явное преобразование идентификатор/указатель.................................................................

Режимы загрузки объектов..................................................................................................

Отсутствие единого корня иерархии классов........................................................................

Именованные методы..........................................................................................................

Выделенные нити.................................................................................................................

 


Список сокращений

 

COM

Component Object Model. Объектная технология фирмы Microsoft.

CORBA

Common Object Request Broker Architecture. Объектная технология консорциума OMG.

Dss

Data Storage System. Система хранения данных.

DssDDL

Dss Data Definition Language. Язык описания данных Dss.

IEEE

Institute of Electrical and Electronics Engineers. Организация, занимающаяся разработкой стандартов в компьютерных технологиях.

ODMG

Object Database Management Group. Подразделение консорциума OMG, занимающееся объектно-ориентированными базами данных.

OMG

Object Management Group. Консорциум ряда крупных фирм, занимающийся развитием объектных технологий.

POSIX

Portable Operating System for Unix. IEEE стандарт, определяющий интерфейс между UNIX и прикладной программой.

АСУТП

Автоматизированная система управления технологическими процессами.

БД

База данных.

ОП

Оперативная память.

СУБД

Система управления базами данных.

СУООБД

Система управления объектно-ориентированными базами данных.


Различия версий Dss

            В данной главе приводятся отличия между различными версиями Dss. При первом ознакомлении с Dss эту главу рекомендуется пропустить.

Отличия Dss v.4.10 от Dss v.4.00

            Cтандартные типы Dss дополнены беззнаковыми целыми типами: unsigned char, unsigned short, unsigned int.

            При изменении типа атрибута с одного стандартного типа на другой стандартный тип утилита dssreg автоматически преобразует старое значение атрибута к новому типу.

            Метод typeGetParams позволяет получать информацию о стандартных типах Dss (char, unsigned char, wchar_t, short, unsigned short, int, unsigned int, float, double). Программы, которые самостоятельно определяли описания этих типов (например, так как указывалось в документации по Dss v.4.10), потеряют работоспособность. Подобные программы необходимо изменить так, как показано в данной документации (см. главу “Пособие программиста”, раздел “Получение информации о типах базы данных”).

            Для итераций поддерживается режим c_uiDssMode_WriteShare.

            В класс TDssKernelInterface добавлен метод typeGetIterationParams, позволяющий определять количество проводимых по типу итераций и их режим.

            В DssDDL добавлен модификатор abstract, позволяющий указать, что хранимый тип является отражением абстрактного C++ класса. Для таких типов строится специальный вспомогательный код.

            Создана утилита dssscg, позволяющая производить раздельную генерацию вспомогательного кода. С ее помощью можно создавать программы, работающие с несколькими БД одновременно. БД могут иметь разные схемы данных.

            Для платформы Linux Dss обеспечивает поддержку многопоточных приложений.

Отличия Dss v.4.20 от Dss v.4.10

            1. В Dss v.4.20 добавлен механизм проверки соответствия описания типа в БД и сгенерированного для него вспомогательного кода. Вспомогательный код в Dss v.4.20 содержит описания наследования (базовые типы, доступность и виртуальность) и собственных атрибутов типа (имена, типы, размерности). При открытии БД сравниваются описания типов в БД и описания типов во вспомогательном коде. Если описания не совпадают, то порождается ошибка eDBErrorInvalidScheme. Данный механизм предотвращает ситуации, когда схема данных модифицируется, а вспомогательный код остается неперекомпилированным и производится попытка работы с БД. В предыдущих версиях Dss такая ситуация оказывалась возможной и приводила к плачевным результатам.

            2. В Dss v.4.20 список типов в схеме данных БД может быть подсписком списка типов во вспомогательном коде, либо список типов во вспомогательном коде может быть подсписком списка типов схемы данных БД. В последнем случае допускается работа только с теми типами, которые присутствуют как во вспомогательном коде, так и в схеме данных БД. Попытки обращения к остальным типам схемы данных БД приводят к возникновению ошибки eDBErrorTypeNotImplemented. Данная возможность Dss v.4.20 позволяет изменять состав схемы данных БД без перекомпиляции вспомогательного кода всех программ, использующих БД, что особенно важно при коллективной разработке приложений.

            3. В Dss v.4.20 множество собственных именованных методов типа в схеме данных БД может быть подмножеством множества собственных именованных методов типа во вспомогательном коде, либо множество собственных именованных методов типа во вспомогательном коде может быть подмножеством собственных именованных методов типа в схеме данных БД. В последнем случае допускается работа только с теми именованными методами, которые присутствуют как во вспомогательном коде, так и в схеме данных БД. Попытки обращения к остальным методам типа приводят к возникновению ошибки eDBErrorMethodNotImplemented. Данная возможность Dss v.4.20 позволяет изменять состав схемы данных БД без перекомпиляции вспомогательного кода всех программ, использующих БД, что особенно важно при коллективной разработке приложений.

            4. На платформах, допускающих многопоточность, Dss v.4.20 позволяет рассматривать нити процесса как один общий процесс (невыделенные нити), или как отдельный процесс (выделенные нити). При использовании невыделенных нитей работа Dss v.4.20 не отличается от работы Dss v.4.10. Т.е. объект, загруженный в одной нити процесса, может быть выгружен в другой нити процесса. Если нить начала транзакцию, то в транзакции учитываются все действия параллельно исполняющихся нитей процесса. В случае невыделенных нитей процесс создает, манипулирует и завершает свои нити, не информируя об этом Dss. Если какая-либо нить производит вызов метода TDssKernelInterface::threadAttach, то Dss начинает рассматривать все действия данной нити как действия нового, совершенно независимого процесса[1]. При этом:

            - выделенная нить может работать с БД, используя другой уровень восстановочной информации, чем указанный при открытии БД;

            - объекты, загруженные выделенной нитью не могут быть выгружены другими нитями;

            - в транзакциях, начатых выделенной нитью, учитываются только действия, выполненные данной нитью;

            - осуществляется блокировка объектов, клонов, имен объектов, задействованных в транзакциях выделенной нити. Блокировка снимается при откате или подтверждении транзакций. В доступе к заблокированным объектам, клонам, именам объектов остальным нитям отказывается;

            - производится автоматическая выгрузка объектов, загруженных выделенной нитью, и завершение итераций, начатых выделенной нитью, при вызове выделенной нитью метода TDssKernelInterface::threadDetach.

            Метод TDssKernelInterface::threadDetach переводит выделенную нить в разряд невыделенных нитей. Dss рассматривает это действие как завершение процесса, чьи действия выполняла выделенная нить.

            5. При откате транзакции Dss v.4.20 автоматически выгружает объекты, загруженные во время транзакции.

            6. При откате транзакции Dss v.4.20 автоматически завершает итерации, начатые во время транзакции.

            7. В языке DssDDL для указания стандартных типов Dss можно использовать псевдонимы TDssChar, TDssUChar, TDssWChar, TDssShort, TDssUShort, TDssInt, TDssUInt, TDssSingle, TDssDouble.

            8. В утилиту dsspt добавлен аргумент “-cloneonly”, при использовании которого печатается только список невложенных клонов схемы данных. Этот аргумент предназначен для извлечения из схемы данных клонов, созданных из программ при помощи метода TDssKernelInterface::cloneCreate. В предыдущих версиях Dss для модификации такой схемы данных необходимо было получить схему данных из БД при помощи утилиты dsspt, внести в нее изменения и зафиксировать изменения в БД при помощи утилиты dssreg. В случае модификации схем нескольких БД, содержащих разные клоны, такой способ оказывался трудоемким и чувствительным к ошибкам. С появлением аргумента “-cloneonly” это действие может выполняться с использованием командных файлов следующего вида (Win32, OS/2):

 

type newscheme.ddl > t.ddl

rem

rem Извлекаем клоны из старой схемы данных

rem и добавляем в новую схему данных

rem

dsspt -db mydb -cloneonly >> t.ddl

rem

rem Регистрируем новую схему данных

rem

dssreg -db mydb -desc t.ddl -code c.cpp -head h.hpp

del t.ddl

 


Dss v.4.20. Пособие программиста

Назначение

Хранение объектно-ориентированных данных

            В последнее время объектно-ориентированный подход получил широкое распространение в производстве программного обеспечения. Одной из причин популярности объектно-ориентированного подхода является удобство представления сложных моделей данных при помощи объектов и их взаимосвязей. Рассмотрим векторное изображение в качестве примера. Изображение представляет собой список элементов. Каждый элемент является отдельной частью изображения. На C++ это можно представить так:

 

class  TImageItem {

       public :

             virtual      ~TImageItem( void ) {

             }

 

             virtual void paint( TPaintDevice & dev ) = 0;

             virtual void queryEnclosedArea(

                    TRectangle & rect ) const = 0;

             virtual void move( const TCoord & x,

                    const TCoord & y, int moveMode ) = 0;

             virtual void rotate( const TRotateParams & p ) = 0;

};

 

class  TImageItemList {

       protected :

             TImageItem * m_pItem;

             TImageItemList * m_pNext;

       public :

             TImageItemList( void );

             ~TImageItemList( void );

 

             TImageItem * queryItem( void );

             void   setItem( TImageItem * pItem );

 

             TImageItemList *    queryNext( void );

             void   setNext( TImageItemList * pNext );

};

 

class  TImage {

       protected :

             TImageItemList * m_pFirst;

 

       public :

             ...

};

 

Элементами изображения могут быть произвольные объекты:

 

class  TRectangle : public TImageItem {

       ...

};

 

class  TMetaFile : public TImageItem {

       ...

};

 

class  TBitMap : public TImageItem {

       ...

};

 

class  TAnimation : public TImageItem {

       ...

};

 

            Представленные таким образом изображения можно использовать в различных задачах. Например, в программном комплексе АСУТП изображения могут использоваться для отображения схем контролируемых технологических процессов. Объект изображения:

 

class  TDataShower : public TImageItem, public TMeterReceiver {

       ...

};

 

может отображать на схеме данные, получаемые с устройств съема информации. Объект изображения:

 

class  THandler : public TImageItem,

       public TDialog,

       public TDataSender {

       ...

};

 

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

            Если необходимо отображать какую-либо иерархическую схему, выбор элемента на которой должен приводить к открытию нового элемента изображения с более подробной схемой выбранного элемента, то можно поступить так:

 

/* Базовый класс для всех элементов иерархической структуры */

class  TStructureUnit {

       protected :

             /* Изображение данного элемента */

             TImage * m_pPortrait;

 

       public :

             TImage *     queryPortrait( void );

};

 

/* Элемент изображения, отвечающий за отображение и открытие элемента структуры */

class  TImageItem_StructureUnit : public TImageItem {

       protected :

             TStructureUnit * m_pUnit;

 

       public :

             /* Этот метод вызывается при выборе элемента изображения */

             void   activate( void ) {

                    /* Создаем новое окно с подробным

                       изображением элемента иерархической

                       структуры */

                    new TImagePlayer( m_pUnit->queryPortrait() );

             }

};

 

            В реальных задачах необходимо хранить такие описания во внешней памяти. Однако, насколько удобен объектно-ориентированный подход для представления моделей данных в оперативной памяти, настолько он не удобен для представления этих же моделей во внешней памяти. Происходит это по следующим причинам. Во-первых, оперативная память является единым хранилищем для всех типов объектов. В реляционных моделях данных для каждого типа объектов должна использоваться отдельная таблица. Во-вторых, в оперативной памяти существует простой и удобный способ идентификации объектов - указатели (адреса). В адресном пространстве процесса не может быть двух разных объектов, расположенных по одному адресу. При этом для всех типов объектов используется единое пространство адресов. В реляционном подходе единое пространство уникальных ключей для всех записей всех таблиц достигается с помощью дополнительной таблицы, содержащей значения ключей. В виду вышеперечисленных причин хранение объектно-ориентированных данных в реляционных СУБД является сложным, зачастую неэффективным решением. Специально для хранения объектно-ориентированных данных применяются системы управления объектно-ориентированными данными. Одной из таких систем является СУООБД Dss.

Основные понятия Dss

            Dss предназначена для хранения объектно-ориентированных данных во внешней памяти. Для этого Dss обеспечивает обе вышеуказанные особенности оперативной памяти: единое хранилище для объектов всех типов и единое пространство идентификаторов для всех объектов. Dss ориентированна, в первую очередь, на язык программирования C++.

            Основными понятиями Dss являются:

            - БД - схема данных и хранилище объектов. На физическом уровне БД представляется шестью файлами с заданным именем и расширениями .di, .tl, .r0, .of0, .nms и .rp1;

            - схема данных - описания типов, объекты которых хранятся в БД;

            - тип - отображение C++ класса или структуры в БД. В типе описывается подмножество, возможно, пустое атрибутов C++ класса, которые хранятся в БД. Каждый тип получает уникальный идентификатор типа. Работа с типами в Dss осуществляется по идентификаторам типов;

            - объект - экземпляр типа схемы данных. Каждый объект получает уникальный идентификатор объекта (или просто идентификатор). Работа с объектами в Dss осуществляется по идентификаторам объектов.

            Типы являются отображением C++ классов в БД. При их определении могут использоваться следующие возможности:

            - наследование - поддерживается одиночное, множественное и виртуальное множественное наследование;

            - вложенность - поддерживается не ограниченная по глубине вложенность типов (типы вида A::B::C, D::E::F::G::I и т.д.).

            Основными операциями над БД являются:

            1) регистрация схемы данных - внесение схемы данных в БД;

            2) модификация схемы данных - внесение в БД изменений схемы данных[2]. Это действие затрагивает как схему данных, так и объекты БД;

            3) создание объекта в БД. По указанному идентификатору типа создается объект в БД. Объекту назначается идентификатор. Назначенный идентификатор возвращается как результат операции;

            4) загрузка объекта в ОП. По указанному идентификатору определяется тип загружаемого объекта. В ОП создается C++ объект этого типа. В созданный C++ объект загружаются значения атрибутов из БД;

            5) запись объекта в БД. Выполняется для загруженных объектов. По указанному идентификатору определяется C++ объект, созданный при загрузке. Значения его атрибутов записываются в БД;

            6) выгрузка объекта из ОП. Выполняется для загруженных объектов. По указанному идентификатору определяется C++ объект, созданный при загрузке, и удаляется из ОП (вызывается деструктор и освобождается выделенная память). Значения атрибутов объекта в БД не записываются. Объект из БД не удаляется;

            7) удаление объекта из БД. Выполняется для незагруженных объектов. Объект с указанным идентификатором удаляется из БД. Идентификатор удаленного объекта становится недействительным.

Функциональные возможности Dss v.4.20

            Dss v.4.20 является однопользовательской СУООБД.

            Dss v.4.20 обеспечивает аппаратную независимость данных. Поддерживаются аппаратные платформы Intel x86, Sun SPARC.

            Dss v.4.20 функционирует в операционных системах Windows 95 и Windows NT, OS/2, Linux, Solaris.

            Dss v.4.20 совместима с компиляторами Borland C++ 5.*; Microsoft Visual C++ 4.*, 5.*; WATCOM C++ 11.0; IBM Visual Age C++ 3.0; GNU C++ 2.7.2.

            Dss v.4.20 поддерживает вложенные транзакции;

            Dss v.4.20 поддерживает модификацию схемы данных на уровне описаний типов.

            Dss v.4.20 позволяет использовать как одиночное, так и множественное наследование, включая виртуальное множественное наследование.

            Dss v.4.20 предоставляет библиотеку классов и функций для доступа к БД из C++.

            На платформах Windows 95, Windows NT, OS/2 и Linux[3] Dss v.4.20 поддерживает многопоточные приложения. В этих приложениях автоматически обеспечивается синхронизация действий над БД.

            Для создания C++ объектов в ОП, их уничтожения и определения расположения атрибутов в объекте, Dss использует вспомогательный код - набор функций и векторов данных, генерируемых при регистрации схемы данных. Вспомогательный код должен подключаться ко всем программам, использующим БД.

Схема данных

            C++ классы и структуры, отображаемые в схему данных, называются хранимыми типами[4]. C++ классы и структуры, не отображаемые в схему данных, называются нехранимыми типами.

Проектирование хранимых типов

            Не все атрибуты C++ класса могут нуждаться в хранении. Например, атрибут “возраст” может высчитываться на основе значения атрибута “дата рождения” и текущей даты. Решение о хранении таких атрибутов принимается на основе требований по скорости работы/объему данных в конкретных задачах.

            Другой разновидностью атрибутов, не подлежащих хранению в БД, являются указатели. Во-первых, размер указателя зависит от платформы (процессора, операционной системы, компилятора, адресной арифметики). Во-вторых, значение указателя может изменяться при каждом запуске программы. Поэтому нельзя гарантировать, что указатель, имевший корректное значение в одном сеансе, будет корректным в другом сеансе работы с БД. По этим причинам атрибуты-указатели не могут сохраняться в БД.

            Далее атрибуты, сохраняемые в БД, называются хранимыми. Не сохраняемые атрибуты называются расширяющими[5].

            Атрибуты-указатели выполняют в C++ классах очень важную роль. Взаимосвязи (ссылки) между объектами осуществляются с помощью указателей. Но, как показано выше, атрибуты-указатели не могут быть хранимыми атрибутами. Для осуществления взаимосвязей между объектами в БД применяются идентификаторы. Идентификатор в БД является аналогом указателя, но не зависит от платформы и сеансового характера работы с БД. Отличительной особенностью Dss является то, что при проектировании хранимых типов необходимо учитывать оба способа организации ссылок между объектами. В БД объекты ссылаются друг на друга при помощи идентификаторов. В ОП объекты ссылаются друг на друга при помощи указателей. Поэтому, при описании типа на C++ необходимо для каждой ссылки вводить два атрибута: хранимый атрибут-идентификатор и расширяющий атрибут-указатель:

 

class  TImageItemList {

       dss_type( TImageItemList );

       protected :

             dss_attr TDssID     m_idItem;

             TImageItem * m_pItem;

             dss_attr TDssID     m_idNext;

             TImageItemList *    m_pNext;

       ...

};

 

            В хранимых типах допускается использование наследования. Dss поддерживает одиночное, множественное и виртуальное множественное наследование:

 

class  T1 {

       dss_type( T1 );

       ...

};

class  T2 : public T1 {

       dss_type( T2 );

       ...

};

class  T3 {

       dss_type( T3 );

       ...

};

class  T4 : public T2, public T3 {

       dss_type( T4 );

       ...

};

class  T5 : virtual public T3, public T4 {

       dss_type( T5 );

       ...

};

 

            Dss не требует единого корня иерархии хранимых типов. При наследовании допускается использование public, protected или private атрибутов доступности базовых типов:

 

class  T1 {

       dss_type( T1 );

       ...

};

class  T2 : public T1 {

       dss_type( T2 );

       ...

};

class  T3 : protected T1 {

       dss_type( T3 );

       ...

};

class  T4 : private T1 {

       dss_type( T4 );

       ...

};

 

            Хранимые типы могут быть вложенными:

 

class  T1 {   // T1

       dss_type( T1 );

       class  T2 {   // T1::T2

             dss_type( T1__T2 );

             ...

       };

       ...

};

 

Глубина вложенности типов не ограничивается. Хранимый тип не может быть вложенным в нехранимый тип.

            Параметризованные классы (классы-шаблоны) не могут быть хранимыми типами.

            Регистрация хранимых типов в БД производится утилитой dssreg. Утилита dssreg воспринимает описания не на C++, а на языке описания данных Dss - DssDDL. Поэтому после проектирования и описания хранимых типов на C++ необходимо составить описание схемы данных на DssDDL.

Описание хранимых типов на DssDDL

            Язык DssDDL - это C-подобный язык описания данных. В описании на DssDDL перечисляются только хранимые типы. В типе перечисляются только хранимые атрибуты:

 

struct TImageItemList {

       TDssID m_idItem;

       TDssID m_idNext;

};

 

            Типом атрибута может быть либо один из стандартных типов Dss, либо один из предварительно описанных пользовательских типов. Стандартными типами Dss являются:

char

8-битовое знаковое целое число

unsigned char

8-битовое беззнаковое целое число

wchar_t

16-битовое беззнаковое целое число

short

16-битовое знаковое целое число

unsigned short

16-битовое беззнаковое целое число

int

32-битовое знаковое целое число

unsigned int

32-битовое беззнаковое целое число

float

32-битовое вещественное число одинарной точности (IEEE 754)

double

64-битовое вещественное число двойной точности (IEEE 754)

Тип char воспринимается как обычный целочисленный тип, несмотря на то, что с его помощью хранятся символьные значения. Поэтому на программиста ложится вся ответственность за используемый набор символов (ASCII, EBCDIC, кодовая страница ASCII для национальных алфавитов). Тип long в настоящее время не поддерживается[6]. Тип TDssID воспринимается как стандартный тип Dss и в описании схемы данных раскрываться не должен[7]. Т.о. следующее C++ описание:

 

class  TSample {

       dss_type( TSample );

       protected :

             dss_attr TDssID m_idSampleRef;

             void * m_pSampleRef;

       public :

             dss_attr unsigned short m_us;

             dss_attr int m_i;

       private :

             dss_attr unsigned char m_sz[ 10 ];

};

 

должно быть представлено на DssDDL так:

 

struct TSample {

       TDssID m_idSampleRef;

       unsigned short      m_us;

       int    m_i;

       unsigned char m_sz[ 10 ];

};

 

            Абстрактный C++ класс[8] на DssDDL должен описываться с использованием модификатора abstract:

 

// C++

class  TRoot {

       dss_type( TRoot );

       protected :

             dss_attr int m_iKey;

 

       pubic :

             dss_meth void create( void );

 

             virtual void dump( ostream & o ) = 0;

             virtual ~TRoot( void );

};

 

// DssDDL

abstract struct TRoot {

       int m_iKey;

       method create TRoot::create;

};

 

Описанный таким образом тип считается абстрактным хранимым типом. Абстрактный тип нельзя использовать в качестве типов атрибутов. В БД нельзя создавать объекты абстрактных типов.

            Абстрактным хранимым типом так же считается хранимый тип, не имеющий атрибутов. Это может произойти, если тип является вершиной иерархии типов и вводит только интерфейсные методы. При описании такого типа модификатор abstract может не указываться[9]:

 

// Такое описание будет воспринято как

// неявное описание абстрактного типа

struct TImplicitAbstractType {

};

 

            Наследование в DssDDL описывается аналогично C++. Атрибуты доступности базовых типов protected и private в Dss не используются. Если в описании наследования на DssDDL не используется ключевое слово public, то указанный класс считается недоступным базовым классом:

 

struct T1 {

       ...

};

struct T2 : public T1 { // доступный базовый класс T1

       ...

};

struct T3 : T1 { // недоступный базовый класс T1

       ...

};

struct T4 : T1 { // недоступный базовый класс T1

       ...

};

 

            Более подробно DssDDL описывается в “Пособии по языку DssDDL”.

Регистрация схемы данных в БД

            Регистрация схемы данных в БД осуществляется утилитой командной строки dssreg[10]. На вход dssreg поступают БД и описание схемы данных на DssDDL. Описание транслируется, и проверяется корректность схемы данных. Каждому типу назначается идентификатор типа. Затем описание схемы данных во внутреннем формате Dss помещается в БД.

            Для каждого типа генерируется три вспомогательные функции на языке C++. Указатели на все сгенерированные функции помещаются в вектор с именем dss_type_info. Сгенерированные функции и вектор dss_type_info являются тем вспомогательным кодом, который использует Dss для работы с объектами. Вспомогательный код помещается в файл, имя которого передается на вход dssreg.

            Рассмотрим пример. Описание хранимого типа на C++:

 

class  TImageItemList {

       dss_type( TImageItemList );

       protected :

             dss_attr TDssID m_idItem;

             TImageItem * m_pItem;

             dss_attr TDssID m_idNext;

             TImageItemList * m_pNext;

       public :

             TImageItemList( void );

             ~TImageItemList( void );

 

             TImageItem * queryItem( void );

             void   setItem( TImageItem * pItem );

 

             TImageItemList *    queryNext( void );

             void   setNext( TImageItemList * pNext );

};

 

Описание этого типа на DssDDL:

 

struct TImageItemList {

       TDssID m_idItem;

       TDssID m_idNext;

};

 

После регистрации схемы данных будет сгенерирован приблизительно такой вспомогательный код[11]:

 

#include <dssi.hpp>

#include <dssmcrs.hpp>

...

void * dss_create_TImageItemList( unsigned int ) {

       return new TImageItemList();

}

void   dss_destroy_TImageItemList( TImageItemList * p ) {

       delete p;

}

char **      dss_get_offs_TImageItemList( TImageItemList * p ) {

       static char * offs[ 2 ];

       offs[ 0 ] = (char *) &p->m_idItem;

       offs[ 1 ] = (char *) &p->m_idNext;

       return offs;

}

...

 

            Данный код не будет скомпилирован по следующим причинам:

            1) имя TImageItemList является неизвестным, поскольку не подключается заголовочный файл с описанием TImageItemList. Подключаются только заголовочные файлы dssi.hpp и dssmcrs.hpp, в которых этого описания нет;

            2) в функции dss_get_offs_TImageItemList нельзя обращаться к атрибутам m_idItem и m_idNext, поскольку:

            а) они являются защищенными атрибутами класса TImageItemList;

            б) функция dss_get_offs_TImageItemList не является членом класса TImageItemList;

            в) функция dss_get_offs_TImageItemList не является дружественной для класса TImageItemList.

            Наиболее важной является причина №2. Для ее преодоления dssreg, помимо вспомогательного кода, генерирует вспомогательный заголовочный файл. Его имя подается на вход dssreg. Сгенерированный заголовочный файл содержит раскрытие макросов dss_type, dss_attr и dss_meth[12]:

 

#include <dssmcrs.hpp>

 

#define dss_type(t) dss_type_##t

#define dss_attr

#define dss_meth static

...

#define dss_type_TImageItemList\

friend void *  dss_create_TImageItemList( unsigned int );\

friend void    dss_destroy_TImageItemList( TImageItemList * p );\

friend char ** dss_get_offs_TImageItemList( TImageItemList * p )

...

 

Этот файл необходимо подключать ко всем описаниям хранимых типов. Тогда макросы dss_type, dss_attr и dss_meth будут нормально раскрыты, и получится следующее описание типа TImageItemList (после раскрытия макросов препроцессором C++):

 

class  TImageItemList {

friend void *  dss_create_TImageItemList( unsigned int );

friend void    dss_destroy_TImageItemList( TImageItemList * p );

friend char ** dss_get_offs_TImageItemList( TImageItemList * p );

       protected :

             TDssID m_idItem;

             TImageItem * m_pItem;

             TDssID m_idNext;

             TImageItemList * m_pNext;

       public :

             TImageItemList( void );

             ~TImageItemList( void );

 

             TImageItem * queryItem( void );

             void   setItem( TImageItem * pItem );

 

             TImageItemList *    queryNext( void );

             void   setNext( TImageItemList * pNext );

};

 

Т.о. функция dss_get_offs_TImageItemList объявляется дружественной для класса TImageItemList. Внутри нее можно обращаться к любым атрибутам класса TImagItemList.

            Устранить причину №1 можно двумя способами. Во-первых, сгенерированный вспомогательный код можно откорректировать вручную и добавить подключение необходимых заголовочных файлов. Во-вторых, на вход dssreg можно указать имена файлов, содержимое которых будет помещаться перед и после сгенерированного кода. В отдельный файл помещаются директивы #include, его имя передается утилите dssreg, а содержимое (т.е. все директивы #include) переносится в начало сгенерированного кода. После этого вспомогательный код готов к использованию.

            Необходимо отметить еще одну особенность, связанную со вспомогательным кодом. При загрузке объекта типа T Dss обращается к функции dss_create_T:

 

void * dss_create_T( unsigned int ) {

       return new T();

}

 

Из-за такого способа создания C++ объектов в ОП необходимо, чтобы все хранимые типы предоставляли конструктор по умолчанию (default constructor)[13].

Типичный порядок действий по подготовке и регистрации схемы данных

            Опыт использования Dss показывает, что типичным порядком действий при подготовке и регистрации схемы данных является следующий порядок действий:

            1) выбирается имя вспомогательного заголовочного файла (например, dbinit.hpp);

            2) создаются описания хранимых типов на C++. В каждый заголовочный файл подключается вспомогательный заголовочный файл. Например, файл image.hpp может выглядеть так:

 

#ifndef _IMAGE_HPP_

#define _IMAGE_HPP_

 

#include “dbinit.hpp”

 

class  TImageItem {

       dss_type( TImageItem );

       ...

};

class  TImageItemList {

       dss_type( TImageItemList );

       ...

};

class  TImage {

       dss_type( TImage );

       ...

};

 

#endif

 

            3) создаются описания хранимых типов на DssDDL (например, в файле scheme.ddl);

            4) создается файл, содержащий директивы #include. Например, файл codetop.cpp:

 

#include “image.hpp”

 

            5) выбирается имя файла для вспомогательного кода и запускается утилита dssreg. Например, пусть вспомогательный код помещается в файл dbinit.cpp. Тогда командная строка будет выглядеть так:

 

dssreg -db image -desc scheme.ddl -code dbinit.cpp -head dbinit.hpp -codetop codetop.cpp

 

В результате, в БД с именем image будет зарегистрирована схема данных, описанная в файле scheme.ddl. Раскрытие макросов dss_type, dss_attr, dss_meth будет помещено в файл dbinit.hpp. Вспомогательный код будет помещен в файл dbinit.cpp. В начало вспомогательного кода будет помещено содержимое файла codetop.cpp (в данном примере - это директива #include ”image.hpp”);

            6) файл со вспомогательным кодом указывается в проектах всех программ, использующих БД.

Модификация схемы данных

            Итеративный процесс разработки программного обеспечения обуславливает необходимость изменения схемы данных с течением времени. Может понадобиться переименовывать типы и атрибуты, добавлять или удалять типы, добавлять или удалять атрибуты, изменять размерность атрибутов, вносить изменения в иерархию типов. Эти действия называются модификацией или эволюцией схемы данных. Dss позволяет осуществлять модификацию схемы данных на уровне языка DssDDL.

            Для модификации схемы данных применяются те же действия, что и для регистрации схемы данных. Сначала все необходимые изменения вносятся в описания хранимых типов на C++. Затем хранимые типы описываются на DssDDL, и запускается утилита dssreg с теми же параметрами[14]. Однако, вместо регистрации схемы данных, dssreg сравнит схему данных из БД и из описания на DssDDL и отразит найденные изменения в БД. Будет переписана схема данных и исправлены все изменившиеся объекты. Так, если какой-либо тип был удален из схемы, то будут удалены и все объекты этого типа. Если в каком-либо типе был добавлен какой-либо атрибут, то новый атрибут будет добавлен во все существующие объекты этого типа. Соответствующие изменения будут выполнены при удалении атрибута и изменении размерности атрибута.

            Для удаления типа из схемы данных необходимо удалить его описание из описания схемы данных на DssDDL. Для удаления атрибута необходимо удалить его описание из описания типа на DssDDL. Для добавления нового типа в схему данных необходимо добавить его описание в описание схемы данных на DssDDL. Для добавления нового атрибута необходимо добавить его описание в описание типа на DssDDL. Для изменения размерности атрибута необходимо указать новую размерность в описании атрибута.

            Если в старом и новом описаниях типа выявлены атрибуты с одинаковым именем, но разных типов, то:

            - если старый и новый атрибут принадлежат к стандартным типам Dss (char, unsigned char, wchar_t, short, unsigned short, int, unsigned int, float, double), то считается, что это один и тот же атрибут, но с изменившимся типом. Утилита dssreg автоматически выполняет преобразование старого значения атрибута к новому типу;

            - в противном случае считается, что старый атрибут удаляется и добавляется новый атрибут другого типа.

            Особым образом происходит переименование типов и атрибутов. В описании типов на C++ необходимо просто заменить имена. В начале описания на DssDDL необходимо вставить специальные операторы переименования. Обычные описания типов, но уже с использованием только новых имен, должны следовать за операторами переименования. Пример. Пусть в старой схеме описан тип:

 

// C++

class  TImageList {

       dss_type( TImageList );

       protected :

             dss_attr TDssID m_idNextList;

             TImageList * m_pNextList;

       ...

};

 

// DssDDL

struct TImageList {

       TDssID m_idNextList;

       ...

};

 

Необходимо переименовать тип TImageList в TImageItemList, а его атрибут m_idNextList в m_idNext. Описание типа на C++ примет вид:

 

class  TImageItemList {

       dss_type( TImageItemList );

       protected :

             dss_attr TDssID m_idNext;

             TImageList * m_pNext;

       ...

};

 

Описание схемы данных на DssDDL должно выглядеть следующим образом:

 

rename type TImageList TImageListItem;

// Типа с именем TImageList уже не существует.

// Для переименования атрибута указываем новое имя типа.

rename attr TImageItemList::m_idNextList m_idNext;

...

// Старый тип TImageList под новым именем

struct TImageItemList {

       // Старый атрибут m_idNextList под новым именем

       TDssID m_idNext;

       ...

};

 

            Более подробно особенности модификации схемы данных описываются в “Пособии по языку DssDDL”.

Интерфейс с Dss в языке C++

            Основой Dss v.4.20 является библиотека C++ классов, условно называемая DssLibrary. На основе этой библиотеки реализованы главные составляющие Dss: утилиты управления базами данных и библиотека работы с БД из программ, написанных на C++. Последняя библиотека условно называется DssInterface. DssInterface полностью скрывает от программиста DssLibrary. DssInterface предоставляет набор C-функций, обращения к которым приводят, в конечном счете, к обращениям к методам двух основных классов библиотеки DssLibrary - TDssKernel и TDssIterator. Эти классы отвечают за работу с БД из программ, использующих DssInterface. Из-за этого классы TDssKernel и TDssIterator настолько специфично работают с БД, что ни одна из утилит Dss не использует их.

            Библиотека DssInterface предоставляет в распоряжение пользователя набор обычных C-функций (не C++-функций!)[15] и реализована в виде динамически загружаемой библиотеки dssi.dll для платформ Win32 и OS/2, а так же в виде статической библиотеки libdssi.a для платформ Linux и Solaris. Реализация DssInterface для различных платформ имеет следующие различия:

            - для платформ Win32 и OS/2 dssi.dll создавалась таким образом, чтобы имена импортируемых из нее функций были одинаковыми для всех компиляторов. Т.к. генерация результирующего имени для C-функции, объявленной с использованием модификаторов _cdecl и _export (__declspec(dllexport) для Visual C++), не стандартизирована, то формирование имен C-функций происходит с применением специальных уловок. При этом учитываются особенности формирования имен функций конкретного компилятора. На сегодняшний день распознаются компиляторы Borland C++ 4.5 (Win32, OS/2), 5.* (Win32); WATCOM C++ 10.0, 11.0 (Win32, OS/2); Visual C++ 4.*, 5.0 (Win32), IBM Visual Age C++ 3.0 (OS/2). В других компиляторах, скорее всего, имена будут формироваться неверно, что приведет к невозможности линкования dssi.dll;

            - поскольку динамически загружаемые библиотеки не являются стандартным средством в UNIX, то для операционных систем Linux и Solaris DssInterface реализована в виде статической библиотеки libdssi.a.

            Программисту, использующему DssInterface, не нужно обращаться к интерфейсным функциям напрямую. Исключение составляют информационные функции (см. “Информационные функции”). Остальные интерфейсные функции инкапсулированы в классах TDssKernelInterface и TDssIteratorInterface. Данные классы предоставляют тот же интерфейс, что и классы TDssKernel и TDssIterator из DssLibrary. Классы TDssKernelInterface и TDssIteratorInterface не содержат виртуальных методов (т.е. отсутствуют виртуальные таблицы), и все методы реализованы в виде inline-методов. Для их применения необходим только заголовочный файл с их описанием и библиотека DssInterface.

            Идентификатор типа представляется в C++ типом TDssIDType (32-битовое беззнаковое целое). Незадействованный идентификатор типа (аналог NULL для указателей) представляется константой c_idtypeDssInvalid.

            Идентификатор объекта представляется в C++ структурой TDssID. Незадействованный идентификатор (аналог NULL для указателей) представляется константой c_idDssInvalid.

            Dss предоставляет программисту следующие доступные заголовочные файлы, расположенные в  каталоге dss/cpp/h:

            - dssi.hpp, содержащий прототипы интерфейсных функций библиотеки и описания классов TDssKernelInterface, TDssIteratorInterface;

            - dssdef.hpp, подключаемый в dssi.hpp и содержащий описания основных типов Dss (т.к. TDssIDType, TDssDBError, TDssID, TDssObjAddress и т.д.);

            - dssmcrs.hpp, подключаемый в генерируемом вспомогательном коде и содержащий описания макросов для вспомогательного кода и прототипы переменных dss_type_info, dss_type_methods, необходимых для открытия БД.

            Таким образом, для использования Dss из программ на языке C++ необходимо:

            - в C++ файлах подключать заголовочный файл dssi.hpp;

            - в путь стандартных заголовочных файлов для компилятора добавить dss/cpp/h (где dss - путь к каталогу с Dss);

            - для платформ Win32 и OS/2 указать библиотеку импорта dss/cpp/lib/dssi.lib, для платформ Linux и Solaris указать библиотеку dssi и путь dss/cpp/lib;

            - в Win32 и OS/2 позаботиться о том, чтобы dssi.dll, которая находится в каталоге dss/bin, была скопирована в каталог с исполнимым файлом, либо была доступна через переменную среды PATH.

            Пример C++ файла:

 

#include <dssi.hpp>

...

TDssKernelInterface dss;

...

 

            Пример компиляции С++ файла, использующего Dss:

 

Visual C++ for Win32

cl -c -ID:\DSS\CPP\H sample.cpp

link /OUT:sample.exe /LIBPATH:D:\DSS\LIB /DEFAULTLIB:dssi.lib sample.obj

 

GNU C++ for Linux

g++ -I/home/dssuser/dss/cpp/h sample.cpp -L/home/dssuser/dss/lib -ldssi -lg++ ‑lpthread

 

Примечание. Для Win32 Dss компилируется при помощи Visual C++, и библиотека импорта строится для этого компилятора. Если Dss используется из другого компилятора, то библиотеку импорта необходимо создать самостоятельно.

WATCOM C++:

wlib -n dssi.lib +D:\DSS\BIN\dssi.dll

Borland C++ (версия не ниже 5.0):

implib dssi.lib D:\DSS\BIN\dssi.dll

Открытие БД

            Для получения доступа к данным из БД необходимо выполнить открытие БД. Открытие БД осуществляется методом:

 

TDssDBError  dbOpen( const TDssChar * pcszDatabase, TDssUInt uiOpenMode, TDssUInt uiRpLevel, const TDssUserTypeInformation * ptypeInfo, const TDssUserTypeMethods * ptypeMethods );

 

Данный метод открывает базу данных с именем, указанным в параметре pcszDatabase. Режим работы с БД указывается в параметре uiOpenMode. Уровень детализации восстановочной информации указывается в параметре uiRpLevel[16]. Параметры ptypeInfo и ptypeMethods указывают на векторы, сгенерированные утилитой dssreg. В Dss v.4.20 эти векторы имеют имена dss_type_info и dss_type_methods (при использовании утилиты dssreg), либо заданные пользователем имена (при использовании утилиты dssscg, см. “Раздельная генерация вспомогательного кода”). Прототипы векторов dss_type_info и dss_type_methods описываются в заголовочном файле dssmcrs.hpp.

            Режим открытия БД зависит как от характера работы с БД, так и от собственного режима БД. Каждая БД имеет собственный режим и может находиться либо в режиме c_uiDssMode_ReadWrite (допускается модификация БД), либо в режиме c_uiDssMode_ReadOnly (модификация запрещена). В методе dbOpen производится сравнение параметра uiOpenMode и собственного режима БД. Открытие БД осуществляется только, если собственный режим БД допускает работу в режиме uiOpenMode. Базу данных, находящуюся в режиме c_uiDssMode_ReadWrite можно открыть либо в режиме c_uiDssMode_ReadWrite, либо в режиме c_uiDssMode_ReadOnly. Базу данных, находящуюся в режиме c_uiDssMode_ReadOnly можно открыть только в режиме c_uiDssMode_ReadOnly.

            Если БД открывается в режиме c_uiDssMode_ReadWrite, то и все файлы БД открываются для чтения-записи без разрешения их использования другими процессами. Если БД открывается в режиме c_uiDssMode_ReadOnly, то все файлы БД открываются в режиме только-чтение и могут быть открыты в этом же режиме другими процессами. В этом случае несколько процессов могут работать с одной БД. Если БД открыта в режиме c_uiDssMode_ReadOnly, то все методы, изменяющие БД, завершаются с кодом ошибки eDBErrorReadOnlyMode.

            Параметр uiOpenMode может принимать значение c_uiDssMode_Unknown. В этом случае режимом открытия БД будет собственный режим БД. Такой способ открытия БД может быть полезным в следующем случае. Если в каком-либо программном комплексе обнаруживается ошибка, из-за которой в БД помещаются неверные данные, то на время исправления этой ошибки БД можно перевести в режим c_uiDssMode_ReadOnly. Если БД открывается в режиме c_uiDssMode_Unknown, то можно использовать те части программного комплекса, которые только читают данные. Попытки изменения БД будут пресекаться. Это позволит защитить БД от записи неверной информации.

            Перевод БД из одного режима в другой производится утилитой dsschmd. Осуществить это из программы можно при помощи метода:

 

TDssDBError  dbSwitchMode( const TDssChar * pcszDatabase, TDssUInt uiOpenMode, TDssUInt uiRpLevel, const TDssUserTypeInformation * ptypeInfo, const TDssUserTypeMethods * ptypeMethods );

 

Данный метод переводит БД в режим uiOpenMode и открывает ее в этом же режиме.

            Метод:

 

TDssDBError  dbGetMode( TDssUInt * puiModeOpen, TDssUInt * puiModeDB );

 

позволяет определить режим открытия БД (параметр puiModeOpen) и собственный режим БД (параметр puiModeDB). Значения возвращаются только для ненулевых параметров. Данный метод должен вызываться после открытия БД.

            Закрытие открытой БД производится методом:

 

TDssDBError  dbClose( void );

 

Деструктор класса TDssKernelInterface закрывает БД автоматически. При закрытии БД не выполняется никаких действий по подтверждению или откату начатых транзакций (см. “Транзакции”).

            Ниже приведен пример программы, получающей в командной строке имя базы данных и режим открытия БД. Режим открытия задается при помощи значений “readonly”, “readwrite” или “unknown”. Программа открывает указанную БД в указаном режиме, определяет собственный режим БД и отображает его на стандартный поток вывода.

 

#include <iostream.h>

#include <string.h>

 

#include <dssi.hpp>

#include <dssmcrs.hpp>

 

void   dss_show_error( TDssDBError dbe,

       const char * pcszFile, int iLine ) {

       if( eDBErrorOk != dbe )

             cerr << "dbe==" << dbe << " "

                    << pcszFile << ", " << iLine << endl;

}

 

#define dss_check(dbe) dss_show_error( dbe, __FILE__, __LINE__ )

 

void   main( int argc, char ** argv ) {

       if( 3 == argc ) {

             /* Определяем режим открытия БД */

             TDssUInt uiMode;

             if( 0 == strcmp( argv[ 2 ], "readonly" ) )

                    uiMode = c_uiDssMode_ReadOnly;

             else

                    if( 0 == strcmp( argv[ 2 ], "readwrite" ) )

                           uiMode = c_uiDssMode_ReadWrite;

                    else

                           if( 0 == strcmp( argv[ 2 ], "unknown" ) )

                                  uiMode = c_uiDssMode_Unknown;

                           else {

                                  cout << "invalid open mode" << endl;

                                  return;

                           }

 

             /* Открываем базу данных */

             TDssKernelInterface db;

             dss_check( db.dbOpen( argv[ 1 ], uiMode,

                    c_uiDssRpLevel1, dss_type_info,

                    dss_type_methods() ) );

 

 

             /* Определяем собственный режим БД */         

             TDssUInt uiDBMode = 0;

             dss_check( db.dbGetMode( 0, &uiDBMode ) );

 

             cout << "БД находится в режиме: " << uiDBMode << endl;

       }

}

Заполнение БД

            Объекты создаются в БД при помощи метода:

 

TDssDBError  objAllocate( TDssIDType idt, TDssID & id, const void * pcvInitial = 0 );

 

В случае успешного создания объекта в id возвращается его идентификатор. Если pcvInitial == 0, то объект инициализируется значениями по умолчанию (для всех стандартных типов это значение 0, для типа TDssID - эквивалент c_idDssInvalid). Если pcvInitial не 0, то считается, что для объекта задано начальное значение и объект инициализируется этим значением. Параметр idt указывает тип, объект которого должен быть создан:

 

...

TDssIDType idt;

dss_check( dss.typeFind( “TAnyType”, idt ) );

TDssID id;

 

/* Создаем объект и инициализируем его значениями по умолчанию */

dss_check( dss.objAllocate( idt, id ) );

cout << id << endl;

 

/* Создаем объект и инициализируем его сами */

TAnyType obj;

obj.any_field = any_value;

dss_check( dss.objAllocate( idt, id, &obj ) );

cout << id << endl;

 

            Изменить значение объекта, не загружая сам объект в ОП, можно при помощи метода

 

TDssDBError  objUpdate( const TDssID & id, const void * pcvValue );

 

В случае нормального завершения этого метода объект id примет значение pcvValue.

            Для более сложной работы с объектами их необходимо загружать в ОП. Для этого применяется метод

 

TDssDBError  objLoad( const TDssID & id, TDssUInt uiMode, void * & pObjPointer );

 

Загрузка объекта выполняется следующим образом:

            - проверяется возможность загрузки объекта (существование объекта, блокированность объекта, наличие уже загруженной копии объекта и т.д.). Если загрузка невозможна, то возвращается соответствующий код ошибки;

            - при помощи оператора new создается объект нужного типа в ОП. Оператор new вызывается при помощи вспомогательного кода, сгенерированного dssreg при регистрации типов. Если оператор new возвратил 0, то загрузка прекращается с кодом ошибки eDBErrorNoMemory;

            - в созданный объект загружаются значения хранимых атрибутов;

            - указатель на созданный объект возвращается в параметре pObjPointer.

Существенно то, что конструктор объекта вызывается до того, как хранимые атрибуты будут загружены. Поэтому в конструкторе по умолчанию (т.е. в конструкторе без параметров) нельзя использовать значения хранимых атрибутов:

 

struct TSample {

       dss_type( TSample );

 

       dss_attr     int    m_i;

 

       TSample( void ) : m_i( 5 ) {

             cout << m_i << endl;

       }

};

...

TDssID id;

TDssIDType idt;

 

dss_check( dss.typeFind( “TSample”, idt ) );

dss_check( dss.objAllocate( idt, id ) );

 

TSample * p;

dss_check( dss.objLoad( id, c_uiDssMode_ReadOnly, DSS_ADDRESS( p ) ) );

cout << p->m_i << endl;

dss_check( dss.objFree( id, p ) );

 

В результате на экран будет выдано:

 

5

0

 

Произойдет это потому, что при создании объекта в БД атрибуту TSample::m_i будет присвоено значение 0. При загрузке объекта сначала произойдет вызов конструктора TSample::TSample( void ). Атрибут TSample::m_i получит значение “5”, что и отобразится на экране. Затем будет загружено значение атрибута TSample::m_i из БД и на экране отобразится “0”.

            Загрузка объекта возможна в трех режимах. Первый режим - c_uiDssMode_ReadOnly. В этом режиме возможна одновременная загрузка нескольких копий объекта. Например, из разных нитей (threads). При этом значение объекта нельзя записать обратно в БД. Предполагается, что в этом режиме программист не будет изменять значения хранимых атрибутов. Если размер объекта в ОП равен размеру объекта в БД (т.е. в объекте имеются только хранимые атрибуты), то фактическая загрузка объекта осуществляется только один раз. Дальнейшие вызовы метода objLoad будут возвращать один и тот же указатель. При этом для объекта увеличивается счетчик обращений. Последующие вызовы objFree будут уменьшать значение этого счетчика. Фактическая выгрузка объекта произойдет при достижении счетчиком обращений нулевого значения. Если размер объекта в ОП больше размера объекта в БД (например, из-за наличия нехранимых атрибутов или виртуальных таблиц), то при каждом вызове objLoad в ОП создается новая копия объекта. В этом случае при выгрузке объекта в objFree необходимо передавать указатель на ту копию объекта, которую необходимо выгрузить. В противном случае метод objFree завершится с кодом eDBErrorObjCopyUnknown. Многократное создание копий объекта происходит из-за того, что невозможно определить причину, по которой размер объекта в ОП превышает размер объекта в БД. Если объект содержит расширяющие атрибуты, которые обязательно должны каким-либо образом изменяться, то необходимо обеспечить независимое редактирование расширяющих атрибутов для каждой копии объекта.

            Второй режим - c_uiDssMode_ReadWrite. Объект загружается в этом режиме для изменения, и его новое значение может быть записано в БД. В этом режиме возможна только однократная загрузка объекта (т.е. нельзя повторно загрузить объект до того, как он будет выгружен). При выгрузке объекта из ОП его новое значение не сохраняется в БД автоматически. Для сохранения значения объекта необходимо явно вызывать метод:

 

TDssDBError  objSave( const TDssID & id );

 

            Третий режим - c_uiDssMode_WriteShare. Этот режим предназначен для изменения объекта и новое значение объекта может быть записано в БД. В этом режиме возможна многократная загрузка объекта. При этом используется единственная копия объекта в ОП и счетчик обращений к ней. Вся ответственность за одновременное редактирование объекта ложится на программиста. Как и в режиме c_uiDssMode_ReadWrite при выгрузке объекта сохранение его значения в БД не происходит. Для сохранения значения в БД необходимо воспользоваться методом objSave.

            Выгрузка объекта из ОП производится методом:

 

TDssDBError  objFree( const TDssID & id, const void * pcvCopyOfObj = 0 );

 

Параметр pcvCopyOfObj должен указывать на копию объекта, подлежащую выгрузке. Данный параметр является обязательным для режима загрузки c_uiDssMode_ReadOnly, в котором объект может загружаться многократно и иметь несколько копий в ОП. Если в этом случае параметр pcvCopyOfObj не указан или указан неверно, то код возврата метода равен eDBErrorObjCopyUnknown.

            Все режимы загрузки несовместимы между собой, т.е. если объект загружен в режиме c_uiDssMode_ReadOnly, то его не удастся повторно загрузить в режимах c_uiDssMode_ReadWrite и c_uiDssMode_WriteShare:

 

struct TSample {

       dss_type( TSample );

 

       dss_attr     int    m_i;

       ...

};

...

TDssIDType idt;

TDssID id;

dss_check( dss.typeFind( “TSample”, idt ) );

dss_check( dss.objAllocate( idt, id ) );

 

/* Загружаем объект для изменения */

TSample * p, * p2;

dss_check( dss.objLoad( id, c_uiDssMode_ReadWrite, DSS_ADDRESS( p ) ) );

p->m_i = 10;

dss_check( dss.objSave( id ) );

 

/* Попробуем загрузить объект, не выгрузив предыдущую копию. Оба следующих действия приведут к ошибкам */

dss_check( dss.objLoad( id, c_uiDssMode_ReadOnly, DSS_ADDRESS( p2 ) ) );

dss_check( dss.objLoad( id, c_uiDssMode_WriteShare, DSS_ADDRESS( p2 ) ) );

 

/* Теперь выгрузим объект */

dss_check( dss.objFree( id, p ) );

 

/* Загружаем объект в режиме c_uiDssMode_WriteShare. Можно повторно загрузить объект, но будет использоваться одна копия */

dss_check( dss.objLoad( id, c_uiDssMode_WriteShare, DSS_ADDRESS( p ) ) );

dss_check( dss.objLoad( id, c_uiDssMode_WriteShare, DSS_ADDRESS( p2 ) ) );

cout << “first pointer: “ << p << “\n”

       << “second pointer: “ << p2 << endl;

p->m_i = 2;

p2->m_i = 3;

dss_check( dss.objSave( id ) );

dss_check( dss.objFree( id, p ) );

 

/* Запрос на загрузку объекта в режиме, отличном от c_uiDssMode_WriteShare, окажется неудачным, т.к. количество обращений к objFree не совпадает с количеством обращений к objLoad */

dss_check( dss.objLoad( id, c_uiDssMode_ReadWrite, DSS_ADDRESS( p ) ) );

 

dss_check( dss.objFree( id, p2 ) );

 

/* Загружаем объект только для чтения */

dss_check( dss.objLoad( id, c_uiDssMode_ReadOnly, DSS_ADDRESS( p ) ) );

dss_check( dss.objLoad( id, c_uiDssMode_ReadOnly, DSS_ADDRESS( p2 ) ) );

cout << “first pointer: “ << p << “\n”

       << “second pointer: “ << p2 << endl;

/* В результате следующего действия... */

cout << p->m_i << endl;

/* на экран будет отображено значение “3”, т.к. в режиме WriteShare производилось редактирование одной и той же копии объекта */

 

dss_check( dss.objFree( id, p ) );

dss_check( dss.objFree( id, p2 ) );

 

Примечание. При выгрузке объектов, загруженных в режимах c_uiDssMode_ReadOnly и c_uiDssMode_WriteShare, указывать копию объекта не обязательно, т.к. в ОП присутствует только одна копия объекта. Однако, такое описание поможет избежать досадных недоразумений в случаях, когда режим загрузки будет изменен, а обращение к objFree останется неизменным.

            Метод objLoad хорошо подходит только к загрузке объектов тех типов, в которых применяется одиночное наследование, либо наследование не применяется вовсе. При использовании множественного наследования применение objLoad становится опасным, поскольку возвращается указатель на начало всего объекта, а не на начало нужной составляющей.

 

/* Пример автоматического преобразования указателей в C++ */

class  TTextOnImage : public TImageItem {

       ...

};

class  TDataReceiver {

       ...

};

class  TDataShower : public TTextOnImage,

       public TDataReceiver {

       ...

};

 

void   SetupTextOnImage( TTextOnImage * po ) {

       po->... = ...;

}

void   SetupDataReceiver( TDataReceiver * po ) {

       po->... = ...;

}

void   SetupDataShower( TDataShower * po ) {

       po->... = ...;

       SetupTextOnImage( po );

       SetupDataReceiver( po );

}

 

В языке C++ при вызове функций Setup* компилятор автоматически преобразует указатель, и в функцию будет передан указатель на соответствующую составляющую объекта. При использовании Dss функции Setup* могут иметь следующий вид[17]:

 

void   SetupTextOnImage( TDssKernelInteface & dss,

       const TDssID & id ) {

       TTextOnImage * po;

       dss_check( dss.objLoad( id, c_uiDssMode_WriteShare,

             DSS_ADDRESS( po ) ) );

       po->... = ...;

       dss_check( dss.objSave( id ) );

       dss_check( dss.objFree( id, po ) );

}

void   SetupDataReceiver( TDssKernelInterface & dss,

       const TDssID & id ) {

       TDataReceiver * po;

       dss_check( dss.objLoad( id, c_uiDssMode_WriteShare,

             DSS_ADDRESS( po ) ) );

       po->... = ...;

       dss_check( dss.objSave( id ) );

       dss_check( dss.objFree( id, po ) );

}

void   SetupDataShower( TDssKernelInterface & dss,

       const TDssID & id ) {

       TDataShower * po;

       dss_check( dss.objLoad( id, c_uiDssMode_WriteShare,

             DSS_ADDRESS( po ) ) );

       po->... = ...;

 

       SetupTextOnImage( dss, id );

       SetupDataReceiver( dss, id );

 

       dss_check( dss.objSave( id ) );

       dss_check( dss.objFree( id, po ) );

}

 

Однако, в этом случае функция SetupDataReceiver будет работать с объектом TDataShower как с объектом TDataReceiver, что приведет к порче составляющей TDataShower::TTextOnImage. Произойдет это из-за того, что не было дано указаний насчет преобразования указателя при загрузке объекта. Дать такие указания можно при помощи метода:

 

TDssDBError  objLoadAs( const TDssID & id, TDssIDType idt, TDssUInt uiMode, void * & pvObjPointer );

 

Данный метод полностью аналогичен методу objLoad, но возвращаемый указатель приводится к типу idt. Приведенный выше пример необходимо переписать следующим образом:

 

void   SetupTextOnImage( TDssKernelInteface & dss,

       const TDssID & id ) {

       TDssIDType idt;

       TTextOnImage * po;

       dss_check( dss.typeFind( “TTextOnImage”, idt ) );

       dss_check( dss.objLoadAs( id, idt, c_uiDssMode_WriteShare,

             DSS_ADDRESS( po ) ) );

       po->... = ...;

       dss_check( dss.objSave( id ) );

       dss_check( dss.objFree( id, po ) );

}

void   SetupDataReceiver( TDssKernelInterface & dss,

       const TDssID & id ) {

       TDssIDType idt;

       TDataReceiver * po;

       dss_check( dss.typeFind( “TDataReceiver”, idt ) );

       dss_check( dss.objLoadAs( id, idt, c_uiDssMode_WriteShare,

             DSS_ADDRESS( po ) ) );

       po->... = ...;

       dss_check( dss.objSave( id ) );

       dss_check( dss.objFree( id, po ) );

}

void   SetupDataShower( TDssKernelInterface & dss,

       const TDssID & id ) {

       TDssIDType idt;

       TDataShower * po;

       dss_check( dss.typeFind( “TDataShower”, idt ) );

       dss_check( dss.objLoadAs( id, idt, c_uiDssMode_WriteShare,

             DSS_ADDRESS( po ) ) );

       po->... = ...;

 

       SetupTextOnImage( dss, id );

       SetupDataReceiver( dss, id );

 

       dss_check( dss.objSave( id ) );

       dss_check( dss.objFree( id, po ) );

}

 

            Выгрузка объектов, загруженных при помощи objLoadAs, происходит обычным образом.

            Для преобразования указателя на уже загруженный объект к требуемому типу предназначен метод:

 

TDssDBError  objTypeCast( const TDssID & id, TDssIDType idt, void * & pvResult, const void * pcvCopyOfObject );

 

Параметр pcvCopyOfObject предназначен для указания копии объекта, если объект загружен в режиме c_uiDssMode_ReadOnly.

            Методы objLoadAs и objTypeCast имеют отличительную особенность: они завершаются успешно, только если указанный тип является типом объекта, либо доступным базовым типом объекта, и если преобразование типов однозначно.

 

class  A {

       dss_type( A );

       ...

};

class  B : public A {

       dss_type( B );

       ...

};

class  C : public A {

       dss_type( C );

       ...

};

class  D {

       dss_type( D );

       ...

};

class  E : public B, public C, protected D {

       dss_type( E );

       ...

};

class  X {

       dss_type( X );

       ...

};

...

TDssIDType idtE;

TDssID id;

dss_check( dss.typeFind( “E”, idtE ) );

dss_check( dss.objAllocate( idtE, id ) );

 

TDssIDType idtA, idtD, idtX;

dss_check( dss.typeFind( “A”, idtA ) );

dss_check( dss.typeFind( “D”, idtD ) );

dss_check( dss.typeFind( “X”, idtX ) );

 

void * p;

 

/* Это действие завершится нормально */

dss_check( dss.objLoadAs( id, idtE, c_uiDssMode_ReadOnly, p ) );

 

/* Ошибка 1 */

dss_check( dss.objLoadAs( id, idtA, c_uiDssMode_ReadOnly, p ) );

 

/* Ошибка 2 */

dss_check( dss.objLoadAs( id, idtD, c_uiDssMode_ReadOnly, p ) );

 

/* Ошибка 3 */

dss_check( dss.objLoadAs( id, idtX, c_uiDssMode_ReadOnly, p ) );

 

Первая ошибка вызвана противоречивостью преобразования типа (E::B::A или E::C::A?). Вторая ошибка вызвана тем, что D является защищенным базовым типом для E, и преобразование к нему не допустимо. Третья ошибка вызвана тем, что тип X не имеет к типу E никакого отношения[18].

Выделение объектов в БД

            Dss спроектирована для задач, объектные модели которых являются сильно связанными, а количество запросов на поиск информации незначительно. Обычно взаимосвязи между объектами представляются деревьями или сетями с одной или несколькими вершинами. Из этих вершин, посредством взаимных ссылок, окрывается доступ ко всем остальным объектам, либо к значительному их числу. Например, от единственного объекта “Завод” открывается доступ к объектам типа “Цех”, от тех - к объектам типа “Производственный участок”, от тех - к объектам типа “Станок” и т.д. В таких задачах необходимо разыскать объекты-вершины. Dss предоставляет для этого два основных способа.

Итерация по типу

            Просмотр всех объектов некоторого типа называется итерацией по типу. В БД может быть зарегистрирован тип “Завод” и существовать единственный объект этого типа. Итерация позволит найти идентификатор этого объекта. Для выполнения итерации предназначен класс TDssIteratorInterface и методы typeStartIteration и typeStopIteration класса TDssKernelInterface. Для проведения итерации в программе объявляется переменная типа TDssIteratorInterface, и ссылка на нее передается в методы typeStartIteration и typeStopIteration. После того, как итерация начата методом TDssKernelInterface::typeStartIteration, можно обращаться к методу TDssIteratorInterface::objNext для определения идентификаторов объектов. После того, как все идентификаторы типа исчерпаны, метод objNext возвратит eDBErrorNotFound. Это означает, что объектов данного типа больше нет и итерацию можно завершить. Завершение итерации осуществляется методом TDssKernelInterface::typeStopIteration. Поиск единственного объекта типа “Завод” может выглядеть так:

 

TDssDBError  FindStartObject( TDssKernelInterface & dss, TDssID & id ) {

       TDssIDType idt;

       dss_check( dss.typeFind( “TFactory”, idt ) );

 

       TDssIteratorInterface iterator;

       dss_check( dss.typeStartIteration( idt, c_uiDssMode_ReadOnly,

             iterator ) );

 

       TDssDBError dbe = iterator.objNext( id );

       dss_check( dss.typeStopIteration( iterator ) );

       return dbe;

}

 

            Итерация накладывает ограничения на использование БД в момент проведения итерации. Так, при проведении итерации нельзя создавать объекты. Если итерация проводится в режиме c_uiDssMode_ReadOnly, то нельзя также удалять объекты и загружать их в режимах, отличных от c_uiDssMode_ReadOnly. Нельзя начать итерацию в режиме c_uiDssMode_ReadOnly, если имеются объекты этого типа, загруженные в режимах c_uiDssMode_ReadWrite или c_uiDssMode_WriteShare. Можно одновременно проводить несколько итераций в режиме c_uiDssMode_ReadOnly. Нельзя начать итерацию в режиме c_uiDssMode_ReadWrite, если существуют другие итерации (в любом режиме) по этому типу. Нельзя начать итерацию в режиме c_uiDssMode_ReadOnly, если по типу проводится итерация в режиме c_uiDssMode_ReadWrite или c_uiDssMode_WriteShare. Можно начать любое количество итераций в режиме c_uiDssMode_WriteShare при условии, что по типу не проводятся итерации в других режимах. Метод:

 

TDssDBError  typeGetIterationParams( TDssIDType idtype, TDssUInt & uiCount, TDssUInt * puiMode );

 

позволяет определить количество проводимых в данный момент итераций по типу idtype и их режим. Данный метод возвращает общее количество итераций, проводимых всеми нитями в данный момент времени.

            Итерации могут применяться и для других целей. Например, при необходимости переинициализации объектов какого-то типа. Допустим, что существует тип TCounter, описывающий счетчик тепла. Каждый счетчик определяется своим номером. Первоначально все номера являлись нормальными целыми числами. Со временем понадобилось присваивать счетчикам составные номера вида “1-1”, “2-3-5” и т.д. В базе данных существует несколько тысяч объектов типа TCounter, на них ссылается множество других объектов и т.д. Необходимо преобразовать целочисленный атрибут в строку и соответствующим образом перестроить объекты БД. Для этого сначала модифицируется структура TCounter следующим образом:

 

// DssDDL

struct TCounter {

       int    m_iCounterID;

       char   m_szCounterID[ 20 ];

       ...

};

 

Затем создается следующий код:

 

void   ModifyTCounter( TDssKernelInterface & dss ) {

       TDssIDType idt;

       dss_check( dss.typeFind( “TCounter”, idt ) );

 

       TDssIteratorInterface iterator;

       dss_check( dss.typeStartIteration( idt, c_uiDssMode_ReadWrite,

             iterator ) );

 

       TDssID id;

       while( eDBErrorOk == iterator.objNext( id ) ) {

             TCounter * p;

             dss_check( dss.objLoadAs( id, idt, c_uiDssMode_ReadWrite,

                    DSS_ADDRESS( p ) ) );

             sprintf( p->m_szCounterID, “%d”, p->m_iCounterID );

             dss_check( dss.objSave( id ) );

             dss_check( dss.objFree( id, p ) );

       }

 

       dss_check( dss.typeStopIteration( iterator ) );

}

 

int    main( void ) {

       TDssKernelInterface dss;

       dss_check( dss.obOpen( “factory”, c_uiDssMode_ReadWrite,

             c_uiDssRpLevel1, dss_type_info, dss_type_methods ) );

 

       ModifyTCounter( dss );

 

       return 0;

}

 

            Основным недостатком итерации при поиске объектов является ее зависимость от количества всех идентификаторов в БД. Так, при наличии нескольких десятков тысяч идентификаторов время проведения итерации становится ощутимым.

Индивидуальные имена объектов

            Любому объекту БД можно присвоить уникальное имя, заданное ASCIIZ-строкой. При удалении объекта его имя будет удалено автоматически. При открытии БД все имена объектов загружаются в ОП и сортируются. Поэтому поиск объекта по имени осуществляется очень быстро.

            Данный механизм предназначен специально для быстрого нахождения объектов-вершин. Так, единственному объекту типа “Завод” можно присвоить имя “завод”, а затем найти идентификатор объекта по этому имени:

 

void   CreateStartObject( TDssKernelInterface & dss ) {

       TDssIDType idt;

       dss_check( dss.typeFind( “TFactory”, idt ) );

 

       TDssID id;

       dss_check( dss.objAllocate( idt, id ) );

       /* Создаем имя объекта */

       dss_check( dss.nameCreate( “factory”, id ) );

       ...

}

...

TDssDBError  FindStartObject( TDssKernelInterface & dss, TDssID & id ) {

       return dss.nameFind( “factory”, id );

}

 

            Имена можно давать не только объектам-вершинам. Например, среди множества изображений, описанных в БД (объектов типа TImage), всегда присутствует изображение, используемое в качестве заставки при загрузке программного комплекса. Чтобы быстро найти это изображение, соответствующему объекту БД можно присвоить имя “image_on_title”.

            Создание имени объекта осуществляется методом:

 

TDssDBError  nameCreate( const char * pcszName, const TDssID & id );

 

Объект, идентификатор которого передан в параметре id, должен существовать. Имя передается в параметре pcszName. Имя - это произвольный набор символов произвольной длины, заканчивающийся 0-символом. Ограничений на используемое множество символов нет. Однако, рекомендуется ограничиться стандартным набором символов ASCII (от 32 до 127) без использования спецсимволов, знаков препинания и пробелов. Имя должно быть уникальным. Объекту может быть назначено любое количество имен, поскольку объект в результате множественного наследования может выполнять несколько ролей.

            Поиск объекта по имени осуществляется методом:

 

TDssDBError  nameFind( const char * pcszName, TDssID & id );

 

Если имя определено, то метод возвращает eDBErrorOk, а идентификатор объекта помещается в id. Если имя не определено, то возвращается eDBErrorNotFound.

            При удалении объекта все имена, назначенные объекту, удаляются автоматически. Можно удалить любое имя, не удаляя сам объект. Осуществляется это методом:

 

TDssDBError  nameRemove( const char * pcszName );

 

Удаление имени может понадобиться для того, чтобы присвоить это имя другому объекту:

 

void   ChangeImageOnTitle( TDssKernelInterface & dss,

       const TDssID & idNew ) {

       dss.nameRemove( “image_on_title” );

       dss_check( dss.nameCreate( “image_on_title”, idNew ) );

}

 

            Dss не предоставляет специальных средств для определения всех имен какого-либо объекта. Однако, это можно осуществить при помощи методов:

 

TDssDBError  nameGetCount( TDssUInt & count );

TDssDBError  nameGetParams( TDssUInt index, char * pszBuf, TDssUInt uiBufSize, TDssUInt * puiNameLength, TDssID * pid );

 

Первый метод возвращает общее количество имен, определенных на данный момент. Второй метод позволяет получить параметры имени по его порядковому номеру в общем списке имен. Имена нумеруются с нуля. Значения возвращаются только для ненулевых аргументов метода. Перечислить все имена какого-либо объекта можно при помощи следующей процедуры:

 

void   EnumObjNames( TDssKernelInterface & dss, const TDssID & id ) {

       TDssUInt count;

       dss_check( dss.nameGetCount( count ) );

       for( TDssUInt index = 0; index < count; index++ ) {

             TDssUInt uiNameLength;

             TDssID idObj;

             dss_check( dss.nameGetParams( index, 0, 0,

                    &uiNameLength, &idObj ) );

             if( idObj == id ) {

                    char * pName = new char[ uiNameLength + 1 ];

                    dss_check( dss.nameGetParams( index, pName,

                           uiNameLength, 0, 0 ) );

                    cout << pName << endl;

                    delete pName;

             }

       }

}

 

Удаление объектов из БД

            Если надобность в каком-либо объекте отпадает, то объект должен быть удален из БД. Например, собранный автомобиль сходит с конвейера, и из БД конвейера за ненадобностью удаляется информация о собранном автомобиле. Удаление объекта производится методом:

 

TDssDBError  objDeallocate( const TDssID & id );

 

Метод должен вызываться для незагруженных объектов. В противном случае, код возврата метода равен eDBErrorLoaded. В случае успешного завершения метода объект перестает существовать. Его идентификатор становится недействительным. Если на этот объект имеются ссылки, то они становятся неверными (Dss их не корректирует). Поэтому перед удалением объекта необходимо позаботиться о том, чтобы не осталось ссылок на объект.

Работа с ссылками в Dss

            Отличительной особенностью Dss является то, что программист должен заботиться о двух способах представления ссылок между объектами[19]. В БД ссылки поддерживаются при помощи хранимых атрибутов-идентификаторов. В ОП ссылки поддерживаются при помощи расширяющих атрибутов-указателей. На практике эта особенность выражается в следующем:

            - при проектировании хранимых типов необходимо описывать два атрибута на каждую ссылку (см. “Проектирование хранимых типов”);

            - при операциях загрузки объектов необходимо загружать объекты, на которые имеются ссылки в атрибутах-идентификаторах и инициализировать атрибуты-указатели;

            - при операциях выгрузки объектов необходимо выгружать загруженные по ссылкам объекты[20].

            Кроме этого, атрибуты-идентификаторы необходимо инициализировать при создании объекта. По умолчанию атрибуты-идентификаторы инициализируются значением c_idDssInvalid. При удалении объекта необходимо заботиться об удалении всех подчиненных объектов по имеющимся ссылкам.

            Рассмотрим пример с объектом-конвейером. Пусть на конвейере установлены три станка (входной, обработчик, выходной). В БД объект-конвейер ссылается на три объекта-станка. При загрузке объекта-конвейера в ОП необходимо загрузить объекты-станки. При выгрузке - выгрузить объекты-станки и объект-конвейер. При удалении объекта-конвейера из БД необходимо удалить объекты-станки. На C++ эту задачу можно решить так:

 

class  TEngineEntry {

       dss_type( TEngineEntry );

       ...

};

class  TEngineProcess {

       dss_type( TEngineProcess );

       ...

};

class  TEngineExit {

       dss_type( TEngineExit );

       ...

};

class  TConveyor {

       dss_type( TConveyor );

       protected :

             /* Ссылки на установленные на конвейере станки */

             dss_attr TDssID m_idEntry;

             TEngineEntry * m_pEntry;

             dss_attr TDssID m_idProcess;

             TEngineProcess * m_pProcess;

             dss_attr TDssID m_idExit;

             TEngineExit * m_pExit;

             ...

       public :

             ...

              /* Средства для создания, загрузки, выгрузки, удаления объекта-конвейера */

             static TDssID create( TDssKernelInterface & dss );

             static TConveyor * load( TDssKernelInteface & dss,

                    const TDssID & id );

             static void unload( TDssKernelInterface & dss,

                    const TDssID & id,

                    TConveyor * pConveyor );

             static void destroy( TDssKernelInterface & dss,

                    const TDssID & id );

};

 

            При создании объекта-конвейера создаются объекты-станки:

 

TDssID TConveyor::create( TDssKernelInterface & dss ) {

       TConveyor conv;

 

       /* Создаем объекты-станки */

       TDssIDType idt;

       dss_check( dss.typeFind( “TEngineEntry”, idt ) );

       dss_check( dss.objAllocate( idt, conv.m_idEntry ) );

 

       dss_check( dss.typeFind( “TEngineProcess”, idt ) );

       dss_check( dss.objAllocate( idt, conv.m_idProcess ) );

 

       dss_check( dss.typeFind( “TEngineExit”, idt ) );

       dss_check( dss.objAllocate( idt, conv.m_idExit ) );

 

       /* Создаем объект-конвейер */

       TDssID id;

       dss_check( dss.typeFind( “TConveyor”, idt ) );

       dss_check( dss.objAllocate( idt, id, &conv ) );

 

       return id;

}

 

            При загрузке объекта-конвейера загружаем объекты-станки:

 

TConveyor *  TConveyor::load( TDssKernelInterface & dss,

       const TDssID & id ) {

       TConveyor * pConv;

       dss_check( dss.objLoad( id, c_uiDssMode_ReadWrite,

             DSS_ADDRESS( pConv ) ) );

 

       /* Загружаем объекты-станки */

       dss_check( dss.objLoad( pConv->m_idEntry,

             c_uiDssMode_ReadWrite,

             DSS_ADDRESS( pConv->m_pEntry ) ) );

       dss_check( dss.objLoad( pConv->m_idProcess,

             c_uiDssMode_ReadWrite,

             DSS_ADDRESS( pConv->m_pProcess ) ) );

       dss_check( dss.objLoad( pConv->m_idExit,

             c_uiDssMode_ReadWrite,

             DSS_ADDRESS( pConv->m_pExit ) ) );

 

       return pConv;

}

 

            При выгрузке объекта-конвейера сначала выгружаем объекты-станки:

 

void   TConveyor::unload( TDssKernelInterface & dss,

       const TDssID & id, TConveyor * pConv ) {

       /* Выгружаем объекты-станки */

       dss_check( dss.objFree( pConv->m_idEntry,

             pConv->m_pEntry ) );

       dss_check( dss.objFree( pConv->m_idProcess,

             pConv->m_pProcess ) );

       dss_check( dss.objFree( pConv->m_idExit,

             pConv->m_pExit ) );

 

       /* Выгружаем объект-конвейер */

       dss_check( dss.objFree( id, pConv ) );

}

 

            При удалении объекта-конвейера сначала удаляются объекты-станки:

 

void   TConveyor::destroy( TDssKernelInterface & dss,

       const TDssID & id ) {

       /* Загрузим объект-конвейер для получения доступа к ссылкам на объекты-станки */

       TConveyor * pConv;

       dss_check( dss.objLoad( id, c_uiDssMode_ReadWrite,

             DSS_ADDRESS( pConv ) ) );

 

       /* Удаляем объекты-станки */

       dss_check( dss.objDeallocate( pConv->m_idEntry ) );

       dss_check( dss.objDeallocate( pConv->m_idProcess ) );

       dss_check( dss.objDeallocate( pConv->m_idExit ) );

 

       /* Выгружаем объект конвейер и удаляем его */

       dss_check( dss.objFree( id, pConv ) );

       dss_check( dss.objDeallocate( id ) );

}

Методы

            После загрузки объекта из БД работа с ним происходит как с обычным C++ объектом. Это касается как обращения к атрибутам (хранимым и расширяющим), так и к методам (статическим[21] и нестатическим):

 

class  TBase {

       dss_type( TBase );

       public :

             virtual void show( void ) = 0;

};

class  TDerived1 : public TBase {

       dss_type( TDerived1 );

       public :

             void   show( void ) {

                    cout << “TDerived1” << endl;

             }

             ...

};

class  TDerived2 : public TBase {

       dss_type( TDerived2 );

       public :

             void   show( void ) {

                    cout << “TDerived2” << endl;

             }

             ...

};

...

TDssIDType idtBase;

TDssIDType idtDerived1;

TDssIDType idtDerived2;

dss_check( dss.typeFind( “TBase”, idtBase ) );

dss_check( dss.typeFind( “TDerived1”, idtDerived1 ) );

dss_check( dss.typeFind( “TDerived2”, idtDerived2 ) );

 

TDssID idDerived1;

TDssID idDerived2;

dss_check( dss.objAllocate( idtDerived1, idDerived1 ) );

dss_check( dss.objAllocate( idtDerived2, idDerived2 ) );

 

TDerived1 * pDerived1;

TDerived2 * pDerived2;

dss_check( dss.objLoadAs( idDerived1, idtDerived1,

       c_uiDssMode_ReadOnly, DSS_ADDRESS( pDerived1 ) ) );

dss_check( dss.objLoadAs( idDerived2, idtDerived2,

       c_uiDssMode_ReadOnly, DSS_ADDRESS( pDerived2 ) ) );

 

pDerived1->show();

pDerived2->show();

 

dss_check( dss.objFree( idDerived1, pDerived1 ) );

dss_check( dss.objFree( idDerived2, pDerived2 ) );

 

Результатом работы этого примера будет:

 

TDerived1

TDerived2

 

            Специфика хранения объектов в БД требует дополнительных механизмов для работы с методами. Предположим, что каждый объект БД должен обладать методом keyString, возвращающим ключевую строку объекта (название, порядковый номер и т.д.). В обычном C++ для этого необходимо создать общий корень иерархии типов (например, класс TObject), в котором описан виртуальный метод keyString. Все остальные типы производятся от общего корня и переопределяют метод keyString. Такой подход вполне применим, если объектная модель не использует множественного наследования. В случае множественного наследования наличие общего корня может создать ряд дополнительных трудностей.

            Еще одним примером может служить программа-браузер БД, позволяющая в интерактивном режиме заполнять БД. В такой программе пользователь видит сначала общий список типов. После выбора элемента в этом списке открывается окно с объектами выбранного типа. Причем объекты отображаются в своем естественном виде (т.е. объекты типа TImage в виде изображений, объекты типа TDocument в виде документов и т.д.). Для реализации такого подхода на C++ потребуется применять приблизительно такой трюк:

 

/* В каждом типе заводится статический метод showObjects */

class  TImage {

       dss_type( TImage );

       public :

             static void  showObjects( TViewer * pWindow );

       ...

};

class  TDocument {

       dss_type( TDocument );

       public :

             static void  showObjects( TViewer * pWindow );

       ...

};

...

/* Строится таблица методов showObjects для всех типов */

typedef void (*TPFnShowObjects)( TViewer * pWindow );

struct TTable {

       const char * m_pcszType;

       TPFnShowObjects m_pfnShowObjects;

};

TTable g_tableShowObjects[] = {

       { “TImage”, (TPFnShowObjects)TImage::showObjects },

       { “TDocument”, (TPFnShowObjects)TDocument::showObjects },

       ...

};

 

Программа-браузер при выборе типа должна просмотреть таблицу g_tableShowObjects и найти там соответствующий элемент. Основным недостатком такого подхода является то, что программист сам должен исправлять g_tableShowObjects при изменении объектной модели, и то, что если подобных методов понадобится несколько, то и таблиц придется делать несколько.

            Dss берет на себя задачу организации и поддержки таких таблиц, предоставляя механизм т.н. именованных методов. Именованный метод в Dss - это уникальное в рамках типа имя, которому после открытия БД ставится в соответствие адрес функции или статического метода класса. Программист, посредством специальных методов Dss, может получить этот адрес и использовать его в соответствии со своими представлениями. Так, при проектировании БД можно обговорить, что для каждого типа БД будет существовать процедура формата:

 

void   ShowObjects( TViewer * pWindow );

 

которая занимается отображением объектов этого типа в отдельном окне программы-броузера. После этого для каждого типа такие функции реализуются в виде функции или статического метода класса:

 

class  TImage {

       dss_type( TImage );

       public :

             dss_meth void showObjects( TViewer * pWindow );

       ...

};

void   TImage::showObjects( TViewer * pWindow ) {

       ...

}

 

class  TDocument {

       dss_type( TDocument );

       friend void  ShowObjects_TDocument( TViewer * pWindow );

       ...

}

void   ShowObjects_TDocument( TViewer * pWindow ) {

       ...

}

 

После этого, при регистрации типов, эти процедуры указываются для именованных методов “showObjects”:

 

struct TImage {

       method showObjects  TImage::showObjects;

       ...

};

struct TDocument {

       method showObjects  ShowObjects_TDocument;

       ...

};

...

 

Программа dssreg для каждого типа строит таблицу, аналогичную g_tableShowObjects, в которую помещает имя процедуры, реализующей метод (в данном случае TImage::showObjects и ShowObjects_TDocument).

            Определить адрес процедуры в процессе работы с БД можно при помощи методов:

 

TDssDBError  methodFindByType( TDssIDType idt, const char * pcszMethod, TDssPFnMethod & pfn );

TDSsDBError  methodFindByObject( const TDssID & id, const char * pcszMethod, TDssPFnMethod & pfn );

 

Первый метод ищет указанный именованный метод в типе idt. Второй - в типе, к которому принадлежит объект id. Если именованный метод не найден, то возвращается код ошибки eDBErrorNotFound. Если метод присутствует в схеме данных БД, но отсутствует в схеме данных программы, то возвращается код ошибки eDBErrorMethodNotImplemented. Использовать именованные методы можно так:

 

// C++

class  TImage {

       dss_type( TImage );

       public :

             dss_meth void showObjects( TViewer * pWindow );

             dss_meth void keyString( TDssKernelInterface & dss,

                    const TDssID & id, char * szString,

                    size_t stringCapacity );

       ...

};

class  TDocument {

       dss_type( TDocument );

       public :

             dss_meth void showObjects( TViewer * pWindow );

             dss_meth void keyString( TDssKernelInterface & dss,

                    const TDssID & id, char * szString,

                    size_t stringCapacity );

       ...

};

 

// DssDDL

struct TImage {

       method showObjects  TImage::showObjects;

       method keyString    TImage::keyString;

       ...

};

struct TDocument {

       method showObjects  TDocument::showObjects;

       method keyString    TDocument::keyString;

       ...

};

 

// C++

typedef void (*TPFnShowObjects)( TViewer * pWindow );

typedef void (*TPFnKeyString)( TDssKernelInterface & dss, const TDssID & id, char * szString, size_t stringCapacity );

 

void   StartShowObjectsOfType( TDssKernelInterface & dss,

       TDssIDType idt, TViewer * pWindow ) {

       TPFnShowObjects pfn;

       if( eDBErrorOk == dss.methodFindByType( idt, “showObjects”,

             DSS_METHOD_CAST( pfn ) ) )

             (*pfn)( pWindow );

}

 

void   CreateKeyString( TDssKernelInterface & dss, const TDssID & id,

       char * szString, size_t stringCapacity ) {

       TPFnKeyString pfn;

       if( eDBErrorOk == dss.methodFindByObject( id,

             “keyString”, DSS_METHOD_CAST( pfn ) ) )

             (*pfn)( dss, id, szString, stringCapacity );

       else

             strncpy( szString, “no description”, stringCapacity );

}

 

            Для перечисления всех[22] именованных методов какого-либо типа можно воспользоваться следующими методами:

 

TDssDBError  methodGetCount( TDssIDType idt, TDssUInt & count );

TDssDBError  methodGetParams( TDssIDType idt, TDssUInt index, char * pszBuf, TDssUInt uiBufSize, TDssUInt * puiNameLength, TDssPFnMethod * pfn );

 

Первый метод возвращает общее количество методов для типа idt. Второй метод позволяет получить параметры именованного метода с порядковым номером index в типе idt. Именованные методы нумеруются с нуля. Значения возвращаются только для ненулевых элементов. Если метод присутствует в схеме данных БД, но отсутствует в схеме данных программы, то в параметре pfn возвращается 0.

 

void   ShowTypeMethods( TDssKernelInterface & dss, TDssIDType idt ) {

       TDssUInt count;

       dss_check( dss.methodGetCount( idt, count ) );

       for( TDssUInt index = 0; index < count; index++ ) {

             TDssUInt uiNameLength;

             dss_check( dss.methodGetParams( idt, index, 0, 0,

                    &uiNameLength, 0 ) );

             char * pName = new char[ uiNameLength + 1 ];

             dss_check( dss.methodGetParams( idt, index, pName,

                    uiNameLength, 0, 0 ) );

             cout << pName << endl;

             delete pName;

       }

}

 

            Методы methodFindByType и methodFindByObject осуществляют поиск именованного метода сначала в самом типе, затем в доступных базовых типах. При использовании множественного наследования именованный метод может быть найден в нескольких базовых типах одновременно:

 

// DssDDL

struct A {

       ...

       method f A::f;

};

 

struct B : public A {

       ...

};

 

struct C : public A {

       ...

};

 

struct D : public B, public C {

       ...

};

 

Если проводить поиск метода f в типе D, то будут найдены два вхождения метода f в иерархию наследования: D::B::A::f и D::C::A::f. В этом случае возникнет ошибка eDBErrorAmbiguous, т.к. в общем случае нельзя выбрать метод из двух равнозначных вариантов. То, что в данном случае наследуется один и тот же метод из одного и того же класса, не решает проблемы, т.к. неизвестно, к какой составляющей класса D (D::B::A или D::C::A) должны относиться действия метода. Если тип A входит в иерархию наследования как виртуальный базовый тип:

 

// DssDDL

struct A {

       ...

       method f A::f;

};

 

struct B : public virtual A {

       ...

};

 

struct C : public virtual A {

       ...

};

 

struct D : public B, public C {

       ...

};

 

то ошибки не возникнет, т.к. существует только одна виртуальная составляющая наследования, и именно к ней будут относиться действия метода f.

Транзакции

            Транзакция в Dss - это последовательность действий, в течение которой БД находится в неверном состоянии. В Dss различаются два вида транзакций: системные и пользовательские. Системная транзакция начинается каждый раз при внесении изменений в БД на системном уровне и заканчивается после завершения изменений. Во время системной транзакции информация о вносимых изменениях помещается в т.н. восстановочный файл. Такая информация называется восстановочной информацией. Если во время системной транзакции произошел сбой, то пользуясь восстановочной информацией можно вернуть БД в состояние, предшествовавшее системной транзакции. Если системная транзакция завершается нормально, то восстановочная информация удаляется.

            Запись восстановочной информации занимает дополнительное время, что снижает скорость работы с БД. Для предоставления программисту выбора между скоростью работы и обеспечением более полной целостности данных Dss предоставляет два уровня детализации восстановочной информации. Первый уровень - c_uiDssRpLevel1. Данный уровень детализации подразумевает сохранение информации, необходимой для целостности только системной части БД, но не пользовательских данных. В этом случае временные затраты на системные транзакции незначительны. Второй уровень - c_uiDssRpLevel2. Данный уровень, в дополнение к уровню c_uiDssRpLevel1, во время системной транзакции сохраняет и старые значения перезаписываемых объектов. Т.е. при каждом обращении к objSave сначала извлекается старое значение объекта из БД и помещается в восстановочный файл. Только затем новое значение объекта записывается в БД. Очевидно, что скорость работы с БД начинает зависить от размеров перезаписываемых объектов. Но такой уровень детализации позволяет контролировать целостность пользовательских данных. Уровень детализации указывается при открытии БД в параметре uiRpLevel в методах dbOpen, dbSwitchMode. При открытии БД в режиме c_uiDssMode_ReadOnly данный параметр игнорируется, поскольку в этом режиме изменение БД невозможно; следовательно, сохранение восстановочной информации не нужно.

            При открытии БД с уровнем восстановочной информации c_uiDssRpLevel2 можно применять пользовательские транзакции. Пользовательская транзакция - это последовательность высокоуровневых действий над БД, в течение которой восстановочная информация по завершению системных транзакций не удаляется, а накапливается. Такое накопление восстановочной информации позволяет организовать откат транзакций. Можно начать транзакцию, затем создать какое угодно количество объектов в БД, изменить их значения, удалить часть из них, а затем выполнить откат транзакции и вернуть БД в исходное состояние.

            Транзакции могут быть вложенными. В этом случае откат самой первой начатой транзакции отменяет все действия, подтвержденные при завершении вложенных транзакций.

            При откате транзакции завершаются все начатые итерации и из ОП выгружаются все объекты, загруженные во время транзакции. В этом случае все указатели, ссылавшиеся на копию объекта в ОП становятся неверными.

            Для работы с транзакциями предназначены методы:

 

TDssDBError  transactionBegin( void );

TDssDBError  transactionEnd( void );

TDssDBError  transactionUndo( void );

TDssDBError  transactionTerminate( void );

 

Метод transactionBegin начинает транзакцию. Метод transactionEnd подтверждает транзакцию. Метод transactionUndo выполняет откат транзакции. Метод transactionTerminate закрывает все файлы БД, очищает все ресурсы класса TDssKernelInterface и переводит объект TDssKernelInterface в особое состояние, делая невозможным дальнейшую работу с БД. Данный метод предназначен для исключительных ситуаций, в которых нет времени ни на подтверждение, ни на откат транзакции. Например, при экстренном завершении программы при получении сигнала от источника бесперебойного питания о падении напряжения.

            Если восстановочная информация по каким-то причинам оказалась неудаленной (сбой при незавершенной транзакции, вызов метода transactionTerminate, закрытие базы данных при незавершенных транзакциях), то последующие попытки открытия БД будут завершаться с кодом ошибки eDBErrorDamagePossible. В этом случае для восстановления БД необходимо воспользоваться утилитой dssrpr.

            В качестве примера применения транзакций можно предположить диалоговое окно, в котором пользователь выполняет действия над объектами БД (создает, изменяет и удаляет). В этом окне для завершения работы предназначены две кнопки: “Ok” (завершить работу и подтвердить все изменения) и “Cancel” (завершить работу и отменить все изменения). При создании такого окна можно начать транзакцию, при нажатии на кнопку “Ok” - подтвердить все действия (метод transactionEnd), а при нажатии на кнопку “Cancel” - отменить все действия (метод transactionUndo).

Атрибуты

            Dss v.4.20 не предоставляет средств, аналогичных встроенным процедурам просмотра и редактирования (браузеров) реляционных БД. Объясняется это прежде всего тем, что создаваемые объекты уже должны находиться в некотором состоянии. В объектно-ориентированных языках для этих целей служат специальные методы-конструкторы, которые переводят объект в нужное состояние сразу же после создания. Помимо присвоения начальных значений атрибутам, конструкторы могут выполнять и другую работу: создавать подчиненные объекты, устанавливать связи между объектами и т.д. Т.е. процесс создания объекта зачастую бывает непростым. Аналогично и с объектно-ориентированной БД. Просто создать объект и заполнить его атрибуты нулевыми значениями явно недостаточно. Некоторые типы, такие как TImage, могут потребовать специальных средств создания, редактирования и удаления объектов. Причем использоваться должны только эти средства и никакие другие. В этих случаях программа-браузер, рассматривающая все типы как равноправные, а именно такая программа и должна предоставляться на уровне Dss, может стать инструментом, с помощью которого легко разрушается логическая целостность БД. Второй основной причиной отсутствия универсальной программы-браузера является большая трудоемкость создания интерактивного инструмента, функционирующего одновременно в трех оконных средах (Win32, OS/2 Presentaion Manager, X-Windows).

            Однако, в крупных проектах очень удобно иметь инструмент, позволяющий просматривать и редактировать БД с учетом специфики каждого типа. Написание такого инструмента является задачей программистов, использующих Dss. Для этого можно, например, выработать спецификацию для всех хранимых типов: создание объекта выполняется методом create, редактирование - методом edit, просмотр - методом view, а удаление - методом destroy. Но практически наверняка будет выделено несколько типов (в сложных объектных моделях их может быть несколько десятков), для которых написание таких методов является лишь напрасной тратой времени. Например, тип TImageItemList, который не может существовать сам по себе, а создается и удаляется типом TImage. Единственное, что необходимо для этого типа - это средство просмотра объектов. Но программировать метод view для каждого несамостоятельного типа также является излишним. Гораздо удобнее написать один метод view, который мог бы просматривать любой тип. Для этого необходимы средства, позволяющие определить структуру типа. Dss предоставляет такие средства.

            Метод:

 

TDssDBError  attrGetCount( TDssIDType idt, TDssUInt & uiCountAll, TDssUInt * puiCountSelf );

 

позволяет определить общее количество атрибутов (параметр uiCountAll) и количество собственных атрибутов (параметр puiCountSelf) для типа idt. Собственные атрибуты - это атрибуты, объявленные в описании типа. Кроме собственных атрибутов в типе могут присутствовать унаследованные атрибуты. Причем унаследованные атрибуты в БД-образе объекта предшествуют собственным атрибутам. Если параметр puiCountSelf равен 0, то возвращается только общее количество атрибутов.

            Метод:

 

TDssDBError  attrGetParams( TDssIDType idt, TDssUInt index, char * pszNameBuf, TDssUInt uiNameBufSize, TDssUInt * puiNameLength, TDssIDType * pidtypeAttr, TDssUInt * puiVectorSize, TDssBool * pbAccessable, char * pszPathBuf, TDssUInt szPathBufSize, TDssUInt * puiPathLength );

 

позволяет получить параметры атрибута с порядковым номером index в общем списке атрибутов типа. Атрибуты нумеруются с нуля. Первыми в спиcке располагаются унаследованные атрибуты, затем собственные (т.е. индекс первого собственного атрибута равен uiCountAll - uiCountSelf). Значения возвращаются только для ненулевых аргументов. Аргументы pszNameBuf и uiNameBufSize описывают буфер-приемник имени атрибута. В аргументе puiNameLength возвращается длина имени атрибута без 0-символа. В pidtypeAttr возвращается тип атрибута. В puiVectorSize возвращается размер вектора. Если атрибут не является вектором, то в puiVectorSize возвращается 0. В pbAccessable возвращается c_bDssTrue, если атрибут считается доступным в смысле Dss. Доступным считается либо собственный атрибут типа, либо доступный атрибут, унаследованный из доступного базового типа. Аргументы pszPathBuf и szPathBufSize описывают буфер-приемник уточненного имени типа для атрибута. В puiPathLength возвращается длина уточненного имени типа.

            Типы атрибутов в Dss v.4.20 кодируются следующими значениями:

значение

тип

константа

1

char

c_uiDssTypeCode_Char

2

wchar_t

c_uiDssTypeCode_WChar

3

short

c_uiDssTypeCode_Short

4

int

c_uiDssTypeCode_Int

5

float

c_uiDssTypeCode_Single

6

double

c_uiDssTypeCode_Double

7

unsigned char

c_uiDssTypeCode_UChar

8

unsigned short

c_uiDssTypeCode_UShort

9

unsigned int

c_uiDssTypeCode_UInt

1000

TDssID

c_uiDssTypeCode_UsrType

Для всех атрибутов пользовательских типов в pidtypeAttr возвращается идентификатор пользовательского типа. Тип TDssID в этом случае так же рассматривается как обычный пользовательский тип.

            В уточненном имени типа для атрибута перечисляются имена типов, из которых атрибут был унаследован, разделенные символом “::”. Последним в уточненном имени указывается имя типа idt и символ “::”.

            Следующий пример отображает список всех атрибутов, включая уточненные имена типов, типы и размерности атрибутов.

 

void   ShowAttrTypeName( ostream & o, TDssKernelInterface & dss,

       TDssIDType idt, TDssUInt attrIndex );

 

void   ShowTypeAttrs( ostream & o, TDssKernelInterface & dss,

       TDssIDType idt ) {

       TDssUInt count;

       dss_check( dss.attrGetCount( idt, count, 0 ) );

       for( TDssUInt index = 0; index < count; index++ ) {

             TDssUInt uiNameLength;

             TDssUInt uiPathLength;

             dss_check( dss.attrGetParams( idt, index, 0, 0,

                    &uiNameLength, 0, 0, 0, 0, 0,

                    &uiPathLength ) );

 

             char * szName = new char[ uiNameLength + 1 ];

             char * szPath = new char[ uiPathLength + 1 ];

             dss_check( dss.attrGetParams( idt, index, szName,

                    uiNameLength, 0, 0, 0, 0, 0, szPath,

                    uiPathLength, 0 ) );

             o << szPath << szName << “: “;

             ShowAttrTypeName( o, dss, idt, index );

 

             delete szName;

             delete szPath;

       }

}

 

void   ShowAttrTypeName( ostream & o, TDssKernelInterface & dss,

       TDssIDType idt, TDssUInt index ) {

       TDssIDType idtAttr;

       TDssUInt uiVectorSize;

       dss_check( dss.attrGetParams( idt, index, 0, 0, 0,

             &idtAttr, &uiVectorSize, 0, 0, 0, 0 ) );

 

       TDssUInt uiTypeNameLength;

       dss_check( dss.typeGetParams( idtAttr, 0, 0,

             &uiTypeNameLength, 0, 0, 0, 0, 0, 0 ) );

       char * szName = new char[ uiTypeNameLength + 1 ];

       dss_check( dss.typeGetParams( idtAttr, szName,

             uiTypeNameLength, 0, 0, 0, 0, 0, 0, 0 ) );

       o << szName;

       delete szName;

 

       if( uiVectorSize )

             o << “[ “ << uiVectorSize << “ ]”;

       o << endl;

}

 

            Dss v.4.20 позволяет определить смещение атрибута относительно начала объекта при помощи метода:

 

TDssDBError  attrGetOffset( TDssIDType idt, TDssUInt index, TDssUInt & offset );

 

Однако, Dss не всегда знает расположение атрибутов в объекте. Об этом становится известно только после выполнения первого из следующих методов:

            - objAllocate с ненулевым параметром pcvInitial;

            - objLoad;

            - objLoadAs;

            - objUpdate.

Если какой-либо из вышеперечисленных методов вызывался для объектов нужного типа, то attrGetOffset возвратит смещение атрибута. В противном случае будет возвращен код ошибки eDBErrorOffsetUnknown. Т.о., если необходимо извлечь значение какого-либо атрибута, то необходимо сначала загрузить объект, затем обратиться к методу attrGetOffset для определения смещения атрибута, затем извлечь значение по указанному смещению.

Работа с идентификаторами

            Dss v.4.20 предоставляет средства для работы с находящимися в БД идентификаторами. Данные средства используются классом TDssIterator, но могут применяться программистами для решения собственных задач. Например, для создания программ-браузеров БД, программ экспорта-импорта данных, программ анализа данных или программ поддержки логической целостности данных.

            Метод:

 

TDssDBError  idGetCount( TDssID & idLast );

 

возвращает значение последнего идентификатора в БД плюс 1 (т.е. общее количество идентификаторов[23]). Возвращается ноль, если в базе данных нет идентификаторов (поля структуры TDssID содержат нулевые значения).

            Методы:

 

TDssDBError  idFirstGlobal( TDssID & id );

TDssDBError  idNextGlobal( TDssID & id );

 

перечисляют все идентификаторы БД, вне зависимости от типа объекта, к которому относится идентификатор. Идентификаторы могут относиться как к существующим, так и к удаленным объектам. Метод idFirstGlobal всегда возвращает первый идентификатор базы данных. Метод idNextGlobal возвращает идентификатор, следующий за идентификатором id. В случае исчерпания идентификаторов кодом возврата методов является значение eDBErrorNotFound.

            Методы:

 

TDssDBError  idFirstLocal( TDssIDType idt, TDssID & id );

TDssDBError  idNextLocal( TDssID & id );

 

возвращают, соответственно, первый и очередной идентификатор объектов типа idt. Метод idFirstLocal всегда возвращает первый идентификатор объектов типа idt. Возвращаемые идентификаторы относятся к существующим объектам. Класс TDssIterator обращается к этим методам при проведении итерации. Итерация накладывает ограничения на использование БД. Если же необходимо избежать этих ограничений и просмотреть объекты какого-либо типа, то можно воспользоваться методами idFirstLocal и idNextLocal. В случае исчерпания идентификаторов кодом возврата методов является значение eDBErrorNotFound.

            Определить параметры идентификатора можно при помощи метода:

 

TDssDBError  idGetParams( const TDssID & id, TDssIDType * pidtype, TDssObjAddress * paddress, TDssBool * pbObjExist );

 

В параметре pidtype возвращается идентификатор типа объекта. Если объект является удаленным, то в pidtype будет возвращено значение c_idtypeDssInvalid. В параметре paddress возвращается расположение объекта. Это значение может использоваться только программистами, хорошо знакомыми с физической организацией Dss. В параметре pbObjExist возвращается c_bDssTrue, если объект существует, в противном случае (объект был удален) - c_bDssFalse. Значения возвращаются только для ненулевых параметров. Если параметр id содержит не существующий идентификатор, то код возврата метода равен eDBErrorInvalidID.

Информационные функции

            Библиотека DssInterface содержит две функции, возвращающие информацию не о БД, а о Dss. Функция:

 

void   IDssInfoGetVersion( TDssUInt * puiVerHi, TDssUInt * puiVerLo, TDssUInt * puiRevision );

 

возвращает номер версии Dss. В параметре puiVerHi возвращается старший номер версии, в параметре puiVerLo - младший номер. В параметре puiRevision возвращается подномер младшего номера версии (номер реализации).

            Функция:

 

void   IDssInfoGetErrorName( TDssDBError dbe, char * pszMnemoName, TDssUInt uiMnemoNameBufSize, TDssUInt * puiMnemoNameLength, char * pszReason, TDssUInt uiReasonBufSize, TDssUInt * puiReasonLength );

 

возвращает описание ошибки с кодом dbe. Параметр pszMnemoName указывает на буфер для приема мнемо-имени ошибки (имя, под которым код dbe описан в перечислении TDssDBError). uiMnemoNameBufSize содержит размер этого буфера. В параметре puiMnemoNameLength возвращается длина мнемо-имени без учета 0‑символа. Параметр pszReason указывает на буфер для краткого описания причины возникновения ошибки. Параметр uiReasonBufSize содержит размер этого буфера. В параметре puiReasonLength возвращается длина описания без учета 0‑символа. Значения возвращаются только для ненулевых параметров.

            Функция IDssInfoGetErrorName предназначена для облегчения отладки программ, использующих Dss:

 

void   ShowDssError( TDssDBError dbe, const char * pcszFile, int iLine ) {

       if( dbe ) {

             char * pszMnemoName;

             char * pszReason;

             TDssUInt uiMnemoNameLength;

             TDssUInt uiReasonLength;

 

             IDssInfoGetErrorName( dbe, 0, 0, &uiMnemoNameLength,

                    0, 0, &uiReasonLength );

             pszMnemoName = new char[ ++uiMnemoNameLength ];

             pszReason = new char[ ++uiReasonLength ];

 

             IDssInfoGetErrorName( dbe, pszMnemoName, uiMnemoNameLength, 0,

                    pszReason, uiReasonLength, 0 );

 

             cerr << pcszFile << “, ” << iLine << “: ”

                    << pszMnemoName << “ (” << pszReason << “)” << endl;

 

             delete pszMnemoName;

             delete pszReason;

       }

}

#define dss_check(dbe) ShowDssError(dbe, __FILE__, __LINE__)

Клонирование типов

            Формирование и изменение схемы данных в БД является весьма трудоемкой задачей, которую решает утилита dssreg. При обработке описания схемы данных dssreg выполняет ряд действий: анализирует описание, модифицирует схему, изменяет объекты, генерирует вспомогательный код. Наличие вспомогательного кода делает невозможным динамическое добавление типов при работе с БД посредством DssInterface. Однако, в некоторых случаях дополнение схемы данных является необходимым действием. Например, в программах, позволяющих пользователю конструировать представление предметной области, используя готовые типы. Так, программа-конструктор информационно-измерительных систем предоставляет тип TDataSourcer, описывающий источники информации. Данный тип имеет средства для работы с какими-либо промышленными контроллерами и счетчиками. Пользователь может создавать объекты этого типа и настраивать их на конкретное оборудование. При этом пользователю хватает средств, заложенных в схему данных. Однако, ему бы хотелось, чтобы счетчики тепла в БД не смешивались со счетчиками газа. Для этого можно было бы ввести два новых типа:

 

struct TDataSourcer_Hot : public TDataSourcer {

};

struct TDataSourcer_Gas : public TDataSourcer {

};

 

которые не расширяют функциональность типа TDataSourcer, а только позволяют считать объекты-счетчики тепла и объекты-счетчики газа формально относящимися к разным типам. Тогда итерация по типу TDataSourcer_Hot будет перечислять только счетчики тепла и никакие другие. Однако, добавить эти типы в БД из программы конструктора можно только прибегнув к сложному механизму: временному выходу из программы, модификации схемы данных, компиляции вспомогательного кода, перелинковке программы-конструктора. При этом с конструктором пользователю необходимо будет предоставить компилятор, часть исходных текстов (заголовочные файлы), конструктор в виде библиотек и объектных файлов. Понятно, что в большинстве случаев это невозможно.

            Для решения этой задачи в Dss v.4.20 введен механизм клонирования типов. Клон - это элемент схемы данных, являющийся ссылкой на описание какого-либо типа в схеме. Клон имеет собственные имя и идентификатор типа. Поэтому объекты клона относятся именно к клону, а не к тому типу, на который ссылается клон. Тип, на который ссылается клон, называется исходным типом. Поскольку клон является только ссылкой на описание типа, то функциональность клона полностью соответствует функциональности исходного типа. Фактически, Dss рассматривает объекты клона как объекты исходного типа. При их загрузке и выгрузке происходит обращение к вспомогательному коду, сгенерированному для исходного типа. Поиск именованных методов по идентификатору клона или идентификаторам объектов клона производится в исходном типе. При внесении изменений в исходный тип изменения отражаются и на всех объектах всех клонов типа. При удалении исходного типа удаляются и все его клоны.

            Типы, для которых возможно клонирование, называются нормальными типами. Нормальный тип описывается на DssDDL при помощи ключевого слова struct и для него генерируется вспомогательный код. Клонировать можно только нормальные типы - нельзя создать клон клона.

            Создание клона осуществляется методом:

 

TDssDBError  cloneCreate( TDssIDType idtype, const char * pcszCloneName, TDssIDType & idtypeClone );

 

Параметр idtype содержит идентификатор исходного типа. Параметр pcszName задает имя клона. В idtypeClone возвращается идентификатор клона. Имя клона - это ASCIIZ строка произвольной длины, содержащая символы ‘A’-’Z’, ‘a’-’z’, ‘_’, ‘0’-’9’, начинающаяся либо с буквы, либо со знака подчеркивания. Имя клона должно быть уникальным. При использовании DssDDL можно создавать вложенные клоны:

 

struct A {

...

};

struct B {

       clone A AClone;     // B::AClone

       ...

};

 

Метод cloneCreate не позволяет создавать вложенные клоны.

            В примере с программой-конструктором можно позволить пользователю создать клоны TDataSourcer_Hot, TDataSourcer_Gas и работать с ними. При этом итерация по TDataSourcer_Hot будет перечислять только объекты-счетчики тепла и никакие другие.

Получение информации о типах базы данных

            Dss v.4.20 предоставляет несколько методов для получения информации о типах, описанных в базе данных.

            Метод:

 

TDssDBError  typeFind( const char * pcszTypeName, TDssIDType & idtype );

 

ищет в БД тип с именем pcszTypeName и, в случае успешного поиска, возвращает в параметре idtype идентификатор найденного типа. Если тип не найден, то код возврата метода равен eDBErrorNotFound. Метод typeFind производит поиск как нормальных типов, так и клонов.

            Метод:

 

TDssDBError  typeGetCount( TDssUInt & uiCount );

 

возвращает количество типов в базе данных[24]. В это количество входят количество нормальных типов и количество клонов.

            Метод:

 

TDssDBError  typeEnum( TDssIDType & idtype );

 

перечисляет все типы базы данных (включая нормальные типы и клоны). При первом вызове метода параметр idtype должен иметь значение c_idtypeDssInvalid. При последующих вызовах - значение, возвращенное предыдущим вызовом. При исчерпании типов код возврата метода равен eDBErrorNotFound.

            Метод:

 

TDssDBError  typeGetParams( TDssIDType idtype, char * pszNameBuf, TDssUInt uiNameBufSize, TDssUInt * puiNameLength, TDssUInt * puiTypeSpec, TDssIDType * pidtypeSource, TDssUInt * puiObjSizeDB, TDssUInt * puiObjSizeMem, TDssUInt * puiBaseCount, TDssUInt * puiObjCount );

 

позволяет получить параметры типа idtype. Параметр pszNameBuf указывает на буфер для приема имени типа. Размер этого буфера указывается в параметре uiNameBufSize. В параметре puiNameLength возвращается имя типа без учета 0‑символа. В параметре puiTypeSpec возвращается признак стандартный тип/ нормальный тип/ клон (значение c_uiDssTypeSpec_Std/ c_uiDssTypeSpec_Normal/ c_uiDssTypeSpec_Clone). Если тип является клоном, то в параметре pidtypeSource возвращается идентификатор исходного типа. В параметре puiObjSizeDB возвращается размер объекта в БД, в параметре puiObjSizeMem - размер объекта в ОП. Размер указывается в байтах. В параметре puiBaseCount возвращается количество непосредственных базовых типов для типа idtype. В параметре puiObjCount возвращается количество существующих объектов этого типа в БД. Значения возвращаются только для ненулевых параметров.

            Определить количество существующих объектов какого-либо типа можно при помощи метода:

 

TDssDBError  typeGetObjCount( TDssIDType idtype, TDssUInt & uiObjCount );

 

            Получить список непосредственных базовых типов для какого-либо типа можно при помощи метода:

 

TDssDBError  typeEnumBase( TDssIDType idtype, TDssIDType & idtypeBase, TDssBool * pbVirtual, TDssUInt * puiDeniedReason );

 

В параметре idtypeBase возвращается идентификатор очередного базового типа. При первом вызове typeEnumBase должен содержать значение c_idtypeDssInvalid. При следующих вызовах - значение, возвращенное предыдущим вызовом метода. В параметре pbVirtual возвращается значение c_bDssTrue, если данный базовый тип является виртуальным, и c_bDssFalse в противном случае. В параметре puiDeniedReason возвращается значение, определяющее доступность базового типа. Значение 0 возвращается, если базовый тип является доступным (public), другие значения указывают на недоступный базовый тип. Значения возвращаются только для ненулевых параметров. При исчерпании базовых типов код возврата равен eDBErrorNotFound.

            Метод:

 

TDssDBError  typeIsDerivedFrom( TDssIDType idtypeDerived, TDssIDType idtypeBase, TDssBool & bResult, TDssUInt * puiDeniedReason );

 

позволяет проверить, является ли тип idtypeBase базовым для типа idtypeDerived. При этом тип idtypeBase не обязательно может оказаться непосредственным базовым типом. Проверяется его наличие во всем графе наследования для типа idtypeDerived. В параметре bResult возвращается c_bDssTrue, если тип входит в граф наследования. При этом в параметре puiDeniedReason возвращается признак доступности этого типа. Если тип idtypeBase доступен, то puiDeniedReason содержит 0, если idtypeBase присутствует в иерархии как недоступный базовый тип, то puiDeniedReason содержит значение c_uiDssDenyReason_Nonpublic. Если нет однозначного преобразования типа idtypeDerived к типу idtypeBase, то puiDeniedReason содержит значение c_uiDssDenyReason_Ambigous. Такая ситуация может произойти, если idtypeBase присутствует в иерархии несколько раз как доступный, либо как доступный и виртуальный базовый тип.

            Данный пример позволяет отображать список типов базы данных в формате, близком к языку DssDDL (не указывается имя процедуры для метода).

 

#include <iostream.h>

 

#include <dssi.hpp>

#include <dssmcrs.hpp>

 

void   ShowDssError( TDssDBError dbe, const char * pcszFile, int iLine ) {

       if( dbe ) {

             char * pszMnemoName;

             char * pszReason;

             TDssUInt uiMnemoNameLength;

             TDssUInt uiReasonLength;

 

             IDssInfoGetErrorName( dbe, 0, 0, &uiMnemoNameLength,

                    0, 0, &uiReasonLength );

             pszMnemoName = new char[ ++uiMnemoNameLength ];

             pszReason = new char[ ++uiReasonLength ];

 

             IDssInfoGetErrorName( dbe, pszMnemoName, uiMnemoNameLength, 0,

                    pszReason, uiReasonLength, 0 );

 

             cerr << pcszFile << “, ” << iLine << “: ”

                    << pszMnemoName << “ (” << pszReason << “)” << endl;

 

             delete pszMnemoName;

             delete pszReason;

       }

}

#define dss_check(dbe) ShowDssError(dbe, __FILE__, __LINE__)

 

void   ShowBases( TDssKernelInterface & dss,

       TDssIDType idt, ostream & o ) {

       TDssUInt uiBaseCount;

       dss_check( dss.typeGetParams( idt, 0, 0,

             0, 0, 0, 0, 0, &uiBaseCount, 0 ) );

       if( uiBaseCount ) {

             o << ":\n";

 

             TDssIDType idtBase = c_idtypeDssInvalid;

             TDssBool bVirtual;

             TDssUInt uiDeniedReason;

             while( eDBErrorOk == dss.typeEnumBase( idt, idtBase,

                    &bVirtual, &uiDeniedReason ) ) {

                    o << "\t";

                    if( bVirtual )

                           o << "virtual ";

                    if( !uiDeniedReason )

                           o << "public ";

 

                    TDssUInt uiBaseTypeNameLength;

                    dss_check( dss.typeGetParams(

                           idtBase, 0, 0, &uiBaseTypeNameLength,

                           0, 0, 0, 0, 0, 0 ) );

                    char * pszBaseName = new char[

                           uiBaseTypeNameLength + 1 ];

                    dss_check( dss.typeGetParams( idtBase, pszBaseName,

                           uiBaseTypeNameLength, 0, 0, 0, 0, 0, 0, 0 ) );

                    o << pszBaseName;

                    delete pszBaseName;

 

                    if( --uiBaseCount )

                           o << ",\n";

                    else

                           o << " ";

             }

       }

}

 

void   ShowAttrTypeName( TDssKernelInterface & dss,

       TDssIDType idt, ostream & o ) {

       o << "\t";

 

       TDssUInt uiTypeNameLength;

       dss_check( dss.typeGetParams( idt, 0, 0, &uiTypeNameLength,

             0, 0, 0, 0, 0, 0 ) );

       char * pszName = new char[ uiTypeNameLength + 1 ];

       dss_check( dss.typeGetParams( idt, pszName,

             uiTypeNameLength, 0, 0, 0, 0, 0, 0, 0 ) );

       o << pszName;

       delete pszName;

}

 

void   ShowAttrs( TDssKernelInterface & dss,

       TDssIDType idt, ostream & o ) {

       TDssUInt uiAttrsCount, uiSelfCount;

       dss_check( dss.attrGetCount( idt, uiAttrsCount, &uiSelfCount ) );

       if( uiSelfCount ) {

             // Существуют собственные атрибуты

             o << "\n\n";

 

             for( TDssUInt index = uiAttrsCount - uiSelfCount;

                    index < uiAttrsCount; index++ ) {

                    TDssUInt uiAttrNameLength;

                    TDssUInt uiVectorSize;

                    TDssIDType idtAttr;

                    dss_check( dss.attrGetParams(

                           idt, index, 0, 0, &uiAttrNameLength,

                           &idtAttr, &uiVectorSize, 0, 0, 0, 0 ) );

                    char * pszAttrName = new char[

                           uiAttrNameLength + 1 ];

                    dss_check( dss.attrGetParams( idt, index, pszAttrName,

                           uiAttrNameLength, 0, 0, 0, 0, 0, 0, 0 ) );

 

                    ShowAttrTypeName( dss, idtAttr, o );

                    o << " ";

                    o << pszAttrName;

                    delete pszAttrName;

                    if( uiVectorSize )

                           o << "[ " << uiVectorSize << " ]";

                    o << ";\n";

             }

       }

}

 

void   ShowMethods( TDssKernelInterface & dss,

       TDssIDType idt, ostream & o ) {

       TDssUInt uiMethods;

       dss.methodGetCount( idt, uiMethods );

       if( uiMethods ) {

             o << "\n\n";

 

             for( TDssUInt ui = 0; ui < uiMethods; ui++ ) {

                    TDssUInt uiNameLength;

                    dss_check( dss.methodGetParams( idt, ui, 0, 0,

                           &uiNameLength, 0 ) );

                    char * pszName = new char[ uiNameLength + 1 ];

                    dss_check( dss.methodGetParams( idt, ui, pszName,

                           uiNameLength, 0, 0 ) );

                    o << "\tmethod " << pszName  << ";\n";

                    delete pszName;

             }

       }

}

 

void   ShowDescForNormalType( TDssKernelInterface & dss,

       TDssIDType idt, const char * pcszTypeName, ostream & o ) {

       o << "struct " << pcszTypeName << " ";

 

       ShowBases( dss, idt, o );

       o << "{\n";

       ShowAttrs( dss, idt, o );

       ShowMethods( dss, idt, o );

       o << "};" << endl;

}

 

void   ShowDescForClone( TDssKernelInterface & dss,

       TDssIDType idtSource, const char * pcszTypeName,

       ostream & o ) {

       // Определим длину имени исходного типа и имя исходного типа

       TDssUInt uiSourceTypeNameLength;

       dss.typeGetParams( idtSource, 0, 0, &uiSourceTypeNameLength,

             0, 0, 0, 0, 0, 0 );

       char * pszSourceTypeName = new char[ uiSourceTypeNameLength + 1 ];

       dss.typeGetParams( idtSource, pszSourceTypeName,

             uiSourceTypeNameLength, 0, 0, 0, 0, 0, 0, 0 );

 

       o << "clone " << pcszTypeName << " "

             << pszSourceTypeName << ";" << endl;

}

 

void   ShowTypeDescription( TDssKernelInterface & dss,

       TDssIDType idt, ostream & o ) {

       // Определим спецификацию и имя типа

       TDssUInt uiTypeSpec;

       TDssIDType idtSource;

       TDssUInt uiTypeNameLength;

       char * pszTypeName;

 

       dss_check( dss.typeGetParams( idt, 0, 0, &uiTypeNameLength,

             &uiTypeSpec, &idtSource,

             0, 0, 0, 0 ) );

       pszTypeName = new char[ uiTypeNameLength + 1 ];

       dss_check( dss.typeGetParams( idt, pszTypeName, uiTypeNameLength,

             0, 0, 0, 0, 0, 0, 0 ) );

 

       if( c_uiDssTypeSpec_Normal == uiTypeSpec )

             ShowDescForNormalType( dss, idt, pszTypeName, o );

       else

             if( c_uiDssTypeSpec_Clone == uiTypeSpec )

                    ShowDescForClone( dss, idtSource,

                           pszTypeName, o );

             else

                    o << "Неизвестная спецификация типа" << endl;

 

       o << flush;

       delete pszTypeName;

}

 

void   main( int argc, char ** argv ) {

       if( 2 == argc ) {

             TDssKernelInterface dss;

             if( eDBErrorOk == dss.dbOpen( argv[ 1 ],

                    c_uiDssMode_ReadOnly, c_uiDssRpLevel1,

                    dss_type_info, dss_type_methods ) ) {

                    TDssIDType idt = c_idtypeDssInvalid;

                    while( eDBErrorOk == dss.typeEnum( idt ) )

                           ShowTypeDescription( dss, idt, cout );

             }

       }

}

 

Инвариантность схемы данных

            Под инвариантностью схемы данных понимается возможность работы с БД, если:

            - список типов схемы данных БД является подсписком списка типов во вспомогательном коде;

            - список типов во вспомогательном коде является подсписком списка типов схемы данных БД;

            - множество собственных именованных методов типа в схеме данных БД является подмножеством множества собственных именованных методов во вспомогательном коде;

            - множество собственных именованных методов типа во вспомогательном коде является подмножеством множества собственных именованных методов в схеме данных БД;

            Инвариантность схемы данных была реализована в Dss v.4.20, исходя из практического опыта применения Dss в коллективной разработке программного обеспечения.

            Коллективная разработка характеризуется интервалами времени, в течение которых отдельные коллективы самостоятельно работают над частями общего проекта без пересечения между собой. В применении к Dss это приводит к тому, что каждый коллектив оперирует своей подсхемой общей схемы данных. Часто эти подсхемы либо не имеют пересечений, либо имеют незначительные пересечения. В такой ситуации необходимо обеспечить наименее болезненную и трудоемкую интеграцию результатов работы отдельных коллективов.

            Предположим, что коллектив А работает над частью проекта с использованием хранимых типов TA, TB и, частично, TC. Коллектив B параллельно работает над частью проекта с использованием хранимых типов TD, TE и, частично, TC. Наступает момент, когда коллективу A необходимо проверить, могут ли их программы работать с объектами типа TC, созданными программами коллектива B. Для этого, коллектив B передает коллективу A свою тестовую БД. В схеме данных этой БД присутствуют типы TD, TE, TC. Во вспомогательном коде программ коллектива A присутствуют типы TA, TB, TC. В Dss младших, чем 4.20, версий понадобилось бы:

            - модифицировать схему данных БД путем добавления типов TA и TB;

            - перекомпилировать вспомогательный код и подключить к программам коллектива A объектный код для типов TD и TE.

В Dss v.4.20 этого не требуется. Программы коллектива A смогут работать с БД коллектива B без каких-либо изменений. Однако, попытки обращения к типам TD и TE, например, попытка загрузить объекты этих типов, будет приводить к возникновению ошибки eDBErrorTypeNotImplemented.

            Предположим, что коллектив B добавил в тип TC метод “dump”, использующийся для отладочных целей, который не войдет в конечную реализацию проекта. В программах коллектива A нет реализации этого метода. Благодаря инвариантности схемы данных коллектив А сможет использовать базу данных коллектива B. Попытка обращения к этому методу приведет к возникновению ошибки eDBErrorMethodNotImplemented. Если же БД коллектива A (в схеме данных которой нет метода “TC::dump”) будет использоваться программами коллектива B, то обращение к методу “TC::dump” приведет к возникновению ошибки eDBErrorNotFound, поскольку поиск метода производится в схеме данных БД.

            Инвариантность схемы данных допускает несовпадение схем данных БД и вспомогательного кода со следующими ограничениями:

            - типы, входящие в пересечение схем данных, должны иметь полностью совпадающие списки непосредственных базовых типов (имена, доступность и виртуальность) и собственных атрибутов типа (имена, типы и резмерности). Порядок следования элементов в списках учитывается;

            - если в пересечение схем данных входит производный тип, то в пересечение должны входить все базовые типы этого типа.

В случае нарушения этих ограничений при открытии БД возникает ошибка eDBErrorInvalidScheme.

Многопользовательские возможности Dss v.4.20

Предварительная реализация многопользовательских возможностей в Dss v.4.20

            Конечной целью развития Dss в настоящее время считается реализация многопользовательской клиент-серверной версии Dss для гетерогенных сред. Dss v.4.20 является очередным шагом к достижению этой цели. Будучи однопользовательской СУБД Dss предоставляет такие возможности многопользовательских СУБД, как параллельные (независимые) транзакции и синхронизация доступа к данным при помощи блокировок. Целью создания Dss v.4.20 явилась необходимость выработки, реализации, тестирования и совершенствования стратегии разделения и синхронизации доступа при параллельных запросах к данным. Результаты, полученные при эксплуатации Dss v.4.20, станут основой для реализации будущих версий Dss.

Использование нитей в качестве отдельных процессов

            При реализации Dss v.4.20 использованы следующие предпосылки:

            - открыть БД для чтения-записи может только один процесс;

            - мельчайшей единицей диспетчеризации в большинстве операционных систем является нить (thread). Каждая нить имеет идентификатор, назначаемый операционной системой. В операционной системе имеются средства определения идентификатора нити, на контексте которой происходят обращение к методу Dss. Т.е. существует способ определить нить, осуществляющую доступ к данным.

            Исходя их этих предпосылок в Dss v.4.20 многопользовательские возможности предоставляются на уровне нитей. Каждая нить может рассматриваться как независимый процесс, осуществляющий собственные действия над БД и нуждающийся в синхронизации доступа по отношению к остальным нитям (процессам).

            Dss v.4.20 делит все нити процесса на две категории: выделенные и невыделенные нити. Выделенная нить рассматривается как отдельный процесс. Все невыделенные нити рассматриваются как один процесс. В случае невыделенных нитей работа Dss v.4.20 не отличается от работы Dss предыдущих версий. Т.е. объект, загруженный одной невыделенной нитью, может быть выгружен другой невыделенной нитью.

            Нить начинает считаться выделенной после вызова ею метода TDssKernelInterface::threadAttach и перестает считаться выделенной после вызова ею метода TDssKernelInterface::threadDetach. Все действия по созданию, манипулированию и завершению нитей осуществляются процессом самостоятельно. При обращении к методам класса TDssKernelInterface Dss определяет идентификатор нити, на контексте которой произведен вызов метода. Если идентификатор принадлежит выделенной нити, то действие рассматривается независимо от действий остальных нитей. В противном случае действие нити рассматривается в зависимости от действий остальных нитей. Так, если выделенная нить не начинала транзакций и вызывает метод transactionEnd, то возникнет ошибка eDBErrorNoTransactions. Если невыделенная нить не начинала транзакций и вызывает метод transactionEnd, то результат будет зависеть от того, начинали ли транзакцию другие невыделенные нити.

            Таким образом, в Dss v.4.20 при помощи метода threadAttach можно указать Dss рассматривать действия нити независимо от действий остальных нитей процесса. В этом случае Dss считает нить отдельным процессом. Dss перестает считать нить отдельным процессом после вызова метода threadDetach.

Метод threadAttach

            Метод:

 

TDssDBError  threadAttach( TDssUInt uiRpLevel );

 

вызванный на контексте какой-либо нити, указывает Dss рассматривать данную нить как отдельный процесс. При этом Dss преобразует системный идентификатор нити в системно-независимый идентификатор процесса[25]. Полученный идентификатор Dss использует в дальнейшем для отметки действий, выполненых данной нитью. Благодаря этому идентификатору Dss осуществляет поддержку независимых транзакций и синхронизацию доступа. Каждая выделенная нить использует уникальный идентификатор процесса. Все невыделенные нити используют одинаковый  идентификатор процесса.

            Параметр uiRpLevel указывает уровень восстановочной информации, который необходим выделенной нити для работы с БД. Уровень восстановочной информации может отличаться от указанного при открытии БД. Так, БД может быть открыта с уровнем c_uiDssRpLevel1, а выделенная нить может указать уровень c_uiDssRpLevel2. В этом случае выделенная нить может использовать транзакции, а невыделенные нити - нет.

            Метод threadAttach может быть вызван в любое время после открытия БД. Метод threadAttach может быть вызван на контексте любой нити, в том числе и нити, открывшей БД.

            Действия нити, выполненные до вызова threadAttach, не относятся к действиям, выполняемым нитью после вызова threadDetach, поскольку для отметки действий Dss использует собственные идентификаторы процессов, а не системные идентификаторы нитей:

 

void   ThreadBody( TDssKernelInterface & dss ) {

       // Сейчас нить рассматривается как невыделенная

       dss_check( dss.transactionBegin() );

 

       TDssIDType idt;

       dss_check( dss.typeFind( “TDssID”, idt ) );

       TDssID id;

       dss_check( dss.objAllocate( idt, id ) );

 

       // Переводим нить в категорию выделенных

       dss_check( dss.threadAttach( c_uiDssRpLevel2 ) );

 

       // Теперь нить не имеет никакого отношения к

       // своим предыдущим действиям

       TDssID id2;

       dss_check( dss.objAllocate( idt, id2 ) );

 

       // Поскольку выделенная нить не начинала транзакций,

       // то следующее действие приведет к ошибке

       dss_check( dss.transactionEnd() );

 

       dss_check( dss.threadDetach() );

 

       // Теперь нить опять стала невыделенной и

       // “вспомнила” свои предыдущие действия

       dss_check( dss.transactionUndo() );

}

 

В результате работы данного примера первый созданный объект, идентификатор которого помещен в id, будет изъят из БД, поскольку невыделенная нить отменила свою транзакцию. Второй созданный объект, идентификатор которого помещен в id2, останется существовать в БД, поскольку его создала выделенная нить.

            Вызовы threadAttach не могут быть вложенными. Повторный вызов threadAttach приводит к возникновению ошибки eDBErrorThreadAttached. Îäíàêî, íèòü ìîæåò íåñêîëüêî ðàç ïåðåõîäèòü â êàòåãîðèþ âûäåëåííîé íèòè.  ýòîì ñëó÷àå íåîáõîäèìî, ÷òîáû çà êàæäûì âûçîâîì threadAttach ñëåäîâàë âûçîâ threadDetach:

 

void   ThreadBody( TDssKernelInterface & dss ) {

       TDssBool bContinue = c_bDssTrue;

 

       while( bContinue ) {

             // Нить становится выделенной для выполнения

             // независимых действий

             dss_check( dss.threadAttach( c_uiDssRpLevel2 ) );

             ...

             dss_check( dss.threadDetach() );

             ...

             // Выяснение необходимости дальнейшей работы

             bContinue = ...;

       }

}

 

Метод threadDetach

            Метод:

 

TDssDBError  threadDetach( void );

 

вызванный на контексте выделенной нити, указывает Dss перевести данную нить в категорию невыделенных нитей и прекратить рассматривать ее действия как действия отдельного процесса.

            При выполнении метода threadDetach Dss осуществляет действия, аналогичные закрытию БД - очищает ресурсы, назначенные нити в процессе работы; однако БД остается открытой. Dss выгружает все объекты, загруженные нитью, и завершает все итерации, начатые нитью. Если на момент вызова threadDetach нить имела незавершенные транзакции, то действия метода threadDetach аналогичны действиям метода transactionTerminate - очищаются все ресурсы, и закрывается БД. Последующие обращения к БД из любых нитей будут приводить к ошибке eDBErrorTransactionTerminated.

            Dss для каждой выделенной нити устанавливает соответствие системного идентификатора нити и внутреннего системно-независимого идентификатора процесса. При вызове какого-либо метода по системному идентификатору определяется системно-независимый идентификатор процесса. Если идентификатор определен, то Dss считает, что вызов осуществила выделенная нить. В противном случае Dss считает, что вызов осуществила невыделенная нить, и в качестве идентификатора процесса берется общее для всех невыделенных нитей значение. Далее, найденый идентификатор используется как метка для выполняемых по запросу действий. Взаимное соответствие системного идентификатора нити и системно-независимого идентификатора процесса уничтожается при вызове threadDetach. Если выделенная нить завершила свою работу без вызова threadDetach, то Dss не сможет определить, что данная нить перестала существовать как единица операционной системы. Поэтому соответствие системного идентификатора нити и системно-независимого идентификатора процесса не будет уничтожено. Некоторые операционные системы, например, Windows NT, используют идентификаторы нитей повторно. В этом случае может произойти следующая ситуация:

            - процесс создает нить, которой назначается системный идентификатор нити N;

            - данная нить вызывает threadAttach;

            - данная нить выполняет свои действия и завершается без вызова threadDetach;

            - через какое-то время процесс создает новую нить, которой система вновь назначает идентификатор N.

В этом случае первый же вызов любого метода из новой нити Dss будет рассматривать как продолжение работы выделенной нити. Поэтому каждая выделенная нить должна осуществлять вызов метода threadDetach перед своим завершением.

            Для обеспечения парности вызовов threadAttach и threadDetach Dss предоставляет класс TDssThreadAttach. Данный класс описывается в dssi.hpp, содержит только inline-методы и не требует ничего, кроме заголовочного файла. В конструкторе класса TDssThreadAttach осуществляется вызов метода threadAttach. Если вызов прошел удачно, то в деструкторе осуществляется вызов метода threadDetach. Поскольку нити оформляются в большинстве случаев в виде подпрограмм, наиболее удобно объявить в начале подпрограммы переменную типа TDssThreadAttach:

 

void   ThreadBody( TDssKernelInterface & dss ) {

       // Делаем нить выделенной

       TDssThreadAttach threadattach( dss, c_uiDssRpLevel2 );

       ...

}

 

В этом случае выход из ThreadBody, который по существу является завершением работы нити, приводит к автоматическому вызову деструктора переменной threadattach. Это, в свою очередь, приведет к вызову threadDetach.

Выделенные нити в случае нескольких БД

            В случае, если процесс одновременно работает с несколькими БД посредством нескольких экземпляров TDssKernelInterface, то любая нить процесса может выступать в любой роли для любой открытой БД. Т.е. нить может быть выделенной для одной БД и невыделенной для других, или наоборот:

 

void   ThreadBody1( TDssKernelInterface & db1,

       TDssKernelInterface & db2,

       TDssKernelInterface & db3 ) {

       // Нить является выделенной только для БД db1

       TDssThreadAttach threadattach( db1, c_uiDssRpLevel2 );

       ...

}

 

void   ThreadBody2( TDssKernelInterface & db1,

       TDssKernelInterface & db2,

       TDssKernelInterface & db3 ) {

       // Нить является выделенной для всех БД,

       // за исключением db1

       TDssThreadAttach threadattachDb2( db2, c_uiDssRpLevel1 );

       TDssThreadAttach threadattachDb3( db3, c_uiDssRpLevel2 );

       ...

}

Многопользовательский режим работы

            В Dss v.4.20 процессом является либо выделенная нить, либо все невыделенные нити программы, использующей Dss. Под многопользовательским режимом работы в Dss v.4.20 понимается одновременная работа параллельных процессов с одной БД посредством одной переменной типа TDssKernelInterface. Многопользовательский режим работы имеет ряд описываемых ниже отличий.

Загрузка и выгрузка объектов

            Для каждого процесса Dss поддерживает список объектов, загруженных данным процессом. Процесс может записывать и выгружать только загруженные им объекты. Процесс не может воздействовать на списки загруженных объектов других процессов.

            Процесс может загрузить объект в режиме c_uiDssMode_ReadOnly, если объект не загружен в данный момент, либо если объект загружен любым процессом в режиме c_uiDssMode_ReadOnly. При загрузке объекта в режиме c_uiDssMode_ReadOnly для каждого процесса в ОП создается собственная копия объекта. Если объект не имеет расширения, то при повторных загрузках объекта процессом используется существующая копия объекта и счетчик обращений к ней. Если объект имеет расширение, то при повторных загрузках объекта создаются новые копии объекта в ОП. Для каждого процесса поддерживаются списки копий объекта. Процесс может выгружать только те копии объекта, которые входят в его список копий. Процесс не может воздействовать на списки копий других процессов (процесс не может выгрузить копию объекта, загруженную параллельным процессом).

            Процесс может загрузить объект в режиме c_uiDssMode_ReadWrite только, если данный объект не загружен в ОП. Если объект загружен в ОП в каком-либо режиме каким-либо процессом (включая и процесс, пытающийся загрузить объект), то попытка загрузки объекта отвергается. Записывать и выгружать объект, загруженный в режиме c_uiDssMode_ReadWrite, может только процесс, загрузивший объект.

            Процесс может загрузить объект в режиме c_uiDssMode_WriteShare, если объект не загружен в ОП, либо если объект загружен в ОП в режиме c_uiDssMode_WriteShare самим загружающим процессом. При повторных загрузках объекта используется единственная копия объекта в ОП и счетчик обращений к ней. Записывать и выгружать объект, загруженный в режиме c_uiDssMode_WriteShare, может только процесс, загрузивший объект.

            Метод objNonSharedLocation работает только в процессе, загрузившем объект в режиме c_uiDssMode_ReadWrite или c_uiDssMode_WriteShare.

            Следующая таблица показывает допустимость загрузки объекта в различных ситуациях. Столбцы указывают желаемый режим загрузки объекта. Строки указывают ситуацию с загруженностью объекта. Ячейка таблицы содержит “+”, если объект можно загрузить в желаемом режиме в сложившейся ситуации. В противном случае ячейка таблицы содержит “-”.

 

 

c_uiDssMode_ReadOnly

c_uiDssMode_ReadWrite

c_uiDssMode_WriteShare

Объект не загружен в ОП ни одним процессом

+

+

+

Объект загружен самим процессом в режиме c_uiDssMode_ReadOnly

+

-

-

Объект загружен самим процессом в режиме c_uiDssMode_ReadWrite

-

-

-

Объект загружен самим процессом в режиме c_uiDssMode_WriteShare

-

-

+

Объект загружен параллельным процессом в режиме c_uiDssMode_ReadOnly

+

-

-

Объект загружен параллельным процессом в режиме c_uiDssMode_ReadWrite

-

-

-

Объект загружен параллельным процессом в режиме c_uiDssMode_WriteShare

-

-

-

Итерации

            Для каждого процесса Dss поддерживает список начатых данным процессом итераций. Процесс может осуществлять и завершать только начатые им итерации. Процесс не может воздействовать на списки начатых итераций других процессов.

            Процесс может начать итерацию то типу в режиме c_uiDssMode_ReadOnly, если нет итераций по типу, либо если по типу проводятся итерации в режиме c_uiDssMode_ReadOnly вне зависимости от того, какие процессы их проводят. Итерацию в режиме c_uiDssMode_ReadOnly нельзя начать, если какой-либо объект данного типа загружен каким-либо процессом в режиме c_uiDssMode_ReadWrite или c_uiDssMode_WriteShare. При наличии итераций по типу в режиме c_uiDssMode_ReadOnly ни один процесс не может создавать (objAllocate), удалять (objDeallocate), обновлять (objUpdate) и записывать (objSave) объекты данного типа.

            Процесс может начать итерацию то типу в режиме c_uiDssMode_ReadWrite только, если ни один процесс не проводит итераций по данному типу. При наличии итераций по типу в режиме c_uiDssMode_ReadWrite ни один процесс не может создавать объекты данного типа. Любой процесс может удалять, обновлять или записывать объекты данного типа.

            Процесс может начать итерацию по типу в режиме c_uiDssMode_WriteShare, если нет итераций по типу, либо если сам процесс проводит итерации по типу в режиме c_uiDssMode_WriteShare. При наличии итераций по типу в режиме c_uiDssMode_WriteShare ни один процесс не может создавать объекты данного типа. Любой процесс может удалять, обновлять или записывать объекты данного типа.

            Возможность удаления объектов параллельными процессами во время проведения итерации в режиме c_uiDssMode_ReadWrite или c_uiDssMode_WriteShare создает опасность возникновения следующих ситуаций:

            - в процессе итерации будет найден удаленный объект, заблокированный транзакцией параллельного процесса (см. ниже). В этом случае метод TDssIteratorInterface::objNext будет возвращать ошибку eDBErrorAccessLocked до снятия блокировки параллельным процессом;

            - в период времени между получением идентификатора при помощи метода TDssIteratorInterface::objNext и дальнейшей обработкой полученного идентификатора объект будет удален параллельным процессом. В этом случае объект обработать не удастся, однако итерация может быть продолжена.

            Необходимо отметить, что из-за временного разрыва между вызовом метода TDssIteratorInterface::objNext и его дальнейшей обработкой, параллельные процессы могут вмешаться в ход итерации. Так, в этот интервал времени параллельный процесс может загрузить объект в режиме c_uiDssMode_ReadWrite. Если обработка найденных в ходе итерации объектов требует загрузки объектов, то выполнить обработку для данного объекта не удастся, поскольку объект загружен параллельным процессом. Dss v.4.20 не располагает средствами, предотвращающими возникновения описанных выше ситуаций.

Транзакции

            Любой процесс, работающий с БД, используя уровень восстановочной информации c_uiDssRpLevel2[26], может применять транзакции. В транзакциях процесса учитываются только действия самого процесса. Подтверждение и откат транзакций параллельных процессов осуществляется независимо друг от друга.

Блокировки

            В Dss принята идеология проведения транзакций, согласно которой действия, осуществленные в транзакции какого-либо процесса, сразу же оказывают влияние на параллельные процессы. Однако, параллельные процессы не могут воспользоваться результатами этих действий до завершения (подтверждения или отката) транзакции. Так, процесс A внутри транзакции создает клон. Параллельный процесс B при помощи метода typeGetCount способен определить момент изменения схемы данных (общее количество типов увеличится). Однако, получить информацию о новом типе при помощи typeGetParams, typeGetObjCount, typeGetIterationParams и т.д. Dss не позволит, поскольку в случае отката транзакции процесса A информация процесса B окажется неверной. Аналогично, процесс C в своей транзакции может создать новый объект какого-либо типа. Однако, процесс D, даже узнав каким-либо образом идентификатор нового объекта, не сможет получить к нему доступ. Хотя, процесс D при помощи метода typeGetObjCount способен определить, что количество объектов типа изменилось.

            При выполнении процессом действий внутри транзакции Dss осуществляет блокировку всех объектов, клонов и индивидуальных имен объектов, созданных, удаленных или измененных действиями процесса. При вызове любого метода Dss проверяет блокированность затрагиваемых методом элементов БД. Если элемент блокирован, то метод возвращает код ошибки. Блокировка снимается либо при откате транзакции, в которой блокировка была установлена, либо при подтверждении всех транзакций процесса. Например, процесс начинает три вложенные транзакции (1, 2, 3). В каждой из транзакций процесс изменяет по одному объекту (A, B, C). Доступ к этим объектам блокируется. Процесс подтверждает транзакцию 3. Доступ к объектам по прежнему блокирован, поскольку транзакции 1 и 2 могут быть отменены. Процесс отменяет транзакцию 2. Восстанавливаются предыдущие значения объектов B и C. Блокировка с этих объектов снимается, поскольку происходит откат транзакций, в которых блокировки были установлены. Объект A попрежнему блокирован. Блокировка объекта А будет снята либо при подтверждении, либо при откате транзакции 1.

            Dss v.4.20 поддерживает 4 уровня блокировок:

            1) c_uiDssLock_Destroy. Запрещается удаление блокированного объекта. Остальные действия над объектом (чтение, запись, проверка существования) разрешаются;

            2) c_uiDssLock_Write. Запрещается удаление и изменение блокированного объекта. Разрешается чтение и проверка существования объекта;

            3) c_uiDssLock_Read. Запрещается удаление, изменение и чтение блокированного объекта. Разрешается проверка существования объекта. Так, блокированный объект нельзя загрузить, но его идентификатор можно использовать в методах idGetInfo, methodFindByObject, TDssIteratorInterface::objNext и т.д.;

            4) c_uiDssLock_Access. Запрещается либой доступ к элементу БД.

            При проверке блокированности элемента БД Dss проверяет уровень блокировки и тип действия над элементом. Для процесса, заблокировавшего элемент БД, разрешается любой тип действия. Для остальных процессов разрашаются только те действия, которые допустимы при установленном уровне блокировки. В противном случае возвращаются соответствующие коды ошибок (eDBErrorDestroyLocked, eDBErrorWriteLocked, eDBErrorReadLocked, eDBErrorAccessLocked).

            В отличие от клонов и индивидуальных имен, объект может быть одновременно блокирован несколькими процессами с уровнями c_uiDssLock_Destroy или c_uiDssLock_Write. В этом случае Dss отслеживает, какой процесс и какую блокировку установил. При завершении транзакций Dss снимает блокировки процесса, не затрагивая блокировок параллельных процессов. При проверке блокированности объекта в этом случае Dss уже не разрешает блокирующему процессу произвольный тип действия, а проверяет доступность действия в соответствии с блокировками параллельных процессов.

            Объект или индивидуальное имя объекта могут блокироваться процессом многократно. Так, процесс может несколько раз записывать объект во время транзакции. При каждой записи Dss увеличивает количество блокировок объекта с уровнем c_uiDssLock_Read. Объект будет разблокирован после подтверждения или отката всех операций записи объекта.

            Dss v.4.20 осуществляет блокирование элементов БД автоматически и только при проведении процессом транзакций. Dss v.4.20 не предоставляет программисту средств ручной блокировки элементов БД.

Блокировка объектов

            При создании объекта (objAllocate) объект блокируется с уровнем c_uiDssLock_Access. Работать с объектом может только создавший его процесс.

            При удалении объекта (objDeallocate) объект блокируется с уровнем c_uiDssLock_Access. Идентификатор удаленного объекта может использовать только блокирующий процесс.

            При обновлении объекта (objUpdate) объект блокируется с уровнем c_uiDssLock_Read. Загружать, записывать, выгружать или удалять объект может только блокирующий его процесс.

            При записи значения объекта (objSave) объект блокируется с уровнем c_uiDssLock_Read. Загружать, записывать, выгружать или удалять объект может только блокирующий его процесс.

            При выгрузке объекта из ОП (objFree) объект блокируется с уровнем c_uiDssLock_Write. Записывать или удалять объект может только блокирующий процесс.

            Уровень блокировки c_uiDssLock_Read блокирует все “прямые” действия над объектом. Однако, параллельные процессы сохраняют возможность выполнения “косвенных” действий: найти объект в процессе итерации, найти именованный метод по идентификатору объекта, определить тип объекта и т.д.

            Объект блокируется при выгрузке по следующим причинам:

            - загруженный объект не нуждается в блокировке, поскольку невозможно удалить, обновить или изменить загруженный объект;

            - объект после выгрузки должен быть защищен от удаления и изменения, поскольку загружавший его процесс может использовать свои знания о значении объекта. Например, пусть процесс A подсчитывает усредненное значение атрибута m_value (значение) в некотором списке объектов типа THeatCounter (счетчик тепла). Процесс B в это же время вычитает значение 5.0 из атрибута m_value одного объекта типа THeatCount и добавляет это значение в атрибут m_value другого объекта THeatCounter. Причем, объекты, изменяемые процессом B, входят в список объектов для процесса A:

 

// Выполнение действий процесса A

float  ProcessA( TDssKernelInterface & dss,

       TDssUInt uiListSize, const TDssID * pidList ) {

       TDssThreadAttach threadattach( dss, c_uiDssRpLevel2 );

 

       dss_check( dss.transactionBegin() );

 

       float fMean = 0;

       THeatCounter * p;

       for( TDssUInt ui = 0; ui < uiListSize; ui++ ) {

             dss_check( dss.objLoad( pidList[ ui ],

                    c_uiDssMode_ReadOnly,

                    DSS_ADDRESS( p ) ) );

             fMean += p->m_value / uiListSize;

             dss_check( dss.objFree( pidList[ ui ], p ) );

       }

 

       dss_check( dss.transactionEnd() );

       return fMean;

}

 

// Выполнение действий процесса B

void   ProcessB( TDssKernelInterface & dss,

       const TDssID & id1,

       const TDssID & id2 ) {

 

       THeatCounter * p;

       dss_check( dss.objLoad( id1, c_uiDssMode_ReadWrite,

             DSS_ADDRESS( p ) ) );

       p->m_value -= 5.0;

       dss_check( dss.objSave( id1 ) );

       dss_check( dss.objFree( idt1, p ) );

 

       dss_check( dss.objLoad( id2, c_uiDssMode_ReadWrite,

             DSS_ADDRESS( p ) ) );

       p->m_value += 5.0;

       dss_check( dss.objSave( id2 ) );

       dss_check( dss.objFree( idt2, p ) );

}

 

Действия процесса B не изменяют среднего значения атрибута m_value в списке объектов процесса A. Однако, если бы объекты в процессе A не блокировались, то могла бы возникнуть следующая ситуация: процесс A уже обработал объект id1, но еще не обработал объект id2, а процесс B уже увеличил значение объекта id          2. Когда процесс A достигнет объекта id2, в среднем значении будет учтено новое значение объекта id2 и старое значение объекта id1, что приведет к получению неверного результата. Блокировка объекта id1 в процессе A не позволит процессу B изменить его значение[27].

            Объект, заблокированный с уровнем c_uiDssLock_Write, может быть загружен любым процессом в любом режиме. Однако, записать объект сможет только блокирующий процесс. Если блокировку c_uiDssLock_Write одновременно установили несколько процессов, то ни один процесс (включая блокирующие) не сможет записать объект.

Блокировка клонов

            Для созданного клона устанавливается блокировка c_uiDssLock_Access. Только создавший клон процесс может работать с клоном. Параллельным процессам запрещаются любые действия над клоном.

Блокировка индивидуальных имен объектов

            Для созданного индивидуального имени объекта устанавливается блокировка с_uiDssLock_Access. Только создавший имя процесс может работать с именем.

            Для удаленного индивидуального имени объекта устанавливается блокировка c_uiDssLock_Access. Это означает, что создать такое же имя заново может только процесс, удаливший имя. Попытки создания или поиска такого имени из параллельных процессов будут заканчиваться с кодом ошибки eDBErrorAccessLocked.

            Создание и удаление индивидуальных имен объектов оказывает воздействие на общее количество имен в БД. Поэтому возможны ситуации, когда один процесс внутри транзакции создает или удаляет имя, а второй процесс в это же время пытается получить список существующих имен, что приводит к возникновению ошибок. Например, процесс A в своей транзакции удаляет имя, расположенное в общем списке имен по индексу 9. Имя, располагавшееся по индексу 10, перемещается на индекс 9. В это время процесс B закончил обработку имени с индексом 8 и перешел к индексу 9. Пусть по индексу 9 располагается имя “image_on_title” для объекта 0x0:0xFFF1. Процесс B получает эту информацию и отображает ее на экране. В этот момент процесс A отменяет свою транзакцию. Удаленное имя возвращается на свое место, а имя “image_on_title” - на свое. После этого процесс B переходит к индексу 10 и вновь получает имя “image_on_title” для объекта 0x0:0xFFF1. В результате процесс B отобразит на экране одно и то же имя дважды.

            Блокировка индивидуальных имен оказывает влияние на удаление объектов. Удаление индивидуального имени устанавливает для имени блокировку c_uiDssLock_Access. При удалении объекта сначала удаляются его имена. Если какое-либо имя заблокированно параллельным процессом, то удаление имени окажется невозможным. Невозможным окажется и удаление объекта. Создание индивидуального имени объекта устанавливает для объекта блокировку c_uiDssLock_Destroy. Удаление объекта из параллельных процессов становится невозможным.

Блокировка идентификаторов

            Блокировка идентификаторов - это еще одно название блокировки объектов. В Dss существует ряд методов, по отношению к которым более логично говорить о блокировке идентификаторов. К таким методам относятся методы для работы с идентификаторами (idGetInfo, idGetCount, idFirstLocal, idFirstGlobal, idNextLocal, idNextGlobal), метод methodFindByObject и методы класса TDssIteratorInterface.

            При создании и удалении объекта для объекта устанавливается блокировка c_uiDssLock_Access. Dss блокирует объект по идентификатору. Поэтому можно говорить о блокировке идентификатора (особенно в случае удаления объекта). Заблокированный с уровнем c_uiDssLock_Access идентификатор не может использоваться в методах idGetInfo, idGetCount и т.д., в методе methodFindByObject и в методах класса TDssIteratorInterface. Негативным последствием этого является, например, обнаружение в процессе итерации заблокированного идентификатора. В этом случае нет возможности ни обработать найденный объект (неизвестно, удален он или создан, в каком состоянии окажется объект при подтверждении или откате блокирующей транзакции), ни перейти к следующему идентификатору данного типа.

Применение специальных средств контроля ошибок

            В многопользовательском режиме средства контроля ошибок приобретают более важное значение, чем в однопользовательском режиме. Для подтверждения этого рассмотрим приведенный выше пример, дополнив его простой реализацией макроса dss_check:

 

void   ShowDssError( TDssDBError dbe, const char * pcszFile, int iLine ) {

       if( dbe ) {

             char * pszMnemoName;

             char * pszReason;

             TDssUInt uiMnemoNameLength;

             TDssUInt uiReasonLength;

 

             IDssInfoGetErrorName( dbe, 0, 0, &uiMnemoNameLength,

                    0, 0, &uiReasonLength );

             pszMnemoName = new char[ ++uiMnemoNameLength ];

             pszReason = new char[ ++uiReasonLength ];

 

             IDssInfoGetErrorName( dbe, pszMnemoName, uiMnemoNameLength, 0,

                    pszReason, uiReasonLength, 0 );

 

             cerr << pcszFile << “, ” << iLine << “: ”

                    << pszMnemoName << “ (” << pszReason << “)” << endl;

 

             delete pszMnemoName;

             delete pszReason;

       }

}

#define dss_check(dbe) ShowDssError(dbe, __FILE__, __LINE__)

 

// Выполнение действий процесса A

float  ProcessA( TDssKernelInterface & dss,

       TDssUInt uiListSize, const TDssID * pidList ) {

       TDssThreadAttach threadattach( dss, c_uiDssRpLevel2 );

 

       dss_check( dss.transactionBegin() );

 

       float fMean = 0;

       THeatCounter * p;

       for( TDssUInt ui = 0; ui < uiListSize; ui++ ) {

             dss_check( dss.objLoad( pidList[ ui ],

                    c_uiDssMode_ReadOnly,

                    DSS_ADDRESS( p ) ) );

             fMean += p->m_value / uiListSize;

             dss_check( dss.objFree( pidList[ ui ], p ) );

       }

 

       dss_check( dss.transactionEnd() );

       return fMean;

}

 

// Выполнение действий процесса B

void   ProcessB( TDssKernelInterface & dss,

       const TDssID & id1,

       const TDssID & id2 ) {

 

       THeatCounter * p;

       dss_check( dss.objLoad( id1, c_uiDssMode_ReadWrite,

             DSS_ADDRESS( p ) ) );

       p->m_value -= 5.0;

       dss_check( dss.objSave( id1 ) );

       dss_check( dss.objFree( idt1, p ) );

 

       dss_check( dss.objLoad( id2, c_uiDssMode_ReadWrite,

             DSS_ADDRESS( p ) ) );

       p->m_value += 5.0;

       dss_check( dss.objSave( id2 ) );

       dss_check( dss.objFree( idt2, p ) );

}

 

В этом случае попытка процесса B записать объект, блокированный процессом A приведет только к выводу сообщения об ошибке, но не к прекращению действий процесса B. Процесс B попытается изменить значение объекта id2 даже, если ему не удалось изменить значение объекта id1. Т.е. блокировки, реализованные в Dss для обеспечения целостности данных, не смогут защитить процесс A.

            Практический опыт применения Dss показывает, что средства контроля ошибок должны прерывать последовательность действий процесса. Наилучшим способом для этого представляется использование исключительных ситуаций. Порождение исключения прерывает исполнение процесса. В то же время исключительные ситуации допускают реализацию гибких механизмов обработки ошибок.

            Для данного примера макрос dss_check можно реализовать следующим образом:

 

void   RaiseDBError( TDssDBError dbe ) {

       if( dbe )

             throw dbe;

}

#define dss_check( dbe ) RaiseDBError( dbe )

 

В этом случае процесс B прервет свою работу при попытке записи блокированного процессом A объекта.

            Применение исключительных ситуаций существенно облегчает реализацию транзакций:

 

void   RaiseDssError( TDssDBError dbe, const char * pcszFile, int iLine ) {

       if( dbe ) {

             char * pszMnemoName;

             char * pszReason;

             TDssUInt uiMnemoNameLength;

             TDssUInt uiReasonLength;

 

             IDssInfoGetErrorName( dbe, 0, 0, &uiMnemoNameLength,

                    0, 0, &uiReasonLength );

             pszMnemoName = new char[ ++uiMnemoNameLength ];

             pszReason = new char[ ++uiReasonLength ];

 

             IDssInfoGetErrorName( dbe, pszMnemoName, uiMnemoNameLength, 0,

                    pszReason, uiReasonLength, 0 );

 

             cerr << pcszFile << “, ” << iLine << “: ”

                    << pszMnemoName << “ (” << pszReason << “)” << endl;

 

             delete pszMnemoName;

             delete pszReason;

 

             throw dbe;

       }

}

#define dss_check(dbe) RaiseDssError(dbe, __FILE__, __LINE__)

 

// Процесс, обнуляющий атрибут m_value в указанном

// списке объектов типа THeatCounter

void   Process( TDssKernelInterface & dss,

       TDssUInt uiListSize, const TDssID * pidList ) {

       TDssThreadAttach threadattach( dss, c_uiDssRpLevel2 );

 

       dss_check( dss.transactionBegin() );

       try {

             THeatCounter * p;

             for( TDssUInt ui = 0; ui < uiListSize; ui++ ) {

                    dss_check( dss.objLoad( pidList[ ui ],

                           c_uiDssMode_ReadWrite,

                           DSS_ADDRESS( p ) ) );

                    p->m_value = 0;

                    dss_check( dss.objSave( pidList[ ui ] ) );

                    dss_check( dss.objFree( pidList[ ui ], p ) );

             }

 

             dss_check( dss.transactionEnd() );

       }

       catch( TDssDBError dbe ) {

             dss.transactionUndo();

       }

}

 

В данном примере возникновение любой ошибки приведет к автоматическому прерыванию действий процесса. Если ошибка произошла внутри транзакции, то произойдет откат транзакции.

            Еще одним преимуществом транзакций является автоматическая “раскрутка” стека и вызов деструкторов всех переменных. Так, если ошибка возникает при вызове метода transactionBegin, то будет порождено исключение и произойдет возврат из функции Process. При этом вызовется деструктор переменной threadattch, что приведет к вызову threadDetach и корректному переводу выделенной нити в категорию невыделенных нитей.

Управление кэшированием

            Работа Dss v.4.20 с файлами имеет две особенности, которые несколько снижают скорость чтения-записи данных. Во-первых, Dss v.4.20 выполняет операции чтения-записи не над всем объектом сразу, а над его атрибутами. Чтение-запись атрибута составляет одну операцию ввода-вывода. Во-вторых, при чтении-записи происходит перекодировка данных из машинно-зависимого в машинно-независимый формат.

            Для ускорения ввода-вывода в Dss v.4.20 применяется кэширование данных при чтении-записи. При чтении сначала проверяется имеется ли часть данных в кэш-буфере. Если имеется, то эта часть копируется в буфер-приемник, где перекодируется в машинно-зависимый формат. Если данные в кэш-буфере отсутствуют полностью или частично, то из файла считывается блок, размер которого равен размеру кэш-буфера. Затем данные пересылаются в буфер-приемник и перекодируются. При записи данные сначала помещаются в кэш-буфер. Там они перекодируются в машинно-независимый формат. Запись кэш-буфера происходит либо сразу после окончания операции ввода-вывода (записи объекта), либо откладывается. Запись откладывается, если включен режим кэширования записи. В этом режиме запись кэш-буфера происходит, если:

            - файл закрывается;

            - кэш-буфер полностью заполняется;

            - в кэш-буфер требуется загрузить новый блок данных.

            По умолчанию все файлы базы данных открываются без использования режима кэширования записи, а размер кэш-буфера равен 2048 байт. Метод:

 

TDssDBError  dbSetCacheMode( TDssUInt uiCacheSize, TDssBool bCacheOnWrite );

 

позволяет изменить установки кэширования по умолчанию. Параметр uiCacheSize задает размер кэш-буфера в байтах. Параметр bCacheOnWrite включает (c_bDssTrue) или не включает (c_bDssFalse) режим кэширования записи. Данный метод должен вызываться только однажды и обязательно перед первым открытием БД. В противном случае код возврата метода равен eDBErrorCacheModeSelected. Данный метод воздействует только на тот объект класса TDssKernelInterface, для которого он вызывался.

Dss и пространства имен

            Dss не поддерживает пространств имен (namespaces)[28]. Все типы схемы данных должны принадлежать глобальному пространству имен в C++.

Раздельная генерация вспомогательного кода

            Регистрация схемы данных в БД осуществляется утилитой dssreg. При этом генерируется вспомогательный код, который должен подключаться ко всем программам, использующим данную БД. В некоторых случаях использование сгенерированного утилитой dssreg вспомогательного кода невозможно. В этих случаях для получения вспомогательного кода необходимо воспользоваться утилитой dssscg.

Одновременная работа с несколькими базами данных

            Если программа должна работать с несколькими БД, имеющими разные схемы данных, то к ней должен быть подключен вспомогательный код для каждой БД. Сгенерированный утилитой dssreg вспомогательный код не удастся использовать по следующим причинам:

            - в нескольких C++ файлах содержатся переменные с одинаковыми именами, но имеющими разное значение. Как минимум, это переменные dss_type_methods, dss_type_info и dss_meth_TDssID, dss_scheme_TDssID, dss_info_TDssID;

            - в нескольких С++ файлах содержатся функции с одинаковыми именами. Как минимум, это функции dss_create_TDssID, dss_get_offs_TDssID, dss_destroy_TDssID.

При линковании нескольких файлов с вспомогательным кодом возникнут предупреждения линкера о повторном определении имен в нескольких файлах. Игнорирование этих предупреждений приведет, в лучшем случае, к ошибкам при открытии БД. В худшем случае возможен крах программы при открытии или работе с БД.

            Для преодоления этой ситуации необходимо разместить вспомогательный код в нескольких C++ файлах так, чтобы любое имя (переменной или функции) присутствовало только в одном файле.

Большое количество типов в схеме данных

            Утилита dssreg располагает весь вспомогательный код в одном C++ файле. При наличии большого числа типов в схеме данных компиляция вспомогательного кода может оказаться невозможной, поскольку некоторые компиляторы отказываются компилировать большие объемы C++ кода с диагностическими сообщениями вида: “Internal compiler error”. В подобных случаях необходимо разбить C++ код на несколько фрагментов и компилировать каждый фрагмент отдельно. Утилита dssscg позволяет осуществить эту операцию для вспомогательного кода.

Структура вспомогательного кода

            Для каждого хранимого типа T генерируется:

            - три вспомогательные функции: dss_create_T, dss_get_offs_T и dss_destroy_T;

            - вектор элементов типа TDssUserMethodInfo с именем dss_meth_T;

            - макрос dss_type_T;

            - вектор строк с именем dss_scheme_T;

            - переменная типа TDssUserTypeAdvInfo с именем dss_info_T.

Вспомогательные функции, векторы dss_meth_T, dss_scheme_T и переменная dss_info_T должны размещаться в C++ файле. Макрос dss_type_T должен размещаться в заголовочном C++ файле.

            Для всей схемы данных генерируются:

            - вектор элементов типа TDssUserTypeMethods, условно называемый type_methods;

            - вектор элементов типа TDssUserTypeInformation, условно называемый type_info.

            Утилита dssreg размещает все вспомогательные функции, векторы dss_meth_*, а также векторы type_methods и type_info в одном C++ файле. Векторам type_methods и type_info присваиваются имена dss_type_methods и dss_type_info.

Утилита dssscg

            Утилита dssscg предназначена для генерации вспомогательного кода по зарегистрированной в БД схеме данных.

            Утилита dssscg позволяет указывать расположение вспомогательных функций, векторов dss_meth_* и dss_scheme_*, переменных dss_info_*, макросов dss_type_*, векторов type_methods и type_info в конкретных C++ файлах. При использовании dssscg имена для векторов type_methods и type_info задаются программистом.

            Для dssscg подготавливается файл описания размещения вспомогательного кода. Файл описания - это текстовый файл, в котором специальными командами назначаются выходные C++ файлы и их содержимое. На вход dssscg подаются имена БД и файла описания. Из указанной БД считывается схема данных, и генерируется вспомогательный код в соответствии с указанным описанием.

Файл описания размещения вспомогательного кода

            Файл описания - это текстовый файл. Файл описания обрабатывается построчно. Каждая прочитанная строка анализируется. Если в строке содержится команда dssscg, то данная команда выполняется. В противном случае вся строка помещается в текущий выходной файл. Если выходной файл не назначен, то строка игнорируется. Одна строка может содержать только одну команду и ее параметры. Параметры команды должны указываться в той же строке.

            Команда:

 

            dsstypemethod <имя>

 

назначает имя для вектора type_methods.

            Команда:

 

            dsstypeinfo <имя>

 

назначает имя для вектора type_info.

            Команда:

 

            dssfile <имя_файла>

 

назначает очередной выходной файл. Прежний выходной файл (если назначен) закрывается. Новый выходной файл создается заново (если указанный файл существовал, то его содержимое теряется). В начало выходного файла помещаются директивы:

 

#include <dssi.hpp>

#include <dssmcrs.hpp>

 

            Команда:

 

            dsstype <имя_типа>

 

предписывает вывести в текущий выходной файл вспомогательные функции, векторы dss_meth_*, dss_scheme_* и переменную dss_info_* для указанного типа.

            Команда:

 

            dsshead

 

предписывает вывести в текущий выходной файл раскрытие макросов dss_type_* для всех типов схемы данных и прототипы векторов type_methods и type_info. Имена векторов type_methods и type_info берутся из параметров команд dsstypemethods и dsstypeinfo. Поэтому команды dsstypemethods и dsstypeinfo должны предшествовать команде dsshead.

            Команда:

 

            dsscode

 

предписывает вывести в текущий выходной файл векторы type_methods и type_info. Имена векторов type_methods и type_info берутся из параметров команд dsstypemethods и dsstypeinfo. Поэтому команды dsstypemethods и dsstypeinfo должны предшествовать команде dsscode.

Пример генерации вспомогательного кода для одновременной работы с двумя базами данных

            Пусть программа работает с четырьмя хранимыми типами: A, B, C и D. В схеме данных первой БД присутствуют типы A, B и C. В схеме данных второй БД присутствуют типы A, B и D. В обеих схемах данных имеются общие типы A, B и, неявно, TDssID.

            Для вспомогательного кода понадобятся следующие файлы:

            - db1dss.hpp, содержащий раскрытие макросов dss_type_* для типов первой БД;

            - db2dss.hpp, содержащий раскрытие макросов dss_type_* для типов второй БД;

            - db1dss.cpp, содержащий вспомогательные функции для типа C и вектора type_methods, type_info первой БД;

            - db2dss.cpp, содержащий вспомогательные функции для типа D и вектора type_methods, type_info второй БД;

            - common.cpp, содержащий вспомогательные функции для типов TDssID, A и B.

            Файл описания для первой БД, db1.txt, примет вид:

 

dsstypemethods db1_type_methods

dsstypeinfo db1_type_info

dssfile common.cpp

/* В этом файле содержатся типы, общие для обеих баз данных */

 

/* Загружаем описание типа A */

#include “a.hpp”

/* Загружаем описание типа B */

#include “b.hpp”

 

dsstype TDssID

dsstype A

dsstype B

 

dssfile db1dss.cpp

/* В этом файле содержатся типы, специфичные для первой БД */

 

/* Загружаем описание типа С */

#include “с.hpp”

 

dsstype C

dsscode

 

dssfile db1dss.hpp

/* В этом файле содержатся раскрытия макросов dss_type_* для первой БД */

 

dsshead

 

            Необходимо обратить внимание на строки, не содержащие команд dssscg. Они полностью переносятся в выходные файлы. Т.о., в выходной файл common.cpp помещаются директивы подключения заголовочных файлов с описаниями классов A и B. Без этих директив скомпилировать получившийся файл не удастся (по этой же причине приходится указывать параметр -codetop для утилиты dssreg).

            Файл описания для второй БД, db2.txt, примет вид:

 

dsstypemethods db2_type_methods

dsstypeinfo db2_type_info

dssfile db2dss.cpp

/* В этом файле содержатся типы, специфичные для второй БД */

 

/* Загружаем описание типа D */

#include “d.hpp”

 

dsstype D

dsscode

 

dssfile db2dss.hpp

/* В этом файле содержатся раскрытия макросов dss_type_* для второй БД */

 

dsshead

 

            Необходимо запустить утилиту dssreg для регистрации схем данных и утилиту dssscg для генерации вспомогательного кода. Для облегчения этого процесса можно создать следующий командный файл (Win32, OS/2):

 

rem Проверяем существование БД

if not exist db1.di dsscreat -db db1

if not exist db2.di dsscreat -db db2

rem Регистрируем схемы БД

dssreg -db db1 -desc db1.ddl -code t -head t

dssreg -db db2 -desc db2.ddl -code t -head t

del t

rem Раздельно генерируем вспомогательный код

dssscg -db db1 -codedesc db1.txt

dssscg -db db2 -codedesc db2.txt

 

            После выполнения этого командного файла в проект программы подключаются файлы db1dss.cpp, db2dss.cpp и common.cpp. При открытии БД в качестве аргументов ptypeInfo и ptypeMethods указываются имена db1_type_info (db2_type_info) и db1_type_methods (db2_type_methods):

 

#include <dssi.hpp>

 

#include “db1dss.hpp”

#include “db2dss.hpp”

 

...

 

TDssKernelInterface db1;

dss_check( db1.dbOpen( “db1”, c_uiDssMode_ReadWrite,

       c_uiDssRpLevel2, db1_type_info, db1_type_methods ) );

TDssKernelInterface db2;

dss_check( db2.dbOpen( “db2”, c_uiDssMode_ReadWrite,

       c_uiDssRpLevel2, db2_type_info, db2_type_methods ) );

 

Примечание. В данном примере в заголовочных файлах а.hpp и b.hpp можно подключать любой из сгенерированных заголовочных файлов (db1dss.hpp или db2dss.hpp). В заголовочном файле c.hpp необходимо подключать заголовочный файл db1dss.hpp, а в d.hpp - db2dss.hpp.

Пример генерации вспомогательного кода для сложных схем данных

            Пусть программа работает с большим количеством хранимых типов: T1, T2, ..., TN. Компиляция вспомогательного кода, расположенного в одном C++ файле завершается неудачно с сообщением “Internal compiler error”. Для преодоления данной проблемы разобъем вспомогательный код по следующим файлам:

            - dbinit.hpp, содержащий раскрытие макросов dss_type_* для типов схемы данных;

            - dbinit1.cpp, содержащий вспомогательные функции для типов T1...TK;

            - dbinit2.cpp, содержащий вспомогательные функции для типов TK+1...TN;

            - dbinit.cpp, содержащий вспомогательные функции для типа TDssID и вектора dss_type_info, dss_type_methods.

            Файл описания, dbinit.txt, примет вид:

 

dsstypemethods dss_type_methods

dsstypeinfo dss_type_info

dssfile dbinit.hpp

/* В этом файле макросы dss_type_* */

 

dsshead

 

dssfile dbinit1.cpp

/* В этом файле содержится первая половина типов схемы данных */

 

/* Подключаем описания типов */

#include <T1.hpp>

...

#include <TK.hpp>

 

/* Располагаем вспомогательный код */

dsstype T1

...

dsstype TK

 

dssfile dbinit2.cpp

/* В этом файле содержится вторая половина типов схемы данных */

 

/* Подключаем описания типов */

#include <TK+1.hpp>

...

#include <TN.hpp>

 

/* Располагаем вспомогательный код */

dsstype TK+1

...

dsstype TN

 

dssfile dbinit.cpp

/* В этом файле содержится вспомогательный код для типа TDssID и результирующие векторы dss_type_info, dss_type_methods */

 

dsstype TDssID

 

dsscode

 

            В файле dbinit.cpp нет подключения заголовочных файлов с описаниями типов, входящих в схему данных. Количество описаний типов в отдельных файлах (т.к. dbinit1.cpp, dbinit2.cpp) контролируется программистом. Благодаря этому можно разместить вспомогательный код в нужном количестве небольших C++ файлов и избежать проблем с компиляцией.

            После создания файла описания необходимо запустить утилиту dssreg для регистрации схем данных и утилиту dssscg для генерации вспомогательного кода. Для облегчения этого процесса можно создать следующий командный файл (Win32, OS/2):

 

rem Проверяем существование БД

if not exist db.di dsscreat -db db

rem Регистрируем схему БД

dssreg -db db -desc db.ddl -code t -head t

del t

rem Генерируем вспомогательный код

dssscg -db db -codedesc dbinit.txt

 

            После выполнения этого командного файла в проект программы подключаются файлы dbinit.cpp, dbinit1.cpp и dbinit2.cpp. При открытии БД в качестве аргументов ptypeInfo и ptypeMethods указываются имена dss_type_info и dss_type_methods:

 

#include <dssi.hpp>

 

#include “dbinit.hpp”

 

...

 

TDssKernelInterface db;

dss_check( db1.dbOpen( “db”, c_uiDssMode_ReadWrite,

       c_uiDssRpLevel2, dss_type_info, dss_type_methods ) );


Пособие по языку DssDDL

Назначение

            В каждой базе данных Dss v.4.20 хранится описание схемы данных - список типов и их взаимосвязей. Порядок типов в этом списке важен: тип может ссылаться только на типы, которые расположены в списке перед ним. Самым первым типом в списке всегда является тип TDssID. Схема данных хранится во внутреннем формате Dss. Создается и изменяется схема утилитой dssreg. Для dssreg схема данных подготавливается на специальном языке DssDDL. DssDDL - это язык деклараций, близкий к языку C++, но имеющий некоторые отличия.

Механизм формирования схемы данных

            После создания базы данных в схеме находится только один тип - TDssID. В дальнейшем схема дополняется и изменяется при помощи описаний на DssDDL следующим образом:

            - из базы данных считывается существующая схема. Эта схема называется старой схемой. Все типы, входящие в старую схему заносятся в т.н. исходный список типов. Порядок следования типов в исходном списке такой же, как и в старой схеме;

            - выполняются операторы переименования, указанные в описании на DssDDL. В ходе выполнения этих операторов в исходном списке модифицируются описания типов (имена типов и/или имена атрибутов). Порядок следования типов при этом не изменяется;

            - анализируются декларации нормальных типов и клонов в описании на DssDDL. При завершении очередной декларации, сформированное описание типа помещается в т.н. выходной список типов. Порядок следования типов в выходном списке будет соответствовать порядку деклараций типов на DssDDL. При занесении типа в выходной список проверяется, имеется ли тип с таким именем в исходном списке. Если имеется, тогда:    

            1) для типа сохраняется старый идентификатор типа;

            2) просматриваются списки атрибутов в старом и новом описании типа. Если атрибут оказывается в обоих описаниях, то он считается подтвержденным.

Если тип не найден в исходном списке, то ему присваивается уникальный идентификатор. Все атрибуты этого типа считаются неподтвержденными;

            - просматриваются все объекты базы данных. Если объект принадлежит типу, отсутствующему в выходном списке типов, то он удаляется из базы данных. Если объект принадлежит типу, присутствующему в выходном списке типов, то значение объекта переписывается следующим образом:

            1) значения подтвержденных атрибутов просто переписываются;

            2) неподтвержденные атрибуты инициализируются начальными значениями (для всех основных типов - нули, для типа TDssID - пара чисел 0, 0xFFFFFFFF).

Если для подтвержденного атрибута требуется увеличение размера вектора, то добавляемые элементы вектора инициализируются начальными значениями. В случае необходимости осуществляется преобразование значений подтвержденных атрибутов к новому типу;

            - формируется новая схема данных путем последовательной записи элементов выходного списка типов;

            - генерируется C++ код, необходимый каждой программе, работающей с базой данных.

Примечание. Поскольку тип TDssID никогда не декларируется в описании на DssDDL, то описание этого типа сразу же переносится из исходного списка в результирующий, а его атрибуты считаются подтвержденными.

            Такой механизм формирования схемы данных приводит к следующим особенностям:

            - все типы, которые должны входить в новую схему, обязательно должны декларироваться в описании на DssDDL. Незадекларированные типы не переносятся из исходного списка в выходной список и не попадают в новую схему;

            - операторы переименования должны указываться до деклараций. Это необходимо для корректного формирования исходного списка типов. Оператор переименования, следующий за декларацией в описании на DssDDL, воспринимается транслятором DssDDL как синтаксическая ошибка.

Особенности объектной модели Dss и транслятора DssDDL

Вложенные типы

            Dss v.4.20 допускает применение вложенных типов. Окончательное имя вложенного типа (т.е. имя, под которым тип регистрируется в базе данных) формируется из имени включающего типа, символа уточнения доступа «::» и собственного имени вложенного типа:

 

struct A { // A

       struct B { // A::B

             struct C { // A::B::C

             };

       };

};

 

Глубина вложенности типов не ограничена. Окончательные имена типов должны быть уникальными.

            При работе с вложенными типами возникают следующие особенности:

            - в схеме данных вложенные типы перечисляются в порядке, обратном их вложенности, т.е. сначала A::B::C, затем A::B и A;

            - ссылаться на вложенный тип необходимо с помощью окончательного имени типа:

 

struct A {

             ...

};

struct B {

       struct A {

             ...

       };

       A m_a; // тип A, а не B::A

       B::A m_a2; // тип B::A

};

Ограничения на множественное наследование

            На множественное наследование существуют следующие ограничения: непосредственный базовый тип не может входить в список доступных базовых типов для всех остальных непосредственных базовых типов. Если применяется виртуальное наследование, то непосредственный невиртуальный базовый тип не может входить в список базовых типов всех остальных непосредственных базовых типов как виртуальный базовый тип.

            Ограничения обусловлены кодом, который генерируется после формирования схемы данных и который в этих случаях C++ считает неоднозначным.

            Пример 1.

 

// C++

class A {

};

class B : public A {

};

class C : A, public B {

};

 

            В класс C класс A входит как компоненты C::A и C::B::A. Причем C::A недоступна (используется не public наследование). Следующий код компилируется, но работает неверно:

 

C c;

B * pb = &c; // C::B

A * pa = &c; // C::B::A, а надо C::A

 

            Пример 2.

 

// C++

class A {

};

class B : public A {

};

class C : public A, public B {

};

 

            Теперь этот код не будет скомпилирован:

 

C c;

B * pb = &c; // C::B

A * pa = &c; // C::B::A или C::A?

 

            Пример 3.

 

// C++

class A {

};

class B : virtual A {

};

class C : public A, public B {

};

 

            В этом случае в C есть виртуальная и, казалось бы, недоступная компонента C::B::A и доступная компонента C::A. Но следующий код не будет скомпилирован, т.к. виртуальная компонента класса в C++ всегда доступна при преобразовании типа:

 

C c;

B * pb = &c; // C::B

A * pa = &c; // C::B::A или C::A?

 

            Выходы из этих ситуаций:

            - не применение подобных конструкций вообще, поскольку это порождает много сложностей и в обычном C++ без использования Dss;

            - введение промежуточных классов так, чтобы формально ограничение соблюдалось:

 

// C++

class A {

};

class B : public A {

};

class A0: public A {

};

class C : public A0, public B {

};

 

тогда получившийся код:

 

C c;

B * pb = &c; // C::B

A0 * pa = &c; // C::A0

 

будет компилироваться и исполняться.

Особенности подтверждения атрибутов

            В схеме данных при описании типа хранится полный список атрибутов. Т.е. помимо собственных атрибутов типа хранятся и все унаследованные атрибуты:

 

struct A {

       int m_a;

};

struct B : public A {

       int m_b;

};

struct C {

       int m_c;

};

struct D : public B, public C {

       int m_d;

};

 

Список атрибутов для D = { D::B::A::m_a, D::B::m_b, D::C::m_c, D::m_d }. При поиске подтвержденных атрибутов происходит сравнение уточненных имен атрибутов в полных списках. Если в новом описании схемы будет указано:

 

struct C1 : public C {

};

struct D : public B, public C1 {

       int m_d;

};

 

то полный список атрибутов для D = { D::B::A::m_a, D::B::m_b, D::C1::C::m_c, D::m_d }. Очевидно, что D::C::m_c не подтверждается (D::C1::C::m_c != D::C::m_c). Значит он будет потерян. В ряде случаев такой исход нежелателен. Например, если C1 вводится для преодоления ограничений на множественное наследование:

 

struct C {

       int m_c;

};

struct A : public C { // !!!

       int m_a;

};

struct B : public A {

       int m_b;

};

struct C1 : public C {

};

struct D : public B, public C1 {

       int m_d;

};

 

Для того, чтобы не терять значения атрибутов, применяется следующий прием: в уточненное имя атрибута при поиске подтвержденных атрибутов включаются либо старые типы, либо новые типы, имеющие собственные атрибуты. Т.к. в данном случае C1 является новым типом и не вводит собственных атрибутов, то вместо имени D::C1::C::m_c будет сформировано имя D::C::m_c и атрибут будет подтвержден.

            Использовать эту особенность необходимо очень осторожно - легко можно продублировать значения старых атрибутов в новых атрибутах:

 

// В старом описании

struct A {

       int m_a;

};

struct B : public A {

       int m_b;

};

 

// В новом описании

struct A {

       int m_a;

};

struct A1 : public A {

};

struct A2 : public A {

};

struct B : public A1, public A2 {

       int m_b;

};

 

В этом случае новые атрибуты B::A1::A::m_a и B::A2::A::m_a получат значение старого атрибута B::A::m_a.

            Ïри сравнении атрибутов помимо уточненного имени учитывается и тип, т.е. атрибуты с одинаковым уточненным именем, но разного типа, считаются разными. Однако, если старый и новый атрибуты принадлежат к стандартным типам Dss (char, unsigned char, wchar_t, short, unsigned short, int, unsigned int, float, double), то считается, что это один и тот же атрибут, но с измененным типом. При перезаписи объектов утилита dssreg автоматически преобразует старое значение атрибута к новому типу.

            Для построения полного списка атрибутов применяется рекурсивный обход графа наследования. Атрибуты добавляются в список либо при достижении конечных вершин (типов, не имеющих базовых типов), либо после возврата из обхода базовых типов. Вершины, соответствующие виртуальным базовым типам, включаются в обход только один раз.

Формат описания на DssDDL

            Описание схемы данных должно находиться в одном текстовом файле, называемом файлом описания или ddl-файлом. Описание состоит из двух необязательных частей - операторов переименования и деклараций. Операторы переименования должны предшествовать декларациям.

Комментарии

            Комментарий начинается с символа «//» и заканчивается в конце строки. При анализе описания комментарии пропускаются.

Зарезервированные слова

            В DssDDL применяются следующие зарезервированные слова:

rename

type

attr

struct

clone

char

wchar_t

short

int

float

double

method

abstract

unsigned

TDssChar

TDssUChar

TDssWChar

TDssShort

TDssUShort

TDssInt

TDssUInt

TDssSingle

TDssDouble

 

Данные слова не могут использоваться в качестве имен.

Примечание. Ключевые слова TDssChar, TDssUChar, TDssWChar, TDssShort, TDssUShort, TDssInt, TDssUInt, TDssSingle, TDssDouble используются в качестве псевдонимов для имен типов char, unsigned char, wchar_t, short, unsigned short, int, unsigned int, float, double.

Оператор переименования типа

            Формат:

            rename type старое_имя новое_имя;

            Старое имя должно быть определено в старой схеме. Новое имя должно быть уникальным в старой схеме. После успешного переименования старое имя перестает существовать.

            Примеры:

 

rename type A B;

rename type B::C D;

rename type E D::G;

rename type A D::A; // Ошибка: A уже не существует

 

Примечания:

            1) результирующее имя может быть произвольным:

rename A D::G::E;

Этот оператор выполнится, даже если нет типов D и D::G;

            2) îïåðàòîðû ïåðåèìåíîâàíèÿ òèïîâ äîëæíû óêàçûâàòüñÿ ïåðåä äåêëàðàöèÿìè òèïîâ è êëîíîâ.

Оператор переименования атрибута

            Формат:

            rename attr тип::старое_имя новое_имя;

            Старое имя должно быть определено в указанном типе в старой схеме. Новое имя должно быть уникальным в типе в старой схеме. После успешного переименования старое имя перестает существовать.

            Примеры:

 

rename attr A::m_a _m_a;

rename attr A::B::m_b _m_b;

rename attr A::m_c _m_a; // _m_a уже определено!

rename attr A::m_a m_ch; // m_a уже не существует!

 

Примечание. Îïåðàòîðû ïåðåèìåíîâàíèÿ àòðèáóòîâ äîëæíû óêàçûâàòüñÿ ïåðåä äåêëàðàöèÿìè òèïîâ è êëîíîâ.

Декларация нормального типа

            Формат:

            [abstract] struct имя [: <базовый тип> [,...]] {

          [<декларации нормальных типов>]

          [<декларации клонов>]

          [<декларации атрибутов>]

          [<декларации методов>]

     };

            Имя должно быть уникальным в новой схеме. Блоки внутри декларации могут располагаться в любом порядке и указываться в любом количестве.

Декларация базового типа

            Формат:

            [public][virtual] имя

            Имя должно быть определено в новой схеме. Порядок следования ключевых слов public и virtual не существенен. Если присутствует ключевое слово public, то декларируется доступный базовый тип, в противном случае - недоступный. Если присутствует ключевое слово virtual, то указывается виртуальное наследование, в противном случае - невиртуальное.

Декларация атрибутов

            Формат:

            имя_типа имя[[размерность][[размерность][...]]][,

имя[[размерность][[размерность][...]]][, ...]];

            Имя типа должно быть определено в новой схеме, либо должно быть именем основного типа {char, wchar_t, short, int, float, double, unsigned char, unsigned short, unsigned int, TDssID}. Имена атрибутов должны быть уникальными среди собственных атрибутов типа.

Примечание. Массив в Dss рассматривается как вектор, размерность которого равна произведению размерностей. Поэтому, если в исходном описании указывалось:

int m_i[ 3 ][ 10 ];

а в новом описании:

int m_i[ 3 ][ 12 ];

то это значит, что будут добавлены 6 новых элементов в конец атрибута m_i, а не по два новых элемента в каждую строку.

Декларация метода

            Формат:

            method имя имя_реализации;

            Имя метода должно быть уникальным в рамках типа. Имя реализации - это либо имя статического метода C++ класса, либо имя C++ функции.

            Примеры:

 

method create TClass::create;

method destroy _Destroy;

Декларация клона

            Формат:

            clone исходный_тип имя_клона;

            Имя исходного типа должно быть определено в новой схеме. Исходный тип должен быть нормальным типом. Имя клона должно быть уникальным в новой схеме.

            Примеры:

 

clone A AClone;

clone B::C::D _D;

Примеры деклараций

abstract struct TReference {

       int m_iRefCount;

 

       method init TReference::init;

       method inc TReference::inc;

       method dec TReference::dec;

};

 

struct TMnemoObject :

       public virtual TReference {

 

       TDssID m_idMnemoList;

 

       method create TMnemoObject::create;

       method destroy TMnemoObject::destroy;

       method viewTypeContents StdViewTypeContents;

};

 

struct TAgent :

       public TMnemoObject {

 

       struct TAttribute {

             int m_iType;

             char m_szName[ 100 ];

             TDssID m_idNext;

       };

};

 

clone TAgent TAgentStove;

clone TAgent TAgentPipeline;

 

Связывание DssDDL и C++

            DssDDL разработан в предположении, что всем типам, описываемым на DssDDL, соответствуют типы на C++. Т.е. сначала разрабатываются типы на C++, затем выясняется, какие атрибуты этих типов должны храниться в базе данных, к каким методам этих типов можно обращаться через базу данных. Затем подготавливаются соответствующие описания на DssDDL, и в базе данных формируется нужная схема данных. В процессе формирования схемы генерируется C++ код, который линкуется ко всем программам, использующим базу данных.

Генерируемый C++ код

            Для каждого нормального типа, зарегистрированного в базе данных, создаются три функции следующего вида:

 

void * dss_create_T( unsigned int ) {

       return new T();

}

void   dss_destroy_T( T * p ) {

       delete p;

}

char **      dss_get_offs_T( T * p ) {

       static char * offs[ N ];

       offs[ 0 ] = (char *)(B1 *)p;

       offs[ 1 ] = (char *)(B2 *)p;

       ...

       offs[ K ] = (char *)(BK *)p;

       offs[ K + 1 ] = (char *)&p->attr1;

       ...

       offs[ N - 1 ] = (char *)&p->attrM;

 

       return offs;

}

 

где

            K = количество непосредственных базовых типов;

            M = количество собственных атрибутов;

            N = K+M

Данные функции используются Dss для загрузки, выгрузки объектов и преобразования типов. Перед загрузкой объекта Dss вызывает функцию dss_create_T для создания буфера в ОП. После этого производится загрузка атрибутов. Смещения атрибутов определяются при помощи функции dss_get_offs. Данная функция вызывается не более одного раза для каждого типа. При выгрузке объекта из ОП вызывается функция dss_destroy_T.

Примечания:

            1) так как C++ объект создается до того, как будут загружены его атрибуты, то нельзя использовать значения атрибутов в конструкторе;

            2) каждый C++ класс, соответствующий нормальному типу в базе данных, должен иметь конструктор по умолчанию.

            Для каждого нормального типа генерируются три вспомогательные переменные:

            - вектор элементов типа TDssUserMethodInfo с именем dss_meth_T, содержащий описания именованных методов;

            - вектор строк с именем dss_scheme_T, содержащий фрагмент схемы данных, относящийся к типу T. Данный вектор используется при открытии БД для проверки адекватности схемы данных БД и схемы данных программы;

            - переменная типа TDssUserTypeAdvInfo с именем dss_info_T, содержащая указатели на вспомогательные функции dss_get_offs_T, dss_create_T, dss_destroy_T, указатель на вектор dss_scheme_T и размер объекта данного типа в оперативной памяти.

            Для того, чтобы передать Dss указатели на элементы вспомогательного кода, утилита dssreg генерирует вектор элементов типа TDssUserTypeInformation под именем dss_type_info. Так, для схемы:

 

struct A {

       int m_a;

};

struct D {

       int m_d;

};

 

будет сгенерирован следующий вектор:

 

TDssUserTypeInformation dss_type_info[] = {

       {      “TDssID”,

             (TDssPFnGetOffs) 0,

             &dss_info_TDssID

       },

       {      “A”,

             (TDssPFnGetOffs) 0,

             &dss_info_A

       },

       {      “B”,

             (TDssPFnGetOffs) 0

             &dss_info_B

       },

       { 0, 0, 0 }

};

 

            Методы типов реализуются статическими методами C++ классов или функциями, имена которых указываются в описании методов. Для того, чтобы Dss могла возвращать указатели на методы по запросам пользователя, указатели методов типа помещаются в вектор dss_meth_T. Указатели на все векторы dss_meth_* заносятся в вектор элементов TDssUserTypeMethods с именем dss_type_methods (для утилиты dssreg).

            Из-за того, что в C++ применяется ограничение доступа к членам класса и базовым типам (при помощи private или protected), то для доступа ко всем атрибутам класса необходимо, чтобы функции dss_*_T объявлялись дружественными для класса T. Для этого в описании класса необходимо использовать макрос dss_type( T ). Параметр содержит мнемо-имя класса. Мнемо-имя - это уточненное имя класса, в котором все символы ‘:’ заменены символом ‘_’ (подчеркиванием):

 

class TReference {

       dss_type( TReference );

 

       protected :

             dss_attr int m_iRefCount;

 

             dss_meth void init();

             dss_meth int inc();

             dss_meth int dec();

 

       public :

             virtual void dump( ostream & o ) = 0;

};

 

class TMnemoObject :

       public virtual TReference {

       dss_type( TMnemoObject );

 

       protected :

             dss_attr TDssID m_idMnemoList;

 

             dss_meth TDssID create();

             dss_meth void destroy( const TDssID & );

       public :

             virtual void dump( ostream & o );

};

 

class TAgent :

       public TMnemoObject {

       dss_type( TAgent );

 

       protected :

             struct TAttribute {

                    dss_type( TAgent__TAttribute );

 

                    dss_attr int m_iType;

                    dss_attr char m_szName[ 100 ];

                    dss_attr TDssID m_idNext;

             };

 

             char m_transient[ 100 ];

 

       public :

             virtual void dump( ostream & o );

};

 

            При формировании схемы данных генерируется специальный код для раскрытия макросов dss_type, dss_attr, dss_meth:

 

#define dss_type( T ) dss_type_##T

#define dss_attr

#define dss_meth static

 

#define dss_type_TReference\

       friend void * dss_create_TReference( unsigned int );\

       friend void  dss_destroy_TReference(\

             TReference * p );\

       friend char **      dss_get_offs_TReference(\

             TReference * p )

 

#define dss_type_TMnemoObject\

       friend void * dss_create_TMnemoObject(\

             unsigned int );\

       friend void  dss_destroy_TMnemoObject(\

             TMnemoObject * p );\

       friend char **      dss_get_offs_TMnemoObject(\

             TMnemoObject * p )

 

#define dss_type_TAgent__TAttribute\

       friend void * dss_create_TAgent__TAttribute(\

             unsigned int );\

       friend void  dss_destroy_TAgent__TAttribute(\

             TAgent::TAttribute * p );\

       friend char **      dss_get_offs_TAgent__TAttribute(\

             TAgent::TAttribute * p )

 

#define dss_type_TAgent\

       friend void * dss_create_TAgent( unsigned int );\

       friend void  dss_destroy_TAgent(\

             TAgent * p );\

       friend char **      dss_get_offs_TAgent(\

             TAgent * p )

 

Примечание. В настоящее время нет необходимости использовать макросы dss_attr и dss_meth. Однако в дальнейшем предполагается создание транслятора описаний с C++ на DssDDL. В этом случае атрибуты и методы, помеченные dss_attr и dss_meth автоматически будут переноситься в описание на DssDDL.

            Т.о., после трансляции следующего описания на DssDDL:

 

abstract struct TReference {

       int m_iRefCount;

 

       method init TReference::init;

       method inc TReference::inc;

       method dec TReference::dec;

};

 

struct TMnemoObject :

       public virtual TReference {

 

       TDssID m_idMnemoList;

 

       method create TMnemoObject::create;

       method destroy TMnemoObject::destroy;

       method viewTypeContents StdViewTypeContents;

};

 

struct TAgent :

       public TMnemoObject {

 

       struct TAttribute {

             int m_iType;

             char m_szName[ 100 ];

             TDssID m_idNext;

       };

};

 

clone TAgent TAgentStove;

clone TAgent TAgentPipeline;

 

будет сгенерирован следующий C++ код:

 

// Сгенерированный заголовочный файл

#define dss_type( T ) dss_type_##T

#define dss_attr

#define dss_meth static

 

#define dss_type_TReference\

       friend void * dss_create_TReference( unsigned int );\

       friend void  dss_destroy_TReference(\

             TReference * p );\

       friend char **      dss_get_offs_TReference(\

             TReference * p )

 

#define dss_type_TMnemoObject\

       friend void * dss_create_TMnemoObject(\

             unsigned int );\

       friend void  dss_destroy_TMnemoObject(\

             TMnemoObject * p );\

       friend char **      dss_get_offs_TMnemoObject(\

             TMnemoObject * p )

 

#define dss_type_TAgent__TAttribute\

       friend void * dss_create_TAgent__TAttribute(\

             unsigned int );\

       friend void  dss_destroy_TAgent__TAttribute(\

             TAgent::TAttribute * p );\

       friend char **      dss_get_offs_TAgent__TAttribute(\

             TAgent::TAttribute * p )

 

#define dss_type_TAgent\

       friend void * dss_create_TAgent( unsigned int );\

       friend void  dss_destroy_TAgent(\

             TAgent * p );\

       friend char **      dss_get_offs_TAgent(\

             TAgent * p )

 

// Сгенерированный C++ файл

void * dss_create_TDssID( unsigned int ) {

       return new TDssID();

}

void   dss_destroy_TDssID( TDssID * p ) {

       delete p;

}

char **      dss_get_offs_TDssID( TDssID * p ) {

       static char * offs[ 2 ];

       offs[ 0 ] = (char *)&p->m_file;

       offs[ 1 ] = (char *)&p->m_value;

       return offs;

}

TDssUserMethodInfo dss_methods_TDssID[] = {

       { 0, 0 }

};

const char * dss_scheme_TDssID[] = {

       “type TDssID”,

       “attr int 0 m_file”,

       “attr int 0 m_value”,

       0

};

TDssUserTypeAdvInfo dss_info_TDssID = {

       (TDssPFnGetOffs) dss_get_offs_TDssID,

       (TDssPFnCreate) dss_create_TDssID,

       (TDssPFnDestroy) dss_destroy_TDssID,

       dss_scheme_TDssID,

       sizeof( TDssID )

};

 

void * dss_create_TReference( unsigned int ) {

       return 0;

}

void   dss_destroy_TReference( TReference * p ) {

}

char **      dss_get_offs_TReference( TReference * p ) {

       static char * offs[ 1 ];

       offs[ 0 ] = (char *)&p->m_iRefCount;

       return offs;

}

TDssUserMethodInfo dss_methods_TReference[] = {

       { “init”, (TDssPFnMethod) TRefetence::init },

       { “inc”, (TDssPFnMethod) TRefetence::inc },

       { “dec”, (TDssPFnMethod) TRefetence::dec },

       { 0, 0 }

};

const char * dss_scheme_TReference[] = {

       “abstract type TReference”,

       “attr int 0 m_iRefCount”,

       “attr int 0 m_value”,

       “method init TReference::init”,

       “method inc TReference::inc”,

       “method dec TReference::dec”,

       0

};

TDssUserTypeAdvInfo dss_info_TReference = {

       (TDssPFnGetOffs) dss_get_offs_TReference,

       (TDssPFnCreate) dss_create_TReference,

       (TDssPFnDestroy) dss_destroy_TReference,

       dss_scheme_TReference,

       sizeof( TReference )

};

 

void * dss_create_TMnemoObject( unsigned int ) {

       return new TMnemoObject();

}

void   dss_destroy_TMnemoObject( TMnemoObject * p ) {

       delete p;

}

char **      dss_get_offs_TMnemoObject( TMnemoObject * p ) {

       static char * offs[ 2 ];

       offs[ 0 ] = (char *)(TReference *)p;

       offs[ 1 ] = (char *)&p->m_idMnemoList;

       return offs;

}

TDssUserMethodInfo dss_methods_TMnemoObject[] = {

       { “create”, (TDssPFnMethod) TMnemoObject::create },

       { “destroy”, (TDssPFnMethod) TMnemoObject::destroy },

       { “viewTypeContents”,

             (TDssPFnMethod) StdViewTypeContents },

       { 0, 0 }

};

const char * dss_scheme_TMnemoObject[] = {

       “type TMnemoObject”,

       “base virtual public TReference”,

       “attr TDssID 0 m_idMnemoList”,

       “method create TMnemoObject::create”,

       “method destroy TMnemoObject::destroy”

       “method viewTypeContents StdViewTypeContents”

       0

};

TDssUserTypeAdvInfo dss_info_TMnemoObject = {

       (TDssPFnGetOffs) dss_get_offs_TMnemoObject,

       (TDssPFnCreate) dss_create_TMnemoObject,

       (TDssPFnDestroy) dss_destroy_TMnemoObject,

       dss_scheme_TMnemoObject,

       sizeof( TMnemoObject )

};

 

void * dss_create_TAgent__TAttribute( unsigned int ) {

       return new TAgent::TAttribute();

}

void   dss_destroy_TAgent__TAttribute( TAgent::TAttribute * p ) {

       delete p;

}

char **      dss_get_offs_TAgent__TAttribute(

       TAgent::TAttribute * p ) {

       static char * offs[ 3 ];

       offs[ 0 ] = (char *)&p->m_iType;

       offs[ 1 ] = (char *)&p->m_szName;

       offs[ 2 ] = (char *)&p->m_idNext;

       return offs;

}

TDssUserMethodInfo dss_methods_TAgent__TAttribute[] = {

       { 0, 0 }

};

const char * dss_scheme_TAgent__TAttribute[] = {

       “type TAgent::TAttribute”,

       “attr int 0 m_iType”,

       “attr char 30 m_szName”,

       “attr TDssID m_idNext”,

       0

};

TDssUserTypeAdvInfo dss_info_TAgent__TAttribute = {

       (TDssPFnGetOffs) dss_get_offs_TAgent__TAttribute,

       (TDssPFnCreate) dss_create_TAgent__TAttribute,

       (TDssPFnDestroy) dss_destroy_TAgent__TAttribute,

       dss_scheme_TAgent__TAttribute,

       sizeof( TAgent::TAttribute )

};

 

void * dss_create_TAgent( unsigned int ) {

       return new TAgent();

}

void   dss_destroy_TAgent( TAgent * p ) {

       delete p;

}

char **      dss_get_offs_TAgent(

       TAgent * p ) {

       static char * offs[ 1 ];

       offs[ 0 ] = (char *)(TMnemoObject *)p;

       return offs;

}

TDssUserMethodInfo dss_methods_TAgent[] = {

       { 0, 0 }

};

const char * dss_scheme_TAgent[] = {

       “type TAgent”,

       “base public TMnemoObject”,

       0

};

TDssUserTypeAdvInfo dss_info_TAgent = {

       (TDssPFnGetOffs) dss_get_offs_TAgent,

       (TDssPFnCreate) dss_create_TAgent,

       (TDssPFnDestroy) dss_destroy_TAgent,

       dss_scheme_TAgent,

       sizeof( TAgent )

};

 

TDssUserTypeMethods dss_type_methods[] = {

       { “TDssID”, dss_methods_TDssID },

       { “TReference”, dss_methods_TReference },

       { “TMnemoObject”, dss_methods_TMnemoObject },

       { “TAgent::TAttribute”,

             dss_methods_TAgent__TAttribute },

       { “TAgent”, dss_methods_TAgent },

       { 0, 0 }

};

 

TDssUserTypeInformation dss_type_info[] = {

       {      “TDssID”,

             (TDssPFnGetOffs) 0,

             &dss_info_TDssID

       },

       {      “TReference”,

             (TDssPFnGetOffs) 0,

             &dss_info_TReference

       },

       {      “TMnemoObject”,

             (TDssPFnGetOffs) 0,

             &dss_info_TMnemoObject

       },

       {      “TAgent::TAttribute”,

             (TDssPFnGetOffs) 0,

             &dss_info_TAgent__TAttribute

       },

       {      “TAgent”,

             (TDssPFnGetOffs) 0,

             &dss_info_TAgent

       },

       { 0, 0, 0 }

};

 

Примечание. При генерации С++ кода используются макросы, описанные в файле dssmcrs.hpp. Код, приведенный выше, получается после применения препроцессора C++.

Применение описаний на DssDDL и генерируемого кода в реальных задачах

            В реальных задачах необходимо помнить, что при генерации кода результатом являются два файла: один с раскрытием макросов dss_type*, а второй с функциями dss_*_T. Без первого файла нельзя описывать классы на C++, без второго - работать с базой данных. Поэтому необходимо сразу определить имена файлов, в которые будет помещаться сгенерированный код. Пусть для макросов dss_type* используется файл DBINIT\H\dbinit.hpp, а для функций dss_*_T - файл DBINIT\dbinit.cpp.

            Тогда в каждый заголовочный файл, в котором описываются C++ классы, необходимо подключать файл DBINIT\H\dbinit.hpp. Например, таким образом:

 

#include <DBINIT/H/dbinit.hpp>

 

            Файл DBINIT\dbinit.cpp необходимо подключать ко всем программам, работающим с базой данных. Однако в сгенерированном коде нет подключения заголовочных файлов, в которых определяются классы. Подключение этих файлов необходимо вставлять в DBINI\dbinit.cpp после каждого обновления схемы. Облегчить эту задачу можно, создав вспомогательный файл DBINIT\codetop.cpp, в котором будут подключаться все необходимые заголовочные файлы. Программе dssreg имя DBINIT\codetop.cpp указывается в параметре ‑codetop:

 

dssreg ‑db DB\db ‑desc DBINIT\db.ddl ‑code DBINIT\dbinit.cpp ‑head DBINIT\H\dbinit.hpp ‑codetop DBINIT\codetop.cpp

 

Тогда его содержимое будет помещаться в DBINIT\dbinit.cpp автоматически.

Ограничение

            Как показано выше, для всех описаний генерируются одни и те же имена глобальных переменных (dss_type_info, dss_type_methods). Поэтому использовать вспомогательный код, сгенерированный утилитой dssreg, можно только, если все открываемые программой БД имеют одинаковую схему данных. В противном случае необходимо воспользоваться утилитой dssscg для раздельной генерации вспомогательного кода (см. главу “Пособие программиста”, раздел “Раздельная генерация вспомогательного кода”).

Грамматика языка DssDDL

program: operator_list

 

operator_list: rename_op_list decl_op_list

 

rename_op_list: /* empty */

       |      rename_op_list rename_type

       |      rename_op_list rename_attr

 

decl_op_list: /* empty */

       |      decl_op_list abst_type_decl

       |      decl_op_list type_decl

       |      decl_op_list clone

 

rename_type: ‘rename’ ‘type’ soft_scoped_name soft_scoped_name ‘;’

 

rename_attr: ‘rename’ ‘attr’ hard_scoped_name name ‘;’

 

clone: ‘clone’ soft_scoped_name name ‘;’

 

soft_scoped_name:   name

       |      soft_scoped_name ‘::’ name

 

hard_scoped_name:   name ‘::’ name

       |      hard_scoped_name ‘::’ name

 

name: IDENTIFIER

 

abst_type_decl:     abstract type_decl

 

type_decl:   type_decl_begin type_decl_body ‘}’ ‘;’

 

type_decl_begin:    type_decl_name ‘{’

       |      type_decl_name ‘:’ base_type_list ‘{’

 

type_decl_name:     ‘struct’ name

 

base_type_list:     base_type

       |      base_type_list ‘:’ base_type

 

base_type:   inheritance_flag soft_scoped_name

                                 

inheritance_flag:   /* empty */

       |      ‘virtual’

       |      ‘virtual’ ‘public’

       |      ‘public’

       |      ‘public’ ‘virtual’

 

type_decl_body:     /* empty */

       |      type_decl_body field_decl

       |      type_decl_body type_decl

       |      type_decl_body clone

       |      type_decl_body method_decl

       |      type_decl_body abst_type_decl

 

field_decl:  field_type_name field_name_list ‘;’

 

field_type_name:    dss_type

       |      soft_scoped_name

 

dss_type:    ‘char’

       |      ‘wchar_t’

       |      ‘short’

       |      ‘int’

       |      ‘single’

       |      ‘double’

       |      ‘unsigned’ ‘char’

       |      ‘unsigned’ ‘short’

       |      ‘unsigned’ ‘int’

       |      ‘TDssChar’

       |      ‘TDssUChar’

       |      ‘TDssWChar’

       |      ‘TDssShort’

       |      ‘TDssUShort’

       |      ‘TDssInt’

       |      ‘TDssUInt’

       |      ‘TDssSingle’

       |      ‘TDssDouble’

 

field_name_list: name

       |      name array_decl

       |      field_name_list ‘,’ name

       |      field_name_list ‘,’ name array_decl

 

array_decl:  array_dimentions

 

array_dimentions:   ‘[’ number ‘]’

       |      array_dimentions ‘[’ number ‘]’

 

number:      NUMBER

 

method_decl: ‘method’ name soft_scoped_name ‘;’


Dss v.4.20. Справочное руководство

            В данной главе дается описание интерфейса Dss с языком C++. Интерфейс включает в себя доступные пользователю типы Dss, константы Dss, класс TDssKernelInterface, класс TDssIteratorInterface, класс TDssThreadAttach, информационные функции, макросы, коды ошибок Dss.

Типы Dss v.4.20

            В данном разделе приводятся доступные пользователю типы, их описание и назначение. Данные типы являются частью интерфейса Dss с C++ и останутся неизменными для обеспечения совместимости программ и будущих версий Dss. Остальные типы, на которые имеются ссылки в документации, являются внутренними типами Dss. Их описание можно найти в заголовочном файле dssdef.hpp. Однако эти типы могут быть в любой момент изменены, что приведет к неработоспособности программ, оперирующих ими. Ответственность за использование типов, не описанных в данном разделе, полностью ложится на программиста.

TDssBool

            Тип TDssBool предназначен для представления булевых значений true и false в языке C++.

            Определяется следующим образом:

 

typedef TDssUInt TDssBool;

TDssChar

            Тип TDssChar предназначен для представления 8-битовых знаковых целых чисел в языке C++.

            Для 32-битовых платформ определяется следующим образом:

 

typedef char TDssChar;

TDssDBError

            Тип TDssDBError - это перечисление, в котором указаны все коды ошибок Dss v.4.20. Подробно коды ошибок описываются ниже.

TDssDouble

            Тип TDssDouble предназначен для представления 64-битовых вещественных чисел двойной точности в языке C++.

            Для 32-битовых платформ определяется следующим образом:

 

typedef double TDssDouble;

TDssID

            Тип TDssID предназначен для представления идентификатора объекта (64-битовое беззнаковое целое) в языке С++.

            Определяется следующим образом:

 

struct TDssID {

       TDssUInt m_file;

       TDssUInt m_value;

 

       TDssID( void );

       TDssID( TDssUInt ui );

       TDssID( TDssUInt file, TDssUInt value );

       TDssID( const TDssID & id );

 

       TDssBool     operator==( const TDssID & id ) const;

       TDssBool     operator<( const TDssID & id ) const;

       TDssBool     operator<=( const TDssID & id ) const;

       TDssBool     operator>( const TDssID & id ) const;

       TDssBool     operator>=( const TDssID & id ) const;

       TDssBool     operator!=( const TDssID & id ) const;

       TDssID &     operator+=( TDssUInt ui );

       TDssID &     operator++( void );

       TDssID operator++( int );

       TDssID operator+( TDssUInt ui ) const;

};

 

Примечания:

            1) идентификатор объекта считается недействительным, если поле m_value содержит значение 0xFFFFFFF;

            2) в Dss v.4.20 и ниже поле m_file всегда содержит нулевое значение;

            3) все методы структуры TDssID являются inline-методами. Для использования TDssID достаточно заголовочного файла dssi.hpp.

TDssIDType

            Тип TDssIDType предназначен для представления идентификатора типа (32-битовое беззнаковое целое) в языке C++.

            Определяется следующим образом:

 

typedef TDssUInt TDssIDType;

TDssInt

            Тип TDssInt предназначен для представления 32-битовых знаковых целых чисел в языке C++.

            Для 32-битовых платформ определяется следующим образом:

 

typedef int TDssInt;

TDssIteratorInterface

            Класс TDssIteratorInterface - это класс, обеспечивающий инкапсуляцию интерфейсных функций Dss v.4.20, относящихся к итераторам. Все методы класса являются inline-методами. Подробнее методы класса TDssIteratorInterface описываются ниже.

TDssKernelInterface

            Класс TDssKernelInterface - это класс, обеспечивающий инкапсуляцию интерфейсных функций Dss v.4.20, относящихся к работе с базой данных. Все методы класса являются inline-методами. Подробнее методы класса TDssKernelInterface описываются ниже.

TDssShort

            Тип TDssShort предназначен для представления 16-битовых знаковых целых чисел в языке C++.

            Для 32-битовых платформ определяется следующим образом:

 

typedef short TDssShort;

TDssSingle

            Тип TDssSingle предназначен для представления 32-битовых вещественных чисел одинарной точности в языке C++.

            Для 32-битовых платформ определяется следующим образом:

 

typedef float TDssSingle;

TDssThreadAttach

            Класс TDssThreadAttach - это класс, предназначенный для обеспечения парности вызовов методов TDssKernelInterface::threadAttach и TDssKernelInterface::threadDetach. Конструктор класса TDssThreadAttach автоматически вызывает метод TDssKernelInterface::threadAttach. Если вызов прошел удачно, то деструктор класса TDssThreadAttach автоматически вызывает метод TDssKernelInterface::threadDetach.

            Все методы класса TDssThreadAttach являются inline-методами. Подробнее методы класса TDssThreadAttach описываются ниже.

TDssUChar

            Тип TDssUChar предназначен для представления 8-битовых беззнаковых целых чисел в языке C++.

            Для 32-битовых платформ определяется следующим образом:

 

typedef unsigned char TDssUChar;

TDssUInt

            Тип TDssUInt предназначен для представления 32-битовых беззнаковых целых чисел в языке C++.

            Для 32-битовых платформ определяется следующим образом:

 

typedef unsigned int TDssUInt;

TDssUShort

            Тип TDssUShort предназначен для представления 16-битовых беззнаковых целых чисел в языке C++.

            Для 32-битовых платформ определяется следующим образом:

 

typedef unsigned short TDssUShort;

TDssWChar

            Тип TDssChar предназначен для представления 16-битовых беззнаковых целых чисел в языке C++. Данный тип используется для хранения т.н. “длинных” символов (wide char).

            Для 32-битовых платформ определяется следующим образом:

 

typedef unsigned short TDssWChar;

Константы Dss v.4.20

            В данном разделе приводятся доступные пользователю константы и их назначение. Данные константы являются частью интерфейса Dss с C++ и останутся неизменными для обеспечения совместимости программ и будущих версий Dss. Остальные константы, на которые имеются ссылки в документации, являются внутренними константами Dss. Их описание можно найти в заголовочном файле dssdef.hpp. Однако, эти константы могут быть в любой момент изменены, что приведет к неработоспособности программ, оперирующих ими. Ответственность за использование констант, не описанных в данном разделе, полностью ложится на программиста.

c_bDssFalse

            Константа c_bDssFalse является представлением булевского значения false для типа TDssBool.

c_bDssTrue

            Константа c_bDssTrue является представлением булевского значения true для типа TDssBool.

c_idDssInvalid

            Константа c_idInvalid предназначена для представления значения незадействованного идентификатора объекта (аналог NULL для указателей).

c_idtypeDssInvalid

            Константа c_idtypeDssInvalid предназначена для представления значения незадействованного идентификатора типа (аналог NULL для указателей).

c_uiDssDenyReason_Ambiguous

            Константа c_uiDssDenyReason_Ambiguous предназначена для указания невозможности выбора из нескольких равноценных вариантов. Например, при множественном наследовании тип унаследовал несколько методов с одинаковыми именами. По имени метода невозможно выбрать, какой метод какого базового класса следует использовать.

c_uiDssDenyReason_Nonpublic

            Константа c_uiDssDenyReason_Nonpublic предназначена для указания невозможности выполнения операции из-за недоступности искомого типа (метода). Например, невозможно преобразование типа из-за того, что указанный тип является недоступным базовым типом.

c_uiDssMode_ReadOnly

            Константа c_uiDssMode_ReadOnly назначает режим доступа “только-чтение” для указанного действия.

c_uiDssMode_ReadWrite

            Константа c_uiDssMode_ReadWrite назначает режим доступа “чтение-запись” для указанного действия.

c_uiDssMode_Unknown

            Константа c_uiDssMode_Unknown предписывает Dss выбрать режим доступа для указанного действия самостоятельно. Применяется в методе dbOpen класса TDssKernelInterface.

c_uiDssMode_WriteShare

            Константа c_uiDssMode_WriteShare назначает режим доступа “разделяемая-запись” для указанного действия.

c_uiDssRpLevel1

            Константа c_uiDssRpLevel1 указывает, что при работе с БД будут доступны только системные транзакции. Обеспечивается сохранность только внутренних данных Dss, но не пользовательских данных.

c_uiDssRpLevel2

            Константа c_uiDssRpLevel2 указывает, что при работе с БД будут доступны как системные, так и пользовательские транзакции. Обеспечивается сохранность не только внутренних данных Dss, но и пользовательских данных.

c_uiDssTypeCode_Char

            Константа c_uiDssTypeCode_Char является идентификатором стандартного типа char.

c_uiDssTypeCode_Double

            Константа c_uiDssTypeCode_Char является идентификатором стандартного типа double.

c_uiDssTypeCode_Int

            Константа c_uiDssTypeCode_Char является идентификатором стандартного типа int.

c_uiDssTypeCode_Short

            Константа c_uiDssTypeCode_Char является идентификатором стандартного типа short.

c_uiDssTypeCode_Single

            Константа c_uiDssTypeCode_Char является идентификатором стандартного типа float.

c_uiDssTypeCode_UChar

            Константа c_uiDssTypeCode_Char является идентификатором стандартного типа unsigned char.

c_uiDssTypeCode_UInt

            Константа c_uiDssTypeCode_Char является идентификатором стандартного типа unsigned int.

c_uiDssTypeCode_UShort

            Константа c_uiDssTypeCode_Char является идентификатором стандартного типа unsigned short.

c_uiDssTypeCode_UsrType

            Константа c_uiDssTypeCode_Char является идентификатором типа TDssID.

c_uiDssTypeCode_WChar

            Константа c_uiDssTypeCode_Char является идентификатором стандартного типа wchar_t.

c_uiDssTypeSpec_Clone

            Константа c_uiDssTypeSpec_Clone показывает, что указанный тип является клоном.

c_uiDssTypeSpec_Normal

            Константа c_uiDssTypeSpec_Clone показывает, что указанный тип является нормальным типом.

c_uiDssTypeSpec_Std

            Константа c_uiDssTypeSpec_Clone показывает, что указанный тип является стандартным типом.

Методы класса TDssKernelInterface

            Описание метода состоит из трех частей. В первой части дается прототип метода. Во второй части описывается назначение метода и основные возвращаемые значения. В третьей части в виде таблицы из трех столбцов описываются аргументы метода. В первом столбце указывается имя аргумента. Во втором столбце указывается вид аргумента: входной (in), выходной (out), необязательный (opt). Значения возвращаются в необязательных аргументах только, если на входе их значения отличны от нуля. В третьем столбце дается описание аргумента.

            В случае успешного завершения методы класса возвращают значение eDBErrorOk. В случае ошибки возвращаются значения, отличные от eDBErrorOk.

attrGetCount

            Формат:

 

TDssDBError  attrGetCount( TDssIDType idtype, TDssUInt & uiCountAll, TDssUInt * puiCountSelf );

 

            Возвращает количество атрибутов в указанном типе.

            Параметры:

idtype

in

идентификатор типа;

uiCountAll

out

количество всех атрибутов в типе (унаследованных и собственных);

puiCountSelf

out opt

количество собственных атрибутов.

Примечание. В списке атрибутов унаследованные атрибуты располагаются перед собственными атрибутами типа. Атрибуты в списке нумеруются с нуля. Индекс первого собственного атрибута равен (uiCountAll - uiCountSelf).

attrGetOffset

            Формат:

 

TDssDBError  attrGetOffset( TDssIDType idtype, TDssUInt index, TDssUInt & offset);

 

            Возвращает смещение указанного атрибута от начала объекта указанного типа. Если смещение атрибута неизвестно, то возвращается код ошибки eDBErrorOffsetUnknown. Смещения атрибутов становятся известными после выполнения любого из следующих методов, относящихся к объектам указанного типа:

            - objAllocate с ненулевым параметром pcvInitial;

            - objUpdate;

            - objLoad;

            - objLoadAs.

            Параметры:

idtype

in

идентификатор типа;

index

in

индекс атрибута в списке атрибутов. Атрибуты нумеруются с нуля;

offset

out

смещение атрибута относительно начала объекта.

attrGetParams

            Формат:

 

TDssDBError  attrGetParams( TDssIDType idtype, TDssUInt index, TDssChar * pszNameBuf, TDssUInt uiNameBufSize, TDssUInt * puiNameLength, TDssIDType * pidtypeAttr, TDssUInt * puiVectorSize, TDssBool * pbAccessable, TDssChar * pszPathBuf, TDssUInt uiPathBufSize, TDssUInt * puiPathLength );

 

            Возвращает параметры указанного атрибута в указаном типе.

            Параметры:

idtype

in

идентификатор типа;

index

in

индекс атрибута в списке атрибутов. Атрибуты нумеруются с нуля;

pszNameBuf

in opt

указатель на буфер для получения имени атрибута;

uiNameBufSize

in opt

размер буфера для получения имени атрибута. Должен указываться, если указывается аргумент pszNameBuf;

puiNameLength

out opt

длина имени атрибута без 0-символа;

pidtypeAttr

out opt

идентификатор типа атрибута;

puiVectorSize

out opt

размер вектора. Если атрибут не является вектором, то получает значение 0;

pbAccessable

out opt

получает значение c_bDssTrue, если атрибут является доступным атрибутом и c_bDssFalse в противном случае. Атрибут считается доступным, если он является собственным атрибутом типа, или он является доступным атрибутом в доступном базовом типе;

pszPathBuf

in opt

указатель на буфер для получения уточнителя имени атрибута. Уточнитель имени - это список имен типов, из которых атрибут унаследован. Имена разделяются символами “::”. Первым именем в списке всегда является имя типа idtype. В конце списка стоят символы “::”;

uiPathBufSize

in opt

размер буфера для получения уточнителя имени. Должен указываться, если указывается аргумент pszPathBuf;

puiPathLength

out opt

длина уточнителя без 0-символа.

cloneCreate

            Формат:

 

TDssDBError  cloneCreate( TDssIDType idtype, const TDssChar * pcszCloneName, TDssIDType & idtypeClone );

 

            Создает в базе данных клон указанного типа с указанным именем. Имя клона - это ASCIIZ строка произвольной длины, состоящая из символов ‘A’-‘Z’, ‘a’-‘z’, ‘0’-‘9’ и ‘_’. Имя должно начинаться с символов ‘A’-‘Z’, ‘a’-‘z’ или ‘_’. Имя клона должно быть уникальным. Исходный тип должен быть нормальным типом.

            Параметры:

idtype

in

идентификатор исходного типа;

pcszCloneName

in

имя клона;

idtypeClone

out

идентификатор созданного клона.

dbClose

 

TDssDBError  dbClose( void );

 

            Закрывает открытую базу данных. Загруженные, но не выгруженные объекты, выгружаются автоматически. Если существуют незавершенные транзакции, то они не подтверждаются и не отменяются. Если в течение транзакций была накоплена восстановочная информация, то база данных считается поврежденной. Для отмены внесенных изменений необходимо воспользоваться утилитой dssrpr.

dbGetMode

 

TDssDBError  dbGetMode( TDssUInt * puiModeOpen, TDssUInt * puiModeDB );

 

            Возвращает собственный режим базы данных и режим, в котором база данных открыта.

            Параметры:

puiModeOpen

out opt

режим, в котором база данных открыта;

puiModeDB

out opt

собственный режим базы данных.

dbOpen

 

TDssDBError  dbOpen( const TDssChar * pcszName, TDssUInt uiMode, TDssUInt uiRpLevel, const TDssUserTypeInformation * ptypeInfo, const TDssUserTypeMethods * ptypeMethods );

 

            Производит открытие базы данных с указанным именем, указанным режимом доступа и заданным уровнем детализации восстановочной информации.

            Если длина восстановочного файла отлична от нуля (потенциальная опасность повреждения базы данных), то база данных не открывается, а возвращается код ошибки eDBErrorDamagePossible (для исправления этой ошибки необходимо воспользоваться утилитой dssrpr).

            Если указанный режим доступа не позволяет работать с базой данных (указывается режим ReadWrite, а база данных находится в состоянии ReadOnly), то открытие базы данных не производится, а возвращается код ошибки eDBErrorReadOnlyMode. Если режим доступа равен c_uiDssMode_Unknown, то режимом доступа считается собственный режим базы данных.

            Параметры:

pcszName

in

имя базы данных;

uiMode

in

режим доступа к базе данных. Должен быть равен c_uiDssMode_ReadWrite,  c_uiDssMode_ReadOnly или c_uiDssMode_Unknown;

uiRpLevel

in

уровень восстановочной информации. Должен быть равен c_uiDssRpLevel1 или c_uiDssRpLevel2;

ptypeInfo

in

указатель на вектор описаний схемы данных. Этот вектор генерируется утилитой dssreg и в Dss v.4.20 имеет имя dss_type_info;

ptypeMethods

in

указатель на вектор описаний именованных методов. Этот вектор генерируется утилитой dssreg и в Dss v.4.20 имеет имя dss_type_methods.

dbSetCacheMode

 

TDssDBError  dbSetCacheMode( TDssUInt uiCacheSize, TDssBool bCacheOnWrite );

 

            Производит установку параметров кэша для файлов refers, objfile, typelist. По умолчанию для всех файлов БД используется кэш размером c_uiDssDefaultBufSize без кэширования записи. Метод должен вызываться однократно перед открытием БД. В противном случае возвращается код ошибки eDBErrorCacheModeSelected.

            Параметры:

uiCacheSize

in

размер кэш-буфера для операций чтения записи;

bCacheOnWrite

in

флаг включения режима кэширования записи. Если содержит значение c_bDssTrue, то включается режим кэширования записи. В противном случае кэширование записи не производится.

dbSwitchMode

 

TDssDBError  dbSwitchMode( const TDssChar * pcszName, TDssUInt uiMode, TDssUInt uiRpLevel, const TDssUserTypeInformation * ptypeInfo, const TDssUserTypeMethods * ptypeMethods );

 

            Открывает базу данных с указанным именем, заданным уровнем детализации восстановочной информации и одновременно переводит базу данных в указанный режим.

            Если длина восстановочного файла отлична от нуля (потенциальная опасность повреждения базы данных), то база данных не открывается, а возвращается код ошибки eDBErrorDamagePossible (для исправления этой ситуации необходимо воспользоваться утилитой dssrpr).

            Параметры:

pcszName

in

имя базы данных;

uiMode

in

режим доступа к базе данных. Должен быть равен c_uiDssMode_ReadWrite или  c_uiDssMode_ReadOnly;

uiRpLevel

in

уровень восстановочной информации. Должен быть равен c_uiDssRpLevel1 или c_uiDssRpLevel2;

ptypeInfo

in

указатель на вектор описаний схемы данных. Этот вектор генерируется утилитой dssreg и в Dss v.4.20 имеет имя dss_type_info;

ptypeMethods

in

указатель на вектор описаний именованных методов. Этот вектор генерируется утилитой dssreg и в Dss v.4.20 имеет имя dss_type_methods.

idCount

            Формат:

 

TDssDBError  idCount( TDssID & uiCount );

 

            Возвращает количество идентификаторов в базе данных. В это количество входят идентификаторы, относящиеся как к существующим, так и к удаленным объектам.

            Параметры:

uiCount

out

количество идентификаторов в базе данных.

Примечание. В Dss v.4.20 в возвращаемом значении задействовано только поле TDssID::m_value. Поле TDssID::m_file всегда имеет нулевое значение.

idFirstGlobal

            Формат:

 

TDssDBError  idFirstGlobal( TDssID & id );

 

            Возвращает самый первый идентификатор базы данных. Если идентификаторов в базе данных нет, то возвращается код ошибки eDBErrorNotFound. Возвращенный идентификатор может относиться как к существующему, так и к удаленному объекту.

            Параметры:

id

out

первый идентификатор базы данных.

idFirstLocal

            Формат:

 

TDssDBError  idFirstLocal( TDssIDType idtype, TDssID & id );

 

            Возвращает идентификатор первого существующего объекта для указанного типа. Если идентификаторов существующих объектов для этого типа не найдено, то возвращается код ошибки eDBErrorNotFound.

            Параметры:

idtype

in

идентификатор типа;

id

out

идентификатор первого существующего объекта.

idGetInfo

            Формат:

 

TDssDBError  idGetInfo( const TDssID & id, TDssIDType * ptype, TDssObjAddress * poffs, TDssBool * pObjExist );

 

            Возвращает параметры указанного идентификатора.

            Параметры:

id

in

идентификатор;

ptype

out opt

идентификатор типа, к которому относится объект. Если идентификатор соответствует удаленному объекту, то возвращается c_idtypeDssInvalid;

poffs

out opt

расположение объекта в файлах базы данных. Это значение должно использоваться только программистами, хорошо знакомыми с физической организацией базы данных Dss;

pObjExist

out opt

признак существования объекта. Получает значение c_bDssTrue, если идентификатор относится к существующему объекту. В противном случае получает значение c_bDssFalse.

idNextGlobal

            Формат:

 

TDssDBError  idNextGlobal( TDssID & id );

 

            Возвращает идентификатор базы данных, следующий за указанным идентификатором. При исчерпании идентификаторов возвращается код ошибки eDBErrorNotFound. Возвращенный идентификатор может относиться как к существующему, так и к удаленному объекту.

            Параметры:

id

in out

на входе - уже просмотренный идентификатор. На выходе - следующий идентификатор.

idNextLocal

            Формат:

 

TDssDBError  idNextLocal( TDssID & id );

 

            Возвращает идентификатор существующего объекта, следующего за указанным объектом. Найденный объект будет относиться к тому же типу. При исчерпании идентификаторов возвращается код ошибки eDBErrorNotFound. Указанный идентификатор обязательно должен относиться к существующему объекту.

            Параметры:

id

in out

на входе - идентификатор уже просмотренного объекта. На выходе - идентификатор следующего существующего объекта того же типа.

methodFindByObject

            Формат:

 

TDssDBError  methodFindByObject( const TDssID & id, const TDssChar * pcszName, TDssPFnMethod & method );

 

            Ищет указанный именованный метод в типе, к которому относится указанный объект. Поиск производится сначала среди собственных методов типа, затем среди унаследованных из доступных базовых типов. Если метод не найден, то возвращается код ошибки eDBErrorNotFound. Если найдено несколько методов, унаследованных из доступных базовых типов, то возвращается код ошибки eDBErrorAmbiguous. Если метод найден в самом типе, то поиск среди унаследованных методов не производится.

            Параметры:

id

in

идентификатор объекта;

pcszName

in

имя метода;

method

out

адрес метода.

methodFindByType

            Формат:

 

TDssDBError  methodFindByType( TDssIDType idtype, const TDssChar * pcszName, TDssPFnMethod & method );

 

            Ищет указанный именованный метод в указанном типе. Поиск производится сначала среди собственных методов типа, затем среди унаследованных из доступных базовых типов. Если метод не найден, то возвращается код ошибки eDBErrorNotFound. Если найдено несколько методов, унаследованных из доступных базовых типов, то возвращается код ошибки eDBErrorAmbiguous. Если метод найден в самом типе, то поиск среди унаследованных методов не производится.

            Параметры:

idtype

in

идентификатор типа;

pcszName

in

имя метода;

method

out

адрес метода;

methodGetCount

            Формат:

 

TDssDBError  methodGetCount( TDssIDType idtype, TDssUInt & uiCount );

 

            Возвращает количество собственных именованных методов в указанном типе.

            Параметры:

idtype

in

идентификатор типа;

uiCount

out

количество собственных именованных методов;

methodGetParams

            Формат:

 

TDssDBError  methodGetParams( TDssIDType idtype, TDssUInt index, TDssChar * pszNameBuf, TDssUInt uiNameBufSize, TDssUInt * puiNameLength, TDssPFnMethod * pmethod );

 

            Возвращает параметры указанного собственного именованного метода в указанном типе.

            Параметры:

idtype

in

идентификатор типа;

index

in

индекс метода в списке методов. Методы нумеруются с нуля;

pszNameBuf

in opt

указатель на буфер для получения имени метода;

uiNameBufSize

in opt

размер буфера для получения имени метода. Должен указываться, если указывается аргумент pszNameBuf;

puiNameLength

out opt

длина имени метода без 0-символа;

pmethod

out opt

адрес метода.

nameCreate

            Формат:

 

TDssDBError  nameCreate( const TDssChar * pcszName, const TDssID & id );

 

            Создает имя для указанного объекта. Имя должно быть уникальным. В противном случае возвращается код ошибки eDBErrorInvalidObjectName. Объект, для которого создается имя, должен существовать. Любой объект может иметь любое количество имен. При удалении объекта все относящиеся к нему имена удаляются автоматически.

            Параметры:

pcszName

in

создаваемое имя;

id

in

идентификатор объекта, для которого создается имя.

nameFind

            Формат:

 

TDssDBError  nameFind( const TDssChar * pcszName, TDssID & id );

 

            Ищет указанное имя и возвращает идентификатор объекта, к которому имя относится. Если имя не найдено, то возвращается код ошибки eDBErrorNotFound.

            Параметры:

pcszName

in

имя для поиска;

id

out

идентификатор объекта, к которому относится имя.

nameGetCount

            Формат:

 

TDssDBError  nameGetCount( TDssUInt & uiCount );

 

            Возвращает количество существующих имен.

            Параметр:

uiCount

out

количество существующих имен.

nameGetParams

            Формат:

 

TDssDBError  nameGetParams( TDssUInt index, TDssChar * pszBuf, TDssUInt uiBufSize, TDssUInt * puiNameLength, TDssID * pid );

 

            Возвращает параметры для указанного имени. Имя указывается порядковым номером в общем списке имен.

            Параметры:

index

in

порядковый номер имени в общем списке имен. Элементы списка нумеруются с нуля;

pszBuf

in opt

указатель на буфер для получения имени;

uiBufSize

in opt

размер буфера для получения имени. Должен указываться, если указывается аргумент pszBuf;

puiNameLength

out opt

длина имени без 0-символа;

pid

out opt

идентификатор объекта, к которому относится имя.

nameRemove

            Формат:

 

TDssDBError  nameRemove( const TDssChar * pcszName );

 

            Удаляет указанное имя. Объект, к которому имя относится, не удаляется.

            Параметр:

pcszName

in

удаляемое имя.

objAllocate

 

TDssDBError  objAllocate( TDssIDType idtype, TDssID & id, const void * pcvInitialValue = 0 );

 

            Создает объект типа idtype и, если pcvInitialValue не ноль, инициализирует его. Если по типу проводится какая-либо итерация, то возвращается код ошибки eDBErrorInIteration. Если база данных открыта в режиме ReadOnly, то возвращается код ошибки eDBErrorReadOnlyMode. Если размер объекта в базе данных равен 0, то возвращается код ошибки eDBErrorAbstractType.

            Если pcvInitialValue равно нулю, то атрибуты объекта инициализируются нулевыми значениями. Ссылочные атрибуты инициализируются значениями, эквивалентными c_idDssInvalid.

            Параметры:

idtype

in

идентификатор типа, объект которого должен быть создан;

id

out

идентификатор созданного объекта;

pcvInitialValue

in opt

начальное значение для создаваемого объекта.

objDeallocate

            Формат:

 

TDssDBError  objDeallocate( const TDssID & id );

 

            Удаляет объект. Если объект уже удален, то возвращается код ошибки eDBErrorFreeID. Если объект загружен, то возвращается код ошибки eDBErrorLoaded. Если по типу проводится итерация в режиме ReadOnly, то возвращается код ошибки eDBErrorInIteration. Если база данных открыта в режиме ReadOnly, то возвращается код ошибки eDBErrorReadOnlyMode.

            Параметры:

id

in

идентификатор удаляемого объекта.

objFree

            Формат:

 

TDssDBError  objFree( const TDssID & id, const void * pobj = 0 );

 

            Выгружает объект из оперативной памяти без сохранения значения объекта в базе данных. Если у объекта есть расширяющие атрибуты, то копия объекта, подлежащая выгрузке, определяется посредством аргумента pobj. Если определить копию объекта не удалось, возвращается код ошибки eDBErrorObjCopyUnknown. Если объект не имеет расширяющих атрибутов, либо если объект загружен в режиме ReadWrite, то параметр pobj не используется.

            Параметры:

id

in

идентификатор выгружаемого объекта;

pobj

in opt

указатель на копию объекта, подлежащую выгрузке.

objLoad

            Формат:

 

TDssDBError  objLoad( const TDssID & id, TDssUInt uiMode, void * & pobj );

 

            Загружает объект в оперативную память в указанном режиме. Если объект загружается в режиме ReadWrite, а по типу проводится итерация в режиме ReadOnly, то возвращается код ошибки eDBErrorInIteration. Ошибка eDBErrorLoaded возвращается в одном из следующих случаев:

            - объект загружается в режиме ReadOnly, а в ОП имеется копия объекта, загруженного в режиме ReadWrite или WriteShare;

            - объект загружается в режиме ReadWrite, а в ОП имеется копия объекта, загруженного в любом режиме;

            - объект загружается в режиме WriteShare, а в ОП имеется копия объекта, загруженного в режиме ReadOnly или ReadWrite.

            Параметры:

id

in

идентификатор загружаемого объекта;

uiMode

in

режим загрузки объекта. Должен иметь значение c_uiDssMode_ReadOnly, c_uiDssMode_ReadWrite или c_uiDssMode_WriteShare;

pobj

out

указатель на начало загруженного объекта.

objLoadAs

            Формат:

 

TDssDBError  objLoadAs( const TDssID & id, TDssIDType idtypeCast, TDssUInt uiMode, void * & pobj );

 

            Загружает объект в оперативную память в указанном режиме и преобразует указатель к указанному типу. Если объект загружается в режиме ReadWrite, а по типу проводится итерация в режиме ReadOnly, то возвращается код ошибки eDBErrorInIteration. Ошибка eDBErrorLoaded возвращается в одном из следующих случаев:

            - объект загружается в режиме ReadOnly, а в ОП имеется копия объекта, загруженного в режиме ReadWrite или WriteShare;

            - объект загружается в режиме ReadWrite, а в ОП имеется копия объекта, загруженного в любом режиме;

            - объект загружается в режиме WriteShare, а в ОП имеется копия объекта, загруженного в режиме ReadOnly или ReadWrite.

            Если преобразовать указатель к указанному типу не удается, то возвращаются коды ошибок eDBErrorInvalidType (если указанный тип не входит в граф наследования), eDBErrorTypecastDenied (если указанный тип не является доступным базовым типом), eDBErrorAmbiguous (если нет однозначного преобразования к указанному типу).

            Параметры:

id

in

идентификатор загружаемого объекта;

idtypeCast

in

идентификатор типа, к которому необходимо преобразовать указатель;

uiMode

in

режим загрузки объекта. Должен иметь значение c_uiDssMode_ReadOnly, c_uiDssMode_ReadWrite или c_uiDssMode_WriteShare;

pobj

out

указатель на загруженный объект, преобразованный к типу idtypeCast.

objNonSharedLocation

            Формат:

 

TDssDBError  objNonSharedLocation( const TDssID & id, void * & pObj );

 

            Возвращает указатель на копию объекта, загруженного в режимах ReadWrite или WriteShare. Если объект не загружен, то возвращается код ошибки eDBErrorNotLoaded. Если объект загружен в режиме ReadOnly, то возвращается код ошибки eDBErrorAccessDenied.

            Параметры:

id

in

идентификатор объекта;

pObj

out

указатель на начало загруженного объекта.

objSave

            Формат:

 

TDssDBError  objSave( const TDssID & id );

 

            Записывает значение объекта из оперативной памяти в базу данных без выгрузки объекта. Если объект загружен в режиме ReadOnly, то возвращается код ошибки eDBErrorAccessDenied. Если объект не загружен, то возвращается код ошибки eDBErrorNotLoaded.

            Параметры:

id

in

идентификатор записываемого объекта.

objTypeCast

            Формат:

 

TDssDBError  objTypeCast( const TDssID & id, TDssIDType idtypeCast, void * & pcastCopy, const void * pobj = 0 );

 

            Возвращает указатель на составляющую указанного загруженного объекта, относящуюся к указанному типу (преобразование указателя). Тип должен входить в граф наследования. Если это не так, то возвращается код ошибки eDBErrorInvalidType. Если указанный тип является недоступным базовым типом, то возвращается код ошибки eDBErrorTypecastDenied. Если преобразование неоднозначно (имеется несколько вхождений данного типа), то возвращается код ошибки eDBErrorAmbiguous.

            Если у объекта есть расширяющие атрибуты, то копия объекта, подлежащая приведению типа, определяется посредством pobj. Если определить копию объекта не удалось, то возвращается код ошибки eDBErrorObjCopyUnknown. Если объект не имеет расширяющих атрибутов, либо если объект загружен в режиме ReadWrite, то параметр pobj не используется.

            Параметры:

id

in

идентификатор объекта для приведения типа;

idtypeCast

in

идентификатор типа, к которому осуществляется приведение указателя;

pcastCopy

out

указатель на объект, приведенный к типу idtypeCast;

pobj

in opt

указатель на копию объекта, для которой необходимо приведение указателя.

objUpdate

            Формат:

 

TDssDBError  objUpdate( const TDssID & id, const void * pObj );

 

            Изменяет значение объекта в базе данных без загрузки объекта в оперативную память. Если объект загружен в оперативную память, то возвращается код ошибки eDBErrorLoaded. Если база данных открыта в режиме ReadOnly, то возвращается код ошибки eDBErrorReadOnlyMode. Если по типу проводится итерация в режиме ReadOnly, то возвращается код ошибки eDBErrorInIteration.

            Параметры:

id

in

идентификатор изменяемого объекта;

pObj

in

новое значение объекта.

threadAttach

            Формат:

 

TDssDBError  threadAttach( TDssUInt uiRpLevel );

 

            Переводит нить, вызвавшую threadAttach, в категорию выделенных нитей. Dss начинает считать данную нить отдельным процессом. Если нить уже является выделенной, то возвращается код ошибки eDBErrorThreadAttached.

            Параметр:

uiRpLevel

in

уровень восстановочной информации, который будет использоваться Dss при работе данной нити с БД. Должен иметь значение c_uiDssRpLevel1 или c_uiDssRpLevel2.

threadDetach

            Формат:

 

TDssDBError  threadDetach( void );

 

            Переводит нить, вызвавшую threadDetach, в категорию невыделенных нитей. Dss прекращает считать данную нить отдельным процессом. Dss выгружает все объекты, загруженные выделенной нитью, и завершает все итерации, начатые выделенной нитью. Если на момент вызова threadDetach выделенная нить имела незавершенные транзакции, то метод threadDetach вызывает метод transactionTerminate и возвращает код ошибки eDBErrorTransactionTerminated.

            Если метод threadDetach вызывает невыделенная нить, то возвращается код ошибки eDBErrorThreadNotAttached.

transactionBegin

            Формат:

 

TDssDBError  transactionBegin( void );

 

            Начинает новую пользовательскую транзакцию. Если база данных открыта c уровнем детализации RpLevel1, то возвращается код ошибки eDBErrorRpLevel1. Если база данных открыта в режиме ReadOnly, то возвращается код ошибки eDBErrorReadOnlyMode.

transactionEnd

            Формат:

 

TDssDBError  transactionEnd( void );

 

            Завершает последнюю незавершенную транзакцию и подтверждает все внесенные изменения. Если нет начатых транзакций, то возвращается код ошибки eDBErrorNoTransactions.

 transactionTerminate

            Формат:

 

TDssDBError  transactionTerminate( void );

 

            Если существуют начатые транзакции, то очищает все ресурсы и переводит класс TDssKernelInterface в особый режим. В этом режиме обращение к любому методу приводит к возврату кода ошибки eDBErrorTransactionTerminated. Если в процессе транзакций была записана восстановочная информация, то она не удаляется и не обрабатывается - база данных считается поврежденной.

            Если нет начатых транзакций, то возвращается код ошибки eDBErrorNoTransactions.

transactionUndo

            Формат:

 

TDssDBError  transactionUndo( void );

 

            Отменяет все действия, выполненные в течение последней незавершенной транзакции, и завершает эту транзакцию. Отменяются так же все действия, которые были подтверждены во всех вложенных транзакциях. Если нет незавершенных транзакций, то возвращается код ошибки eDBErrorNoTransactions.

typeEnum

            Формат:

 

TDssDBError  typeEnum( TDssIDType & idtype );

 

            Перечисляет идентификаторы типов в схеме данных. Перечисляются как нормальные типы, так и клоны. При первом вызове метода параметр idtype должен быть равен c_idtypeDssInvalid. При последующих вызовах метода параметр idtype должен содержать значение, полученное при предыдущем вызове метода. При исчерпании типов в схеме возвращается код ошибки eDBErrorNotFound.

            Параметры:

idtype

in out

идентификатор типа. При первом вызове должен быть равен c_idtypeDssInvalid.

typeEnumBase

            Формат:

 

TDssDBError  typeEnumBase( TDssIDType idtype, TDssIDType & idtypeBase, TDssBool * pbVirtual = 0, TDssUInt * puiDeniedReason = 0 );

 

            Перечисляет базовые типы указанного типа. При первом вызове метода idtypeBase должен содержать значение c_idtypeDssInvalid. При следующих вызовах метода он должен содержать идентификатор типа, возвращенный предыдущим вызовом метода. В случае исчерпания базовых типов или отсутствия таковых возвращается код ошибки eDBErrorNotFound.

            Параметры:

idtype

in

идентификатор типа, для которого перечисляются базовые типы;

idtypeBase

in out

идентификатор очередного базового типа. При первом вызове метода должен быть равен c_idtypeDssInvalid;

pbVirtual

out opt

признак виртуального вхождения базового типа. Получает значение c_bDssTrue, если базовый тип является виртуальным базовым типом;

puiDeniedReason

out opt

признак доступного вхождения базового типа. Получает значение 0, если базовый тип является доступным базовым типом.

typeFind

            Формат:

 

TDssDBError  typeFind( const TDssChar * pcszTypeName, TDssIDType & idtype );

 

            Производит поиск типа с указанным именем и возвращает его идентификатор. Если тип не найден, то возвращается код ошибки eDBErrorNotFound.

            Параметры:

pcszTypeName

in

имя типа для поиска;

idtype

out

идентификатор типа.

typeGetCount

            Формат:

 

TDssDBError  typeGetCount( TDssUInt & uiCount );

 

            Возвращает количество типов в схеме данных. В это число входят нормальные типы и клоны.

            Параметры:

uiCount

out

количество типов в схеме данных.

typeGetIterationParams

            Формат:

 

TDssDBError  typeGetIterationParams( TDssIDType idtype,TDssUInt & uiCount, TDssUInt * puiMode );

 

            Возвращает количество проводимых по типу итераций и их режим.

            Параметры:

idtype

in

идентификатор типа;

uiCount

out

количество проводимых в данный момент итераций;

puiMode

out opt

режим, в котором проводятся итерации. Определен, если uiCount отличен от нуля.

typeGetObjCount

            Формат:

 

TDssDBError  typeGetObjCount( TDssIDType idtype, TDssUInt & uiObjCount );

 

            Возвращает количество существующих объектов указанного типа.

            Параметры:

idtype

in

идентификатор типа;

uiObjCount

out

количество существующих объектов.

typeGetParams

            Формат:

 

TDssDBError  typeGetParams( TDssIDType idtype, TDssChar * pszNameBuf, TDssUInt uiNameBufSize, TDssUInt * puiNameLength, TDssUInt * puiTypeSpec, TDssIDType * pidtypeSource, TDssUInt * puiObjSizeDB, TDssUInt * puiObjSizeMem, TDssUInt * puiBaseCount, TDssUInt * puiObjCount );

 

            Возвращает параметры типа.

            Параметры:

idtype

in

идентификатор типа, для которого определяются параметры;

pszNameBuf

in opt

указатель на буфер для получения имени типа;

uiNameBufSize

in opt

размер буфера для получения имени типа. Должен указываться, если указывается аргумент pszNameBuf;

puiNameLength

out opt

длина имени типа без 0-символа;

puiTypeSpec

out opt

спецификация типа. Получает значение c_uiDssTypeSpec_Normal, если тип является нормальным типом. Получает значение c_uiDssTypeSpec_Clone, если тип является клоном. Получает значение c_uiDssTypeSpec_Std, если тип является стандартным типом Dss;

pidtypeSource

out opt

идентификатор исходного типа для клона. Определен, если puiTypeSpec получает значение c_uiDssTypeSpec_Clone;

puiObjSizeDB

out opt

размер объекта в базе данных;

puiObjSizeMem

out opt

размер объекта в оперативной памяти;

puiBaseCount

out opt

количество непосредственных базовых типов;

puiObjCount

out opt

количество существующих объектов.

typeIsDerivedFrom

            Формат:

 

TDssDBError  typeIsDerivedFrom( TDssIDType idtypeDerived, TDssIDType idtypeBase, TDssBool & bResult, TDssUInt * puiDeniedReason = 0 );

 

            Проверяет наличие типа idtypeBase в графе наследования для типа idtypeDerived.

            Параметры:

idtypeDerived

in

тип, для которого тестируется граф наследования;

idtypeBase

in

тип, который ищется в графе наследования;

bResult

out

признак наличия типа idtypeBase в графе наследования для типа idtypeDerived. Получает значение c_bDssTrue, если тип idtypeBase найден в графе наследования;

puiDeniedReason

out opt

признак доступности вхождения типа idtypeBase в граф наследования. Определен, только если bResult получает значение c_bDssTrue. Содержит значение 0, если можно привести тип idtypeDerived к типу idtypeBase. Содержит значение c_uiDssDenyReason_Nonpublic, если тип idtypeBase входит в граф наследования как недоступный базовый тип. Содержит значение c_uiDssDenyReason_Ambiguous, если тип idtypeBase входит в граф наследования и как доступный базовый тип, и как виртуальный базовый тип, либо если тип idtypeBase входит в граф наследования как доступный базовый тип несколько раз. В этом случае приведение типа idtypeDerived к типу idtypeBase невозможно.

typeStartIteration

            Формат:

 

TDssDBError  typeStartIteration( TDssIDType idtype, TDssUInt uiMode, TDssIteratorInterface & iterator );

 

            Начинает итерацию по указанному типу в указанном режиме.

            Если итерация начинается в режиме ReadOnly, а по типу уже проводится итерация в режиме ReadWrite, либо если итерация начинается в режиме ReadWrite, а по типу уже проводится итерация в режиме ReadOnly, то возвращается код ошибки eDBErrorAccessDenied. Можно начать любое количество итераций в режиме ReadOnly.

            Нельзя проводить итерацию в режиме ReadOnly, если есть загруженные объекты этого типа в режиме ReadWrite. В этом случае возвращается код ошибки eDBErrorLoaded.

            Параметры:

idtype

in

идентификатор типа для проведения итерации;

uiMode

in

режим проведения итерации. Должен иметь значение c_uiDssMode_ReadOnly, c_uiDssMode_ReadWrite или c_uiDssMode_WriteShare;

iterator

in out

объект-итератор.

typeStopIteration

            Формат:

 

TDssDBError  typeStopIteration( TDssIteratorInterface & iterator );

 

            Завершает итерацию по типу. Параметр iterator должен быть проинициализирован методом typeStartIteration.

            Параметры:

iterator

in

объект-итератор.

Методы класса TDssIteratorInterface

            Описание метода состоит из трех частей. В первой части дается прототип метода. Во второй части описывается назначение метода и основные возвращаемые значения. В третьей части в виде таблицы из трех столбцов описываются аргументы метода. В первом столбце указывается имя аргумента. Во втором столбце указывается вид аргумента: входной (in), выходной (out), необязательный (opt). Значения возвращаются в необязательных аргументах только, если на входе их значения отличны от нуля. В третьем столбце дается описание аргумента.

idGoto

            Формат:

 

TDssDBError  idGoto( const TDssID & id );

 

            Осуществляет переход итерации на указанный идентификатор. Идентификатор должен соответствовать существующему объекту того типа, для которого создан итератор. Идентификатор может быть уже просмотренным идентификатором или еще непросмотренным.

            Параметр:

id

in

идентификатор существующего объекта, на который должна перейти итерация.

idNext

            Формат:

 

TDssDBError  idNext( TDssID & id, TDssBool * pObjExist );

 

            Ищет и возвращает идентификатор следующего существующего объекта. Если идентификаторы исчерпаны, то возвращается код ошибки eDBErrorNotFound.

            Параметры:

id

out

идентификатор следующего существующего объекта;

pObjExist

out opt

всегда получает значение c_bDssTrue.

Примечание. Данный метод введен для совместимости исходных текстов, написанных для предыдущих версий Dss.

lastID

            Формат:

 

TDssID       lastID( void );

 

            Возвращает идентификатор объекта, на котором итерация остановилась. Если ни одного объекта еще не было найдено, то возвращается аналог c_idDssInvalid.

lastObjExist

            Формат:

 

TDssBool     lastObjExist( void );

 

            Возвращает значение c_bDssTrue, если в процессе итерации был найден хотя бы один объект.

Примечание. Данный метод введен для совместимости исходных текстов, написанных для предыдущих версий Dss.

objNext

            Формат:

 

TDssDBError  objNext( TDssID & id );

 

            Ищет и возвращает идентификатор следующего существующего объекта. Если идентификаторы исчерпаны, то возвращается код ошибки eDBErrorNotFound.

            Параметр:

id

out

идентификатор следующего существующего объекта.

Информационные функции

IDssInfoGetErrorName

            Формат:

 

void   IDssInfoGetErrorName       ( TDssUInt error, TDssChar * pszNameBuf, TDssUInt uiNameBufLen, TDssUInt * puiNameLen, TDssChar * pszReasonBuf, TDssUInt uiReasonBufLen, TDssUInt * puiReasonLen );

 

            Возвращает описание указанной ошибки.

            Параметры:

error

in

код ошибки;

pszNameBuf

in opt

указатель на буфер для получения имени ошибки в перечислении TDssDBError;

uiNameBufLen

in opt

размер буфера для получения имени ошибки. Должен указываться, если указывается аргумент pszNameBuf;

puiNameLen

out opt

длина имени ошибки без 0-символа;

pszReasonBuf

in opt

указатель на буфер для получения краткого описания причины возникновения ошибки;

uiReasonBufLen

in opt

размер буфера для получения краткого описания. Должен указываться, если указывается атрибут pszReasonBuf;

puiReasonLen

out opt

длина краткого описания причины возникновения ошибки без 0-символа.

IDssInfoGetVersion

            Формат:

 

void IDssInfoGetVersion( TDssUInt * puiVerHi, TDssUInt * puiVerLo, TDssUInt * puiVerRevision );

 

            Возвращает номер версии Dss.

            Параметры:

puiVerHi

out

старший номер версии;

puiVerLo

out

младший номер версии;

puiVerRevision

out

подномер младшего номера версии (номер реализации).

Методы класса TDssThreadAttach

            Описание метода состоит из трех частей. В первой части дается прототип метода. Во второй части описывается назначение метода и основные возвращаемые значения. В третьей части в виде таблицы из трех столбцов описываются аргументы метода. В первом столбце указывается имя аргумента. Во втором столбце указывается вид аргумента: входной (in), выходной (out), необязательный (opt). Значения возвращаются в необязательных аргументах только, если на входе их значения отличны от нуля. В третьем столбце дается описание аргумента.

TDssThreadAttach

            Формат:

 

TDssThreadAttach( TDssKernelInterface & dss, TDssUInt uiRpLevel );

 

            Конструктор класса TDssThreadAttach. Осуществляет вызов метода TDssKernelInterface::threadAttach для указанного объекта TDssKernelInterface.

            Параметры:

dss

in

БД, с которой будет работать данная нить;

uiRpLevel

in

уровень восстановочной информации, с которым данная нить будет осуществлять работу с БД.

~TDssThreadAttach

            Деструктор. Вызывает метод TDssKernelInterface::threadDetach, если в конструкторе класса TDssThreadAttach вызов TDssKernelInterface::threadAttach прошел успешно, и за время жизни объекта не было вызовов метода detach.

isAttached

            Формат:

 

TDssBool     isAttached( void ) const;

 

            Возвращает c_bDssTrue, если вызов метода TDssKernelInterface::threadAttach в конструкторе класса TDssThreadAttach прошел успешно, и не было вызовов метода detach.

detach

            Формат:

 

TDssDBError  detach( void );

 

            Производит вызов метода TDssKernelInterface::threadDetach. Возвращает код возврата метода TDssKernelInterface::threadDetach. Если в конструкторе класса TDssThreadAttach вызов TDssKernelInterface::threadAttach закончился неудачно, или за время жизни объекта осуществлялись вызовы метода detach, то возвращается eDBErrorError.

Макросы Dss v.4.20

DSS_ADDRESS

 

#define DSS_ADDRESS(pointer) *((void**) &pointer)

 

            Предназначен для автоматического преобразования аргументов в методах objLoad, objLoadAs, objTypeCast. Указатель на объект в этих методах возвращается через аргумент типа (void *&). Если в него передать указатель на какой-либо класс:

 

TAnyType * p;

dss.objLoad( id, c_uiDssMode_ReadOnly, p );

 

то возникнет ошибка компиляции, связанная с несовпадением типов последнего аргумента ((void *&) != (TAnyType *&)). Для избежания этой ошибки используется макрос DSS_ADDRESS:

 

TAnyType * p;

dss.objLoad( id, c_uiDssMode_ReadOnly, DSS_ADDRESS( p ) );

DSS_METHOD_CAST

 

#define DSS_METHOD_CAST(pointer) *((TDssPFnMethod*) &pointer)

 

            Предназначен для автоматического преобразования аргументов в методах methodFindByType, methodFindByObject. Адрес именованного метода в этих методах возвращается через аргумент типа (TDssPFnMethod&). Если в него передать указатель на какой-либо класс:

 

TPFnMyMethod p;

dss.methodFindByObject( id, “myMethod”, p );

 

то возникнет ошибка компиляции, связанная с несовпадением типов последнего аргумента ((TDssPFnMethod &) != (TPFnMyMethod &)). Для избежания этой ошибки используется макрос DSS_METHOD_CAST:

 

TPFnMyMethod p;

dss.methodFindByObject( id, “myMethod”, DSS_METHOD_CAST( p ) );

 

Информирование о непоправимых ошибках

            В процессе работы с Dss могут возникать непоправимые ошибки. Например, недостаточно памяти для системных данных или неожиданные ошибки ввода-вывода. В этом случае все выделенные ресурсы очищаются, а дальнейшие обращения к любым методам Dss будут возвращать код возникшей ошибки. Так, если при загрузке объекта возникла непоправимая ошибка с кодом eDBErrorNoMemory, то последующее обращение к методу typeFind также завершится с кодом eDBErrorNoMemory.

Коды ошибок Dss v.4.20

eDBErrorAbstractType

            Попытка создания объекта абстрактного типа. Абстрактным типом в Dss v.4.20 является тип, не имеющий хранимых атрибутов (размер объекта в базе данных равен 0).

eDBErrorAccessDenied

            Попытка доступа к какому-либо ресурсу в другом режиме. Например, попытка получения доступа к объекту, загруженному в режиме ReadOnly, при помощи метода objNonSharedLocation.

eDBErrorAccessLocked

            Попытка доступа к какому-либо ресурсу, заблокированному параллельной транзакцией в эксклюзивном режиме.

eDBErrorAmbiguous

            Невозможно сделать выбор между имеющимися вариантами. Например, при наличии нескольких унаследованных методов с одинаковыми именами.

eDBErrorBadAddress

            Внутренняя ошибка Dss. Неверное расположение какого-либо объекта или системной информации в файлах базы данных.

eDBErrorBadArgument

            Неверное значение аргумента. Для указателя передается нулевое значение, либо значение является недопустимым для какого-либо аргумента. Например, режим c_uiDssMode_Unknown нельзя указывать в качестве аргумента метода dbSwitchMode.

            Если эта ошибка возникает в методах dbOpen, dbSwitchMode, а значения аргументов корректны, то описание схемы данных, влинкованное в программу, не совпадает с описанием схемы данных в открываемой базе.

eDBErrorCacheModeSelected

            Параметры кэширования указываются после того, как они были выбраны и начали использоваться. Например, метод dbSetCacheMode вызывается после метода dbOpen.

eDBErrorDamagePossible

            Длина файла с восстановочной информацией отлична от нуля. База данных может быть повреждена.

eDBErrorDestroyLocked

            Попытка удаления какого-либо объекта, заблокированного параллельной транзакцией.

eDBErrorError

            Неклассифицируемая ошибка там, где ее не ожидали. Как правило, связана с ошибками ввода-вывода.

eDBErrorFileIDUnknown

            Внутренняя ошибка Dss. В восстановочном файле найден неизвестный блок.

eDBErrorFileNotOpen

            Какой-либо из файлов базы данных не был открыт.

eDBErrorFileOpened

            Внутренняя ошибка Dss. Попытка повторного открытия уже открытого файла.

eDBErrorFreeID

            Попытка обращения к удаленному объекту.

eDBErrorInIteration

            Попытка выполнения действия, запрещенного при проведении итерации. Например, попытка создания объекта.

eDBErrorInvalidID

            Попытка использования неверного идентификатора объекта.

eDBErrorInvalidObjectName

            Указанное имя объекта не является уникальным в базе данных.

eDBErrorInvalidScheme

            Описание схемы данных, влинкованное в программу, не соответствует схеме данных в БД.

eDBErrorInvalidType

            Попытка использования неверного идентификатора типа.

eDBErrorInvalidTypeName

            Указанное имя клона либо содержит недопустимые символы, либо не является уникальным в схеме данных.

eDBErrorLoaded

            Попытка повторной загрузки уже загруженного объекта, либо попытка начала итерации в режиме ReadOnly при наличии объектов, загруженных в режиме ReadWrite или WriteShare.

eDBErrorLockDisabled

            Внутренняя ошибка Dss. Невозможно установить указанную блокировку.

eDBErrorMethodAlreadyDefined

            Внутренняя ошибка Dss. Возникает если в программе используется неверно сформированное описание схемы данных.

eDBErrorMethodNotImplemented

            Попытка обращения к методу, не реализованному в схеме данных программы.

eDBErrorNoCompatible

            Попытка открытия базы данных, созданной более старшей версией Dss.

eDBErrorNoCompatibleOld

            Попытка открытия базы данных, созданной старой, уже не поддерживаемой, версией Dss.

eDBErrorNoMemory

            Недостаточно памяти для выполнения действия.

eDBErrorNotFound

            Неудачное завершение поиска.

eDBErrorNotImplemented

            Вызванный метод Dss больше не поддерживается. Методами Dss v.4.20 не возвращается. Введен для совместимости с предыдущими версиями Dss и для будущего использования.

eDBErrorNotLoaded

            Попытка обращения к незагруженному объекту.

eDBErrorNotOpen

            Попытка работы с неоткрытой базой данных.

eDBErrorNoTransactions

            В данный момент времени нет незавершенных транзакций.

eDBErrorObjCopyUnknown

            Невозможно определить копию загруженного объекта, для которой необходимо выполнить действие.

eDBErrorObjInit

            Внутренняя ошибка Dss. Попытка повторной инициализации объекта.

eDBErrorObjNotInit

            Внутренняя ошибка Dss. Попытка обращения к неинициализированному объекту.

eDBErrorOffsetUnknown

            Смещение атрибута еще не определено.

eDBErrorOk

            Нормальное завершение действия.

eDBErrorOpen

            Попытка повторного открытия базы данных.

eDBErrorReadLocked

            Попытка чтения объекта, заблокированного параллельной транзакцией от операций чтения.

eDBErrorReadOnlyMode

            Попытка выполнения действия, запрещеного в режиме ReadOnly. Например, создание объекта, если база данных открыта в режиме ReadOnly.

eDBErrorRpItemNotExist

            Внутренняя ошибка Dss. Следующего блока в восстановочной информации не существует.

eDBErrorRpItemNotFinished

            Внутренняя ошибка Dss. Текущий блок в восстановочной информации не завершен.

eDBErrorRpItemNotStarted

            Внутренняя ошибка Dss. Нет текущего блока в восстановочной информации.

eDBErrorRpLevel1

            Попытка начать пользовательскую транзакцию в базе данных, открытой с уровнем детализации RpLevel1.

eDBErrorSameMethodMissing

            Порождалась предыдущими версиями Dss при несовпадении схем данных программы и базы данных.

eDBErrorSameTypeInfoMissing

            Порождалась предыдущими версиями Dss при несовпадении схем данных программы и базы данных.

eDBErrorSourceIsClone

            Попытка создания клона клона.

eDBErrorThreadAttached

            Текущая нить уже рассматривается Dss как отдельный процесс.

eDBErrorThreadNotAttached

            Текущая нить не рассматривается Dss как отдельный процесс.

eDBErrorTransactionTerminated

            Работа с базой данных была прервана вызовом метода transactionTerminate.

eDBErrorTypecastDenied

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

eDBErrorTypeInfoAlreadyDefined

            Внутренняя ошибка Dss. Возникает если в программе используется неверно сформированное описание схемы данных.

eDBErrorTypeNotImplemented

            Попытка обращения к типу, не реализованному в схеме данных программы.

eDBErrorWriteLocked

            Попытка изменения какого-либо ресурса, заблокированного параллельной транзакцией в режиме только-чтение.

Соответствие числовых кодов ошибок и элементов TDssDBError

Код

Элемент перечисления TDssDBError

0

eDBErrorOk

1

eDBErrorError

2

eDBErrorFileNotOpen

3

eDBErrorNoMemory

4

eDBErrorOpen

5

eDBErrorNotOpen

6

eDBErrorInvalidType

7

eDBErrorNotFound

8

eDBErrorInvalidID

9

eDBErrorInIteration

10

eDBErrorLoaded

11

eDBErrorNotLoaded

12

eDBErrorBadAddress

13

eDBErrorBadArgument

14

eDBErrorFreeID

15

eDBErrorNoCompatible

16

eDBErrorNoCompatibleOld

17

eDBErrorAccessDenied

18

eDBErrorNotImplemented

19

eDBErrorFileOpened

20

eDBErrorRpItemNotStarted

21

eDBErrorRpItemNotFinished

22

eDBErrorRpItemNotExist

23

eDBErrorAbstractType

24

eDBErrorObjCopyUnknown

25

eDBErrorDamagePossible

26

eDBErrorReadOnlyMode

27

eDBErrorObjInit

28

eDBErrorObjNotInit

29

eDBErrorRpLevel1

30

eDBErrorNoTransactions

31

eDBErrorTransactionTerminated

32

eDBErrorFileIDUnknown

33

eDBErrorTypeInfoAlreadyDefined

34

eDBErrorSameTypeInfoMissing

35

eDBErrorSourceIsClone

36

eDBErrorInvalidTypeName

37

eDBErrorMethodAlreadyDefined

38

eDBErrorSameMethodMissing

39

eDBErrorOffsetUnknown

40

eDBErrorTypecastDenied

41

eDBErrorAmbiguous

42

eDBErrorCacheModeSelected

43

eDBErrorInvalidObjectName

44

eDBErrorInvalidScheme

45

eDBErrorThreadAttached

46

eDBErrorThreadNotAttached

47

eDBErrorAccessLocked

48

eDBErrorWriteLocked

49

eDBErrorReadLocked

50

eDBErrorDestroyLocked

51

eDBErrorTypeNotImplemented

52

eDBErrorMethodNotImplemented

53

eDBErrorLockDisabled


Утилиты Dss v.4.20

            Утилиты Dss v.4.20 предназначены для администрирования баз данных Dss.

            Утилиты располагаются в каталоге dss/bin (dss - путь к каталогу с Dss).

            Утилиты используют интерфейс командной строки. Если утилита запускается без параметров, то на стандартный поток вывода отображается краткое описание утилиты и всех ее параметров.

            Все утилиты возвращают следующие общие коды возврата:

0

нормальное завершение работы;

1

неверные аргументы.

            Утилиты dssreg, dssrpr используют каталог для временных файлов. Имя этого каталога задается в аргументе -tmp. Если оно не задано, то берется имя каталога из переменных среды TMP, TEMP. Если эти переменные не заданы, то в качестве каталога для временных файлов используется текущий каталог.

dsschmd: изменение режима базы данных

            Перевод базы данных в режим только-чтение или чтение-запись осуществляет утилита dsschmd.

            Аргументы утилиты dsschmd:

-db <имя>

задает имя базы данных;

-m {readonly | readwrite}

задает требуемый режим базы данных.

            При изменении режима базы данных в стантартный поток вывода выводится старый и новый режимы базы данных.

            Примеры:

 

Установка режима только-чтение

>dsschmd -db mydatabase -m readonly

Old database mode is: readwrite

New database mode is: readonly

 

Установка режима чтение-запись

>dsschmd -db mydatabase -m readwrite

Old database mode is: readonly

New database mode is: readwrite

 

            Коды возврата, специфичные для утилиты dsschmd:

2

база данных или один из ее файлов не существует;

3

база данных создана более старшей версией Dss;

4

база данных повреждена;

5

неожиданная низкоуровневая ошибка.

dsscreat: cоздание базы данных

            Создание базы данных осуществляет утилита dsscreat. Â ñõåìó äàííûõ ñîçäàííîé áàçû äàííûõ ïîìåùàåòñÿ òèï TDssID.

            Аргументы утилиты dsscreat:

-db <имя>

задает имя создаваемой базы данных;

-o

указывает на перезапись базы данных. Если этот флаг не указан, а база данных существует, то перезапись не производится.

            Примеры:

 

Создание базы данных без перезаписи существующей базы

>dsscreat -db mydatabase

 

Создание базы данных с перезаписью существующей базы

>dsscreat -db mydatabase -o

 

            Коды возврата, специфичные для утилиты dsscreat:

2

база данных существует, но создана более старшей версией Dss. База данных с указанным именем не может быть создана;

3

база данных существует, но ключ ‘-o’ в командной строке не указан. База данных не пересоздается;

4

неожиданная низкоуровневая ошибка.

            Примечание. Если обнаружен файл .di, созданный более старшей версией Dss, то наличие остальных файлов базы данных не проверяется.

dsspt: печать зарегистрированных типов базы данных

            Печать зарегистрированных типов базы данных осуществляет утилита dsspt. Печать типов производится на стандартный вывод. Типы выводятся на языке DssDDL.

            Аргументы утилиты dsspt:

-db <имя>

задает имя базы данных;

-cloneonly

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

            Примеры:

 

Отображение схемы данных на экран

>dsspt -db mydatabase

 

Занесение схемы данных в файл для последующего редактирования

>dsspt -db mydatabase > myscheme.ddl

 

Занесение в файл описаний клонов

>dsspt -db mydatabase -cloneonly > myscheme.dll

 

            Коды возврата, специфичные для утилиты dsspt:

2

база данных или один из ее файлов не существует;

3

база данных создана более старшей версией Dss;

4

база данных повреждена;

5

неожиданная низкоуровневая ошибка;

            Примечание. Тип TDssID в списке типов не отображается.

dssreg: регистрация типов в базе данных

            Регистрацию типов в базе данных осуществляет утилита dssreg. Данная утилита обрабатывает передаваемое ей описание схемы данных на DssDDL и регистрирует типы в базе данных. Не указанные в описании типы из базы данных удаляются. После регистрации типов производится перестройка базы данных и отражение всех изменений на существующие объекты базы данных. Например, если из какого-либо типа был удален атрибут, то этот атрибут удаляется из всех существующих объектов этого типа. Если из схемы данных был изъят какой-либо тип, то в базе данных удаляются все объекты этого типа. Если для удаляемых объектов существовали имена, то имена также удаляются.

            Преобразование базы данных осуществляется следующим образом:

            1) в каталоге для временных файлов создается новый файл .tl под именем dssreg.tl. В него помещается новая схема данных;

            2) в каталоге для временных файлов создаются пустые новые файлы .of0, .r0 и .nms под именами dssreg.of0, dssreg.r0, dssreg.nms;

            3) в новые файлы .of0 и .r0 помещаются преобразованные объекты из исходных файлов. В новый файл .nms помещаются имена, которые относятся к существующим объектам;

            4) удаляются исходные файлы .tl, .of0, .r0, .nms. Если в этот момент происходит аварийное завершение работы (сбой питания, например), то восcтановить базу данных можно, взяв dssreg.tl, dssreg.of0, dssreg.r0, dssreg.nms из каталога для временных файлов и переименовав их в исходные файлы;

            5) файлы dssreg.tl, dssreg.of0, dssreg.r0, dssreg.nms из каталога для временных файлов копируются под именами исходных файлов;

            6) удаляются временные файлы dssreg.tl, dssreg.of0, dssreg.r0, dssreg.nms.

            Аргументы утилиты dssreg:

-db <имя>

задает имя базы данных;

-desc <имя>

задает имя с описанием типов;

-code <имя>

задает имя выходного C++-файла с кодом инициализации объектов;

-head <имя>

задает имя выходного C++-файла заголовка с макросами для описания типов на C++;

-v

указывает выдать список изменений в базе данных без их осуществления;

-codetop <имя>

задает имя файла, содержимое которого должно быть помещено в начало генерируемого кода;

-codebottom <имя>

задает имя файла, содержимое которого должно быть помещено в конец генерируемого кода;

-tmp <путь>

задает имя каталога для временных файлов.

            Примеры:

 

Самый простой способ регистрации типов

>dssreg -db mydatabase -desc myscheme.ddl -code dbinit.cpp -head dbinit.hpp

 

Регистрация типов и помещение вспомогательного кода в нужные каталоги

>dssreg -db mydatabase -desc myscheme.ddl -code src/dbinit/dbinit.cpp -head src/dbinit/h/dbinit.hpp

 

Регистрация типов и вставка во вспомогательный код дополнительных действий

>dssreg -db mydatabase -desc myscheme.ddl -code src/dbinit/dbinit.cpp -head src/dbinit/h/dbinit.hpp -codetop src/dbinit/codetop.cpp -codebottom src/dbinit/codebottom.cpp

 

            Коды возврата, специфичные для утилиты dssreg:

2

база данных создана более старшей версией Dss;

3

база данных не существует;

4

база данных повреждена;

5

база данных находится в режиме только-чтение;

6

неожиданная низкоуровневая ошибка;

7

не найден файл-описания;

8

синтаксическая ошибка в описании;

9

в описании типов базы данных найден неизвестный блок (в нормальном состоянии базы данных эта ошибка не должна возникнуть);

10

недопустимое использование абстрактного типа (используется как тип атрибута или как исходный тип для клона);

100 и более

семантическая ошибка в описании.

            Примечания.

            1. Для облегчения администрирования базы данных и отслеживания схемы данных при разработке программного обеспечения несколькими программистами можно использовать командные файлы следующего вида (OS/2, Win32):

 

@echo off

echo Create database (if needed) and register types

rem

rem Add common types

rem

type src\dbinit\common.ddl > \tmp\scheme.ddl

rem

rem Add types of programer #1

rem

type src\dbinit\prog1scheme.ddl >> \tmp\scheme.ddl

...

rem

rem Add types of programer #N

rem

type src\dbinit\progNscheme.ddl >> \tmp\scheme.ddl

rem

rem If database do not exist then create it

rem

if not exist db\database.di dsscreat -db db\database

rem

rem Register types

rem

dssreg -db db\database -desc \tmp\scheme.ddl -tmp \tmp -code src\dbinit\dbinit.cpp -head src\dbinit\h\dbinit.hpp -codetop src\dbinit\codetop.cpp -codebottom src\dbinit\codebottom.cpp

del \tmp\scheme.ddl

 

            2. Если при администрировании схемы базы данных необходимо учитывать клоны, зарегистрированные в БД при помощи метода TDssKernelInterface::cloneCreate, то предыдущий пример можно модифицировать следующим образом:

 

@echo off

echo Create database (if needed) and register types

rem

rem Add common types

rem

type src\dbinit\common.ddl > \tmp\scheme.ddl

rem

rem Add types of programer #1

rem

type src\dbinit\prog1scheme.ddl >> \tmp\scheme.ddl

...

rem

rem Add types of programer #N

rem

type src\dbinit\progNscheme.ddl >> \tmp\scheme.ddl

 

rem

rem If database exist then extract clones description

rem =================================================

rem

if exist db\database.di dsspt -db db\database -cloneonly >> \tmp\scheme.ddl

 

rem

rem If database do not exist then create it

rem

if not exist db\database.di dsscreat -db db\database

rem

rem Register types

rem

dssreg -db db\database -desc \tmp\scheme.ddl -tmp \tmp -code src\dbinit\dbinit.cpp -head src\dbinit\h\dbinit.hpp -codetop src\dbinit\codetop.cpp -codebottom src\dbinit\codebottom.cpp

del \tmp\scheme.ddl

dssrpr: восcтановление базы данных

            Восcтановление базы данных осуществляется утилитой dssrpr.

            Восcтановление базы данных осуществляется следующим образом:

            1) в каталог для временных файлов копируются файлы .tl, .of0, .r0 и .nms под именами dssrpr.tl, dssrpr.of0, dssrpr.r0, dssrpr.nms. Делается это для того, чтобы в случае аварийного завершения работы (при сбое питания, например), исходные файлы БД остались в том же состоянии, что и до начала восcтановления;

            2) для временных файлов происходит отмена действий, описанных в rp1;

            3) удаляются исходные файлы .tl, .of0, .r0 è .nms. Если в этот момент происходит аварийное завершение работы (сбой питания, например), то восcтановить базу данных можно, взяв dssrpr.tl, dssrpr.of0, dssrpr.r0, dssrpr.nms из каталога для временных файлов и переименовав их в исходные файлы;

            4) файлы dssrpr.tl, dssrpr.of0, dssrpr.r0, dssrpr.nms из каталога для временных файлов копируются под именами исходных файлов;

            5) удаляются временные файлы dssrpr.tl, dssrpr.of0, dssrpr.r0, dssrpr.nms.

            Аргументы утилиты dssrpr:

-db <имя>

задает имя базы данных;

-tmp <путь>

задает имя каталога для временных файлов.

            Ïðèìåð:

 

>dssrpr -db mydatabase -tmp mytmppath

 

            Коды возврата, специфичные для утилиты dssrpr:

2

база данных создана более старшей версией Dss;

3

база данных не существует;

6

неожиданная низкоуровневая ошибка.

dssscg: раздельная генерация вспомогательного кода

            Утилита dssscg предназначена для генерации вспомогательного кода по зарегистрированной в БД схеме данных. Утилита dssscg позволяет указывать расположение вспомогательных функций, векторов dss_meth_*, макросов dss_type_*, векторов type_methods и type_info в конкретных C++ файлах. При использовании dssscg имена для векторов type_methods и type_info задаются программистом.

            Для dssscg подготавливается файл описания размещения вспомогательного кода. Файл описания - это текстовый файл, в котором специальными командами назначаются выходные C++ файлы и их содержимое. На вход dssscg подаются имена БД и файла описания. Из указанной БД считывается схема данных, и генерируется вспомогательный код в соответствии с указанным описанием.

            Файл описания обрабатывается построчно. Каждая прочитанная строка анализируется. Если в строке содержится команда dssscg, то данная команда выполняется. В противном случае вся строка помещается в текущий выходной файл. Если выходной файл не назначен, то строка игнорируется. Одна строка может содержать только одну команду и ее параметры. Параметры команды должны указываться в той же строке.

            Аргументы утилиты dssscg:

-db <имя>

задает имя базы данных;

-codedesc <имя>

задает имя файла с описанием расположения вспомогательного кода.

            Команды управления расположением вспомогательного кода:

dssfile <имя>

назначает очередной выходной файл. Прежний выходной файл (если назначен) закрывается. Новый выходной файл создается заново (если указанный файл существовал, то его содержимое теряется);

dsstype <имя>

предписывает вывести в текущий выходной файл вспомогательные функции и вектор dss_meth_* для указанного типа;

dsshead

предписывает вывести в текущий выходной файл раскрытие макросов dss_type_* для всех типов схемы данных и прототипы векторов type_methods и type_info. Имена векторов type_methods и type_info берутся из параметров команд dsstypemethods и dsstypeinfo;

dsscode

предписывает вывести в текущий выходной файл вектора type_methods и type_info. Имена векторов type_methods и type_info берутся из параметров команд dsstypemethods и dsstypeinfo;

dsstypeinfo <имя>

назначает имя для вектора type_info. Команда должна предшествовать командам dsshead и dsscode;

dsstypemethods <имя>

назначает имя для вектора type_methods. Команда должна предшествовать командам dsshead и dsscode.

            Коды возврата, специфичные для утилиты dssscg:

2

база данных или один из ее файлов не существует;

3

база данных создана более старшей версией Dss;

4

база данных повреждена;

5

неожиданная низкоуровневая ошибка;

6

тип с указанным именем не найден;

7

неизвестная команда или неверный формат команды.

dssvid: получение информации об идентификаторе

            Получить информацию о любом идентификаторе базы данных можно с помощью утилиты dssvid. На стандартный вывод выводятся: имя базы данных, указанный идентификатор, статус идентификатора (соответствует существующему объекту, соответствует удаленному объекту, не существует в базе данных). Если идентификатор соответствует существующему объекту, то выводится также следующая информация: идентификатор типа объекта; имя типа объекта; файл, в котором находится объект; смещение объекта в файле.

            Аргументы утилиты dssvid:

-db <имя>

задает имя базы данных;

-id <ID>

задает идентификатор.

Идентификатор указывается в следующей форме:

 

[{<number>|{0x|0X}<hexnumber>}:]{<number>|{0x|0X}<hexnumber>}

 

где

            number - десятичное число;

            hexnumber - шестнадцатиричное число.

            Примеры:

 

>dssvid -db db\ot2_v4 -id 0x0:0x4332

Database: db\ot2_v4

ID: 0x0:0x4332

Status: exist

Object information:

       idtype: 1021

       type name: TV3MeasurePoint

       location:

             file: objfile0

             offset: 0x8d174 (577908)

 

>dssvid -db db\ot2_v4 -id 17202

Database: db\ot2_v4

ID: 0x0:0x4332

Status: exist

Object information:

       idtype: 1021

       type name: TV3MeasurePoint

       location:

             file: objfile0

             offset: 0x8d174 (577908)

 

>dssvid -db db\ot2_v4 -id 0X1:0X0

Database: db\ot2_v4

ID: 0x1:0x0

Status: not exist

 

            Коды возврата, специфичные для утилиты dssvid:

2

база данных или один из ее файлов не существует;

3

база данных создана более старшей версией Dss;

4

база данных повреждена;

5

неожиданная низкоуровневая ошибка.

dssvnms: просмотр существующих индивидуальных имен объектов

            Просмотр существующих индивидуальных имен объектов осуществляет утилита dssvnms. Существующие имена и идентификаторы объектов выводятся на стандартный вывод.

            Аргумент утилиты dssvnms:

-db <имя>

задает имя базы данных.

            Пример:

 

>dssvnms -db mydatabase

0x00000000:0x000001FF2: title_on_image

0x00000000:0x00000003D: factory

0x00000000:0x000001234: root_object

 

            Коды возврата, специфичные для утилиты dssvnms:

2

база данных или один из ее файлов не существует;

3

база данных создана более старшей версией Dss;

4

база данных повреждена;

5

неожиданная низкоуровневая ошибка.


Соотношение Dss v.4.20 и других СУООБД

            На сегодняшний день в мире существуют несколько коммерческих СУООБД (ObjectStore и ObjectStore PSE фирмы ObjectDesign, Inc; Objectivity фирмы Objectivity/DB; Versant фирмы Versant Object Technology; O2 фирмы O2 Tecnology; POET фирмы POET Software GmbH; Jasmine фирмы Computer Associates; Ontos DB фирмы Ontos, Inc; Gemstone фирмы Gemstone, Inc; ODB-Jupiter российской фирмы НПЦ “Интелтек Плюс”) и консорциум фирм производителей СУООБД ODMG. Консорциум ODMG занимается унификацией отдельных компонент СУООБД - языка описания данных (ODL), языка объектных запросов (OQL) и связывания СУООБД с языком программирования, что должно обеспечить, по мнению ODMG, мобильность прикладных систем относительно различных СУООБД. Результатом деятельности ODMG является стандарт ODMG-93, который в большей или меньшей степени поддерживается в перечисленных выше продуктах.

            Dss начала разрабатываться в 1995 году. При создании двух первых работающих версий Dss v.1.00 и v.2.00 информация о ситуации в области объектно-ориентированных баз данных была недоступна. В связи с этим до конца 1996 года Dss развивалась самостоятельно и совершенно изолированно от других подобных систем. Это обеспечило Dss ряд отличительных черт, отсутствующих как в стандарте ODMG-93, так и в других СУООБД.

Явное преобразование идентификатор/указатель

            В ряде СУООБД (Versant, ObjectStore, Gemstone, POET, O2) применяется неявное преобразование идентификаторов в указатели и обратно. Понятие идентификатора существует на системном уровне, скрытом от прикладного программиста. Для преобразования идентификатор/указатель каждая СУООБД применяет собственный механизм.

            Dss не использует механизмов автоматического преобразования идентификатор/ указатель. В Dss это необходимо осуществлять явно:

 

// DssDDL

struct T {

       TDssID m_idRef;

};

// C++

class  T {

       dss_type( T );

       protected :

             dss_attr TDssID m_idRef;

             T * m_pRef;

             ...

       public :

             static void  load( TDssKernelInterface & dss,

                    const TDssID & id, T * & pt ) {

                    dss.objLoad( id, c_uiDssMode_ReadWrite,

                           DSS_ADDRESS( pt ) );

                    if( c_idDssInvalid != pt->m_idRef )

                           dss.objLoad( pt->m_idRef,

                                  c_uiDssMode_ReadWrite,

                                  DSS_ADDRESS( pt->m_pRef ) );

             }

};

 

Данный способ работы с идентификаторами был заложен в первую версию Dss и остается неизменным по соображениям совместимости. Самый главный недостаток этого способа - трудоемкость. Каждую операцию чтения-записи объектов необходимо программировать отдельно. Достоинством этого способа является гибкость. Можно запрограммировать именно такой способ загрузки-выгрузки объектов, который необходим для конкретной задачи.

Режимы загрузки объектов

            В отличие от остальных СУООБД Dss предоставляет программисту три различных способа загрузки объектов: ReadOnly, ReadWrite, WriteShare. ODMG-93 предусматривает только один способ загрузки объектов - аналог режима WriteShare. При этом все пользователи загруженного объекта работают с одним и тем же блоком памяти. В случае необходимости эксклюзивного доступа к объекту необходимо использовать механизмы блокировки объектов.

            Dss для эксклюзивного доступа предоставляет режим ReadWrite. В этом случае не нужно применять каких-либо средств блокировки, что снижает вероятность возникновения тупиковых ситуаций.

            Наиболее уникальные возможности предоставляет режим загрузки ReadOnly. В этом режиме объект можно загружать многократно. Если размер объекта в ОП превышает размер объекта в БД, то для каждой загрузки в ОП создается отдельная копия объекта. Это позволяет загружать объекты в режиме ReadOnly одновременно для разных целей и не беспокоиться о расширяющих полях. Так, объект типа TImage может иметь расширяющие поля m_pDevice (устройство отображения) и m_scale (масштаб отображения), зависящие от того, на какое устройство изображение выводится:

 

class  TImage {

       dss_type( TImage );

       protected :

             TPaintDevice * m_pDevice;

             float m_scale;

             ...

             static void  load( TDssKernelInterface & dss,

                    const TDssID & id, TImage * & pimage ) {

                    ...

             }

       public :

             ...

             // Загрузка изображения и подготовка к отображению

             // в окне

             static void  loadToWindow(

                    TDssKernelInterface & dss,

                    const TDssID & id, TImage * & pimage,

                    TWindow * pwindow ) {

                    load( dss, id, pimage );

 

                    pimage->m_pDevice = new TPaintDeviceWindow( pwindow );

                    pimage->m_scale = 1.0;

             }

 

             // Загрузка изображения и подготовка к отображению

             // на принтере

             static void  loadToPrinter(

                    TDssKernelInterface & dss,

                    const TDssID & id, TImage * & pimage,

                    TPrinter * pprinter ) {

                    load( dss, id, pimage );

 

                    pimage->m_pDevice = new TPaintDevicePrinter( pprinter );

                    pimage->m_scale = 0.5;

             }

};

 

В этом случае объект TImage можно одновременно загрузить в несколько разных окон и отображать на принтере. И у каждого объекта будут собственные значения атрибутов m_pDevice и m_scale. Для получения такого же эффекта в ODMG-93 потребуется вынести атрибуты m_pDevice и m_scale в отдельный нехранимый класс, и в этом классе инкапсулировать свойства класса TImage. Такой подход затрудняет разработку программ прямо пропорционально количеству подобных классов.

Отсутствие единого корня иерархии классов

            Стандарт ODMG-93 предполагает единую вершину в иерархии классов - класс d_Object. В большинстве СУООБД в вершине иерархии хранимых классов должен располагаться специальный класс или набор классов (Object в Ontos, Pobject в Versant, GS_Object в Gemstone, PtObject в POET, TOdbObject в ODB-Jupiter).

            Наличие единого корня в иерархии классов является вполне нормальным решением для языков программирования, допускающих только одиночное наследование (Smalltalk, Java, Ada). Однако, в C++ это может стать дополнительной сложностью при использовании множественного наследования. Так, в POET хранимые типы, являющиеся базовыми для какого-либо типа, должны иметь тип PtObject виртуальным базовым типом. Наличие в объектной модели виртуальных базовых типов требует к себе очень внимательного и осторожного отношения. Если же множественное наследование потребовалось вводить уже в процессе использования базы данных, то перепроектирование классов и их перепрограммирование потребует дополнительных трудозатрат. Внесение множественного наследования в модель, изначально подготовленную для одиночного наследования, значительно увеличивает вероятность появления множества скрытых и трудноуловимых ошибок, присущих именно множественному наследованию.

            Dss не накладывает на объектную модель ограничений в виде общего корня. Поэтому построение объектных моделей, использующих множественное наследование, в Dss проще, чем в ODMG-93 и основанных на нем СУООБД, поскольку пользователь может проектировать объектную модель, исходя только из собственных предположений.

Именованные методы

            Dss v.4.20 представляет механизм именованных методов. Программист посредством специальных средств Dss по имени метода получает адрес процедуры и может делать с ним все, что пожелает:

 

/* Предполагаем, что объект обладает методом keyString следующего формата:

void   keyString( TDssKernelInterface & dss, const TDssID & id, char * psz, size_t size );

*/

typedef void (*TPFnKeyString)( TDssKernelInterface & dss, const TDssID & id, char * psz, size_t size );

void   ShowObjKeyString( TDssKernelInterface & dss, const TDssID & id ) {

       TPFnKeyString pfn;

       char szKeyString[ 100 ] = “No description”;

       if( eDBErrorOk == dss.methodFindByObject( id, “keyString”,

             DSS_METHOD_CAST( pfn ) ) )

             (*pfn)( dss, id, szKeyString, sizeof szKeyString );

       cout << szKeyString << endl;

}

 

Принимая во внимание то, что объект с идентификатором id является обычным C++ объектом, но хранящимся в БД, можно переписать этот пример так:

 

// Это не C++!

void   ShowObjKeyString( TObj * p ) {

       char szKeyString[ 100 ] = “No description”;

       // В C++ нельзя вызывать метод таким образом

       p->“keyString”( szKeyString, sizeof szKeyString );

       cout << szKeyString << endl;

}

 

Однако, нормальный результат в этом случае не гарантирован. Во-первых, у объекта может не оказаться именованного метода с именем keyString. В этом случае вместо ключевой строки будет отображено “No description”. Во-вторых, реальный формат метода keyString может оказаться другим. Тогда следует ожидать самых худших для программы последствий.

            Эта ситуация является обычной для не строго типизированных языков, например, Smalltalk. В них так же можно вызвать у объекта метод, которого не существует, что приведет к ошибке времени исполнения. Механизм именованных методов Dss позволяет добавить в C++ такое свойство нетипизированных языков, как возможность вызова методов по именам. Можно сказать, что Dss вносит в C++ ряд черт Smalltalk.

            Строгая и нестрогая типизация имеют свои сильные и слабые стороны. Строгая типизация позволяет увеличить скорость работы программ, поскольку в процессе исполнения не надо делать лишних проверок. Нестрогая типизация позволяет строить более гибкие объектные модели. Предположим, что все объекты БД должны иметь метод:

 

void   dump( ostream & o );

 

В C++ этого можно достигнуть, только введя общий корень иерархии типов:

 

class  TObjRoot {

       public :

             virtual void dump( ostream & o );

             ...

};

 

Предположим также, что метод TObjRoot::dump не является чистым виртуальным методом, а выполняет какую-то работу. Т.о., метод dump для класса, производного от TObjRoot, должен выглядеть так:

 

void   TDerivedFromObjRoot::dump( ostream & o ) {

       TObjRoot::dump( o );

       ...

}

 

Предположим, что в объектной модели понадобилось множественное наследование:

 

class  TDerived1 : virtual public TObjRoot {

       public :

             void   dump( ostream & o ) {

                    TObjRoot::dump( o );

                    ...

             }

             ...

};

class  TDerived2 : virtual public TObjRoot {

       public :

             void   dump( ostream & o ) {

                    TObjRoot::dump( o );

                    ...

             }

             ...

};

class  TDerived3 : public TDerived1, public TDerived2 {

       public :

             void   dump( ostream & o ) {

                    TDerived1::dump( o );

                    TDerived2::dump( o );

                    ...

             }

             ...

};

 

В методе TDerived3::dump произойдет классическая для виртуального множественного наследования ошибка: для единственной копии TObjRoot метод dump будет вызван дважды - сначала в TDerived1::dump, затем в TDerived2::dump. Для исправления этой ошибки придется поступить следующим образом:

 

class  TObjRoot {

       protected :

             // Этот метод выполняет действия метода dump

             void   _dump( ostream & o ) {

                    ...

             }

       public :

             virtual void dump( ostream & o ) {

                    _dump( o );

             }

             ...

};

class  TDerived1 : virtual public TObjRoot {

       protected :

             void   _dump( ostream & o );

       public :

             void   dump( ostream & o ) {

                    TObjRoot::_dump( o );

                    _dump( o );

             }

             ...

};

class  TDerived2 : virtual public TObjRoot {

       protected :

             void   _dump( ostream & o );

       public :

             void   dump( ostream & o ) {

                    TObjRoot::_dump( o );

                    _dump( o );

             }

             ...

};

class  TDerived3 : public TDerived1, public TDerived2 {

       protected :

             void   _dump( ostream & o );

       public :

             void   dump( ostream & o ) {

                    TObjRoot::_dump( o );

                    TDerived1::_dump( o );

                    TDerived2::_dump( o );

                    _dump( o );

             }

             ...

};

 

Очевидно, что такой подход более трудоемок и череват малозаметными ошибками. Например, стоит забыть вставить вызов какого-либо метода из базового класса, и это приведет к длительным поискам причин неправильной работы программы. При использовании именованных методов можно обойтись без единого корня и упростить объектную модель:

 

class  TClass1 {

       public :

             dss_meth void dump( TClass1 & obj, ostream & o ) {

                    ...

             }

             ...

};

class  TClass2 {

       public :

             dss_meth void dump( TClass2 & obj, ostream & o ) {

                    ...

             }

             ...

};

class  TDerived : public TClass1, public TClass2 {

       public :

             dss_meth void dump( TDerived & obj, ostream & o ) {

                    TClass1::dump( obj, o );

                    TClass2::dump( obj, o );

                    ...

             }

             ...

};

 

            Еще один пример. Допустим, что часть типов объектной модели является “высокоуровневыми” типами, использующими остальные типы. Объекты высокоуровневых типов можно создавать, редактировать и удалять в специальной программе-конструкторе. Программа-конструктор использует для этого следующие методы:

 

void   createObjView( TWindow * pOwner );

void   createObj( TDssID & id );

void   destroyObj( const TDssID & id );

void   editObj( const TDssID & id );

 

Каждый высокоуровневый тип должен обладать этими методами. Для этого необходимо ввести базовый класс:

 

class  TTopLevelType {

       public :

             virtual void editObj( const TDssID & id );

             virtual void destroyObj( const TDssID & id );

             ...

};

 

Однако, методы createObjView и createObj относятся не к конкретному объекту, а к типу. Поэтому они должны объявляться в классе TTopLevelType как статические. В производных классах эти методы так же будут реализовываться как статические. Для вызова статического метода в C++ всегда необходимо явно указывать класс, из которого этот метод вызывается. Кроме того, в C++ нельзя определить адрес метода по его имени. Поэтому в программе-конструкторе необходимо сразу описать адреса методов createObjView и createObj для всех высокоуровневых типов. При дополнении объектной модели новыми высокоуровневыми типами необходимо модифицировать программу-конструктор. Механизм именованных методов Dss в этом примере позволяет организовать полиморфизм для статических методов:

 

class  TTopLevelType {

       public :

             dss_meth void createObjView( TWindow * powner );

             dss_meth void createObj( TDssID & id );

             dss_meth void editObj( const TDssID & id );

             dss_meth void destroyObj( const TDssID & id );

             ...

};

class  TType1 : public TTopLevelType {

       public :

             dss_meth void createObjView( TWindow * powner );

             ...

};

class  TType2 : public TTopLevelType {

       public :

             ...

};

 

typedef void (*TPFnCreateObjView)( TWindow * );

void   CreateObjView( TDssKernelInterface & dss, TDssIDType idt, TWindow * powner ) {

       TPFnCreateObjView pfn;

       if( eDBErrorOk == dss.methodFindByType( idt,

             “createObjView”,

             DSS_METHOD_CAST( pfn ) ) )

             (*pfn)( powner );

}

...

TDssIDType idt1;

TDssIDType idt2;

 

dss.typeFind( “TType1”, idt1 );

dss.typeFind( “TType2”, idt2 );

 

CreateObjView( dss, idt1, powner );

CreateObjView( dss, idt2, powner );

 

В этом примере для типа TType1 будет вызван метод TType1::createObjView. Для типа TType2 будет вызван метод TTopLevelType::createObjView. Произойдет это потому, что тип TType2 не вводит своего метода createObjView, но наследует его из типа TTopLevelType.

            Механизм именованных методов Dss вносит в C++ две важные возможности:

            1) слабую типизацию для методов;

            2) полиморфизм статических методов относительно идентификатора типа.

Выделенные нити

            Dss v.4.20 позволяет рассматривать выделенные нити как отдельные процессы. Выделенные нити могут осуществлять транзакции независимо друг от друга. Данная особенность позволяет реализовывать распараллеливание действий программы на уровне нитей. Так, первая нить может осуществлять диалог с пользователем, вторая нить может подготавливать данные для обработки, третья нить может проводить обработку данных, четвертая - печатать результаты обработки и т.д. При этом Dss отслеживает действия каждой нити отдельно от действий других нитей и осуществляет блокировку измененных данных до окончательного подтверждения или отмены изменений.

            Каждая выделенная нить указывает необходимый ей уровень восстановочной информации, что позволяет управлять быстродействием приложения. Так, не использующие транзакций нити могут указывать уровень восстановочной информации c_uiDssRpLevel1. Работа этих нитей с Dss будет осуществляться быстрее, чем нитей, использующих уровень c_uiDssRpLevel2. В качестве примера можно привести приложение, занимающееся сбором информации (например, данных энергопотребления) в реальном времени и позволяющее осуществлять переконфигурирование без останова приложения. Основные нити программы работают с Dss, используя уровень c_uiDssRpLevel1. Нить, на которой запускается процедура переконфигурирования указывает уровень c_uiDssRpLevel2, т.к. скорость ее работы зависит от скорости работы пользователя. При этом процедура переконфигурирования может использовать транзакции для обеспечения целостности изменяемых данных.



[1] Вызов threadAttach выделяет нить из общего множества нитей процесса. Отсюда вытекают названия “выделенная” или “невыделенная” нить.

[2] На практике регистрация и модификация схемы данных выполняется как одно действие.

[3] На платформе Linux поддержка многопоточности осуществляется с использованием библиотеки pthread, обеспечивающей многопоточность в соответствии со стандартом POSIX (IEEE 1003). Для тестирования поддержки многопоточности использовалась версия RedHat Linux 5.1.

[4] В зарубежной литературе для этого употребляется термин persistance (постоянный).

[5] Название “расширяющий” использовано из-за того, что нехранимые в БД атрибуты увеличивают размер объекта в ОП по сравнению с размером объекта в БД, т.е. расширяют объект.

[6] Размер типа long на 32-битовых платформах составляет 32-бита. На 64-битовых платформах размер типа long может составить 64 бита.

[7] В БД описание типа TDssID хранится в схеме данных так же, как и описания остальных типов.

[8] Абстрактным классом в C++ является класс, имеющий хотя бы один чистый виртуальный метод.

[9] Утилита dsspt во всех случаях использует модификатор abstract для обозначения абстрактных типов.

[10] Перед применением dssreg БД должна быть создана утилитой dsscreat.

[11] Детали сгенерированного кода опущены для упрощения изложения.

[12] Макросы dss_attr и dss_meth зарезервированы для будущего использования.

[13] Конструктором по умолчанию в C++ считается конструктор без параметров.

[14] Программист может задать для модификации схемы параметры, отличные от тех, которые использовались при регистрации схемы. Однако, практика показывает, что в этом нет необходимости.

[15] При компиляции имя C++-функции преобразуется компилятором для указания типов аргументов и возвращаемого значения. Способ преобразования имени зависит от компилятора. Из-за этой особенности преобразования имен C++-функций невозможно линкование объектных файлов и библиотек, созданных разными компиляторами. Имя C-функции либо не преобразуется вовсе, либо в начало имени добавляется символ подчеркивания. Это делает возможным совместное использование библиотек, созданных разными компиляторами (при условии единого формата библиотек).

[16] Подробно уровни восстановочной информации описываются в разделе “Транзакции”.

[17] В данном примере режим загрузки c_uiDssMode_WriteShare позволяет не производить лишних загрузок объекта в функциях SetupTextOnImage и SetupDataReceiver.

[18] Более подробно ограничения на объектную модель излагаются в пособии по языку DssDDL.

[19] В большинстве существующих СУООБД используются неявные средства преобразования идентификатор/указатель и работа со ссылками происходит в них, как с обычными указателями.

[20] В некоторых случаях требуется загрузить только сам объект, без его ссылок. Например, объект-счетчик загружается для изменения его названия. В этом случае при загрузке объекта-счетчика его ссылки не загружаются, а при выгрузке не выгружаются.

[21] Для вызова статического метода не надо загружать объект. Вызов статического метода класса происходит обычным для C++ способом.

[22] Перечисляются только собственные методы типа, без унаследованных.

[23] Dss v.4.20 поддерживает не более 228 идентификаторов. Более старшие версии Dss смогут поддерживать большее количество идентификаторов.

[24] Теоретически, идентификаторов типов может быть 232. Практически, количество типов зависит от суммарного размера их описаний. Этот размер не может превышать 232 байт (4Гб).

[25] Под идентификатором процесса понимается назначаемая Dss величина, не имеющая ничего общего с идентификаторами процессов, назначаемыми операционной системой каждому процессу.

[26] Для выделенных нитей уровень восстановочной информации указывается при вызове threadAttach. Для невыделенных нитей используется уровень восстановочной информации, указанный при открытии БД.

[27] Для простоты изложения данный пример считается корректным. На практике данный пример будет являться корректным только, если макрос dss_check реализован так, чтобы в случае ошибки прерывать цепочку действий. Например, вызывать abort() или порождать исключительные ситуации.

[28] Пространства имен могут быть добавлены в Dss, если будет принято решение об ориентации Dss исключительно на C++.



Hosted by uCoz