|
Новшества в управлении даннымиCLIP поддерживает хранение в MEMO-полях переменных любого типа, включая объекты, массивы. Параллельно с функциями db*() имеется пакет функций rdd*() работающий не с алиасами, а с указателями на открытые таблицы и связанные с ними индексы. Для облегчения работы подобным образом написан класс RDD clip при dbUseArea на самом деле файлы не открывает в том понятии, в котором это понимает DOS. На самом деле открывается не файл, а на него создается MEMORYMAP - отображение содержимого файла в виртуальном адресном пространстве памяти ОС. Т.е. clip обращается не к файловым операциям, а берет данные прямо из памяти. И только, если система не дает этого сделать, тогда идет чтение из файла. Так вот чтобы брать много данных, надо дать процессу возможность адресоваться к такому размеру памяти, в котором должны уместится все используемые данные + индексы. Такие ограничения/разрешения даются при помощи команды shell~а ulimit -v. Clip-программа сама пытается выставить ограничение по максимуму, но это зависит от настроек этого самого ulimit. Вполне возможно, что такое и не удаcтся сделать и тогда clip пишет в log-файл сколько же ему удалось запросить про запас. Эту память можно увидеть в программе top под названием shared. Не надо пугаться, что для этого механизма требуется много физической памяти. Это совсем не так. Эта память используется одновременно несколькими процессами и она одна для всех. Например, ядро пользуется этим же механизмом для обеспечения shared-code, т.е. программы грузятся в память и несколько программ используют статический код, находящийся в одной и той же памяти. Поэтому top, практически у всех программ, показывает некоторое кол-во shared памяти. В связи с этим получилось так, что некоторые операции, которые раньше могли исполнятся только в excl режиме, теперь могут быть выполнены и share-режиме. Например, zap. Не советую, конечно, пользоваться этими возможностями, лучше писать "классически". Параллельно с пакетом функций db...(), которые управляют данными посредством механизма alias & select, имеется пакет функций rdd....(), которые управляют данными через указатель на файл. Например: fd:=rddUseArea(.t.,file_name,......) rddGoTop(fd) rddSkip(fd,nskip) ну и так далее...... /* Есть RDD с аббревиатурой DBFMEM работает так же, как DBFNTX, но не использует дисковых операций! При вызове закрывающих функций для индексов и данных уничтожается память, в которой он существовал. Очень рекомендую использовать для всяческих временных таблиц не слишком большого размера - иначе можете влететь в длинюююююююющий своп. */ Чтобы использовать разные кодировки данных поставьте set("DBF_CHARSET",code_page_file_name), при вызове dbusearea эти установки будут учтены. Имеются функции: dbread() - возвращает объект со структурой, соответствующей структуре dbf текущего select Например: use test // имеет поля field1,field2,....fieldn rec=dbread() ? rec:field1, rec:field2, .... dbwrite(rec) - записывает объект в dbf, равносилен конструкции replace field1 with value1, field2 with value2, .... Например: rec=map() или rec=dbread() rec:field1="asdf" rec:field2=date() dbwrite(rec) dbappend(rec) - добавляет запись и записывает в нее объект. Оторванные индексы (Independed Indices) Оторванный индекс представляет собой обычный индексный файл. Но ключи в него добавляются вручную. Фактически это сортированный список ключей с некоторыми ассоциированными с ними данными. Набор функций для манипулирования оторванными индексами включает в себя функции создания/открытия/закрытия индекса/тега, функции добавления/удаления ключей, а также набор функций для навигации по ключам. В данный момент в качестве оторванного индекса можно использовать только CDX. Реализованы следующие функции: II_CREATE([<driver>],<name>) -> index handle создает пустой индекс (tagbag) без единого тега, используя указанный трехбуквенный драйвер <driver> (например "CDX"). Если драйвер не указан, используется драйвер индексов от текущего RDD (rddsetdefault()). После создания индекс остается открытым. Возвращается хендл для работы с этим индексом. II_CREATETAG(<index handle>,<tag name>,<expr>) -> tag handle создает в индексе новый тег типа возвращаемого <expr> значения. Возвращается хендл для работы с вновь созданным тегом. II_OPEN([<driver>],<name>) -> index handle открывает индекс. II_OPENTAG(<index handle>,<name>) -> tag handle открывает тег. II_CLOSE(<index handle>) -> NIL закрывает индекс. II_CLOSETAG(<tag handle>) -> NIL закрывает тег. II_ADDKEY(<tag handle>,<id>,<key>) -> NIL добавляет ключ в тег. <id> - набор байт (для CDX'а - 4) которые будут храниться в ассоциации с ключом. <id> должен быть типа CHARACTER. Указатель текущего ключа устанавливается на вновь добавленный ключ II_DELKEY(<tag handle>) -> NIL удаляет текущий ключ. II_GOTOP(<tag handle>) -> NIL устанавливает указатель текущего ключа на самый первый ключ (ключ с наименьшим значением). II_GOBOTTOM(<tag handle>) -> NIL устанавливает указатель текущего ключа на самый последний ключ (ключ с наибольшим значением). II_BOF(<tag handle>) -> BOF возвращает признак перемещения за первый ключ. II_EOF(<tag handle>) -> EOF возвращает признак перемещения за последний ключ. II_ID(<tag handle>) -> associated data возвращает строку байт ассоциированных с текущим ключом (для CDX - четыре байта). II_KEY(<tag handle>) -> key value возвращает значение текущего ключа. II_SKIP(<tag handle>,<count>) -> NIL перемещает указатель текущего ключа вперед или назад. SIX В мемо-полях можно хранить данные любого типа, включая массивы и обЪекты. Например: dbcreate("test",{{"mmm","M",10,0}}) use test append blank ? valtype(field->mmm) // "M" field->mmm := 123 ? valtype(field->mmm) // "N" field->mmm := {1,2,3} ? valtype(field->mmm) // "A" m := map() m:fname := "Bob" m:lname := "Smith" field->mmm := m ? valtype(field->mmm) // "O"Для хранения/восстановления обЪектов с методами ОО-модель предоставляет возможность "регенерации" обЪектов. См. описание ОО-модели. VariFields Тип "V" позволяет хранить в таблице строки любой длины. При этом непосредственно в DBF-файле хранятся лишь первые символы строки, а остаток - в FPT-файле. В DBF-файле также хранится 6 байт служебной информации. Например: dbcreate("test",{{"vchar","V",20,0}}) field->vchar := "123456789012345678901234567890" ? field->vchar // 123456789012345678901234567890Если строка умещается в <длина V-поля>-2 символах, то она целиком записывается в DBF. В противном случае в DBF запишется первые <длина V-поля>-6 символов, а остаток в FPT. В V-поле можно хранить даты (V-дата занимает на диске всего 3 байта вместо 8). Для этого нужно указать длину V-поля равную 3. Аналогично, в V-поле с длиной 4 можно хранить целые числа. Например: a := {} aadd(a,{"date","V",3,0}) aadd(a,{"int","V",4,0}) dbcreate("test",a) use test append blank field->date := date() field->int := 1234 ? valtype(field->date),field->date // D 11/05/2001 ? valtype(field->int),field->int // N 1234Триггеры Триггер - это пользовательская функция, которая вызывается Клипом при возникновении следующих ситуаций: EVENT_PREUSE - перед открытием базы данных * EVENT_POSTUSE - после открытия базы данных * EVENT_UPDATE - перед изменением индекса EVENT_APPEND - перед добавлением записи EVENT_DELETE - перед удалением записи EVENT_RECALL - перед восстановлением записи EVENT_PACK - перед PACK EVENT_ZAP - перед ZAP EVENT_PUT - перед записью данных в таблицу * EVENT_GET - после чтения данных из таблицы EVENT_PRECLOSE - перед закрытием базы данных * EVENT_POSTCLOSE - после закрытия базы данных EVENT_PREMEMOPACK - перед MEMOPACK * EVENT_POSTMEMOPACK - после MEMOPACKТриггеру передается 4 параметра: Например, триггер может иметь следующий вид: function MyTrigger(nEvent,nArea,nFieldPos,xTrigVal) do case case nEvent == EVENT_DELETE // Каскадное удаление записей из дочерней таблицы DeleteChilds() case nEvent == EVENT_ZAP if alert("Are you sure?",{"Yes","No"}) != 1 return .f. endif case nEvent == EVENT_PUT if FieldName(nFieldPos) == "AGE" if xTrigVal < 18 .OR. xTrigVal > 100 alert("Bad age!") return .f. endif endif endcase return .t.Триггер устанавливается предложением TRIGGER команды USE или функцией rm_setTrigger(). (Команда USE ... TRIGGER определена в six2clip.ch). Например: USE test TRIGGER "MyTrigger"или USE test rm_setTrigger(EVENT_INSTALL,"MyTrigger")Если вы создатите "триггер по умолчанию" (функцию rm_defTrigger()) он будет использоваться со всеми рабочими областями, на которые триггер не устанавливался. !!! Обратите внимание на то что SIX-овые фичи работают только с DBFCDX |