Google

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

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