#### Новшества в управлении данными

CLIP поддерживает хранение в MEMO-полях переменных любого типа, включая объекты, массивы.

     field->memo_field := {"A",1.00,date(),.t.,tbrowseNew()}
     ? field->memofield

Параллельно с функциями 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 параметра:

    	<nEvent>    - номер типа события (константы EVENT_* определены в six2clip.ch)
    	<nArea>     - номер рабочей области
    	<nFieldPos> - номер поля (только для событий EVENT_GET, EVENT_PUT)
    	<xTrigVal>  - значение поля (только для событий EVENT_GET, EVENT_PUT)

Триггер должен возвращать логическое значение. Если триггер возвращает .T. операция выполняется, в противном случае - нет. (Операции соответствующие событиям, помеченным (*), выполняются в любом случае).
Например, триггер может иметь следующий вид:

    	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

© Ю.Хныкин, uri@itk.ru, 2000