Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2013.09.22;
Скачать: [xml.tar.bz2];

Вниз

UDF на Delphi, FireBird и NULL   Найти похожие ветки 

 
ПЛОВ ©   (2013-04-11 13:08) [0]

Есть у меня некая UDF для FireBird-а написанная на Delphi. Внутри функция, принимающая числовое значене, причем очень важно знать, null оно, или все таки равно 0. Передвать null в UDF-функции FireBird не умеет.
Чтобы определить эти самые null-ы решил воспользоватся правилом:
X +/- 1 = Y; X +/- null = null, т.е. передаем в функцию число, число + 1 ну и например число - 1, если исходное значение null, то на выходе получаем все нули, если не null - какое-то одно будет точно <> 0.
Это работает, но правильно ли так делать?


 
ВВВ   (2013-04-11 13:20) [1]

У числа типа PInteger?


 
Компромисс1   (2013-04-11 13:20) [2]

Do not call UDF with NULL parameters
Many (most) UDFs implement functions that are write-null-through type, i.e. NULL input parameter will result in NULL output value, (e.g. function that converts string to lowercase should return NULL if input parameter is NULL). If this is the case, it is not necessary to call the function at all, because you know the return value (NULL in this case) in advance.

Example 1
UPDATE tab
  SET product_type = LowerUDF (product_type)
WHERE product_type IS NOT NULL;


http://www.volny.cz/iprenosil/interbase/ip_ib_udf_null.htm

Не подходит?


 
ПЛОВ ©   (2013-04-11 13:31) [3]


> Компромисс1   (11.04.13 13:20) [2]

Увы, не подходит. Входных параметров несколько, т.е. одно число может иметь значение, а другое при этом null


 
ПЛОВ ©   (2013-04-11 13:34) [4]

Имею в виду, параметров, для которых нужно определить null, я написал в начале топика свой пример определения для одного...

Число типа Double. Нужно любое значение (положительное, отрицательное, ноль) отличить от пустоты


 
Компромисс1   (2013-04-11 13:43) [5]

Вроде в последующих версиях FB есть by descriptor и еще null после объявления параметра. Если нельзя провести upgrade СУБД, то похоже придется придумывать свое решение. Можно передавать X и X+1, тогда если X+1 не равно 0, то можно использовать X как обычное число, иначе придется проверить и X.


 
ПЛОВ ©   (2013-04-11 13:50) [6]


> Компромисс1   (11.04.13 13:43) [5]

Спасибо, уже нашел. Сейчас попробую сделать через by descriptor.
Версию юзаю 2.5, там оно уже есть


 
ПЛОВ ©   (2013-04-12 12:38) [7]

Что делать, если внутрь UDF передается непраивльное значение? Все сделал четко по примерам, забил в поле БД некое значение, но вызов get_double_value из модуля ibutil возвращает совершенно другое значение :(
При этом если входное значение напрямую передать на выход с использованием типа PDSC то в гриде оно отбражается правильно.
Из-за чего хоть такое вообще м.б.?


 
Inovet ©   (2013-04-12 13:00) [8]

> [7] ПЛОВ ©   (12.04.13 12:38)

Насколько помню. Там что-то через указатель передаётся, и в базе надо функцию декларировать соответственно.


 
ПЛОВ ©   (2013-04-12 13:16) [9]

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

Из БД значение поля передается как DOUBLE PRECISION BY DESCRIPTOR, т.е. внутри UDF оно должно соотв. Delphi типу Double. Передаю 100.45, после преобразования получаю вот такое: 4,8272411094e-268.
В струтуре дескриптора (TDSC) dsc_length имеет значение 4 (я так понял что это длина данных, передаваемых через указатель??). А Double имеет 8 байт длину...


 
Компромисс1   (2013-04-12 13:33) [10]

ПЛОВ ©

Я никогда не работал, я только google освоил :)
Я думаю, стоит привести тут описание функции, чтобы можно было проанализировать, чем оно от примеров отличается. А иначе "ошибка в 17 строке":)


 
Компромисс1   (2013-04-12 13:37) [11]

Ну и как вызывается функция, конечно


 
ПЛОВ ©   (2013-04-12 13:55) [12]

Описываю подробнее

В таблице БД поле объявлено как DOUBLE PRECISION,

Регистрация функции в БД (это ф-ция просто для тестов, чтобы понять как оно все работает):

DECLARE EXTERNAL FUNCTION UDF_EBF_TEST
   DOUBLE PRECISION BY DESCRIPTOR,
   DOUBLE PRECISION BY DESCRIPTOR
 RETURNS DOUBLE PRECISION BY DESCRIPTOR
 ENTRY_POINT "udf_ebf_Test" MODULE_NAME "EBUdf.dll";


В Делфи:

function udf_ebf_Test(RetVal, Val: PDSC): PDSC; cdecl; export;
var
 FRetVal, FVal: Double;
begin
 FRetVal := get_double_value(RetVal);
 FVal := get_double_value(Val);

 if isNull(RetVal) or isNull(Val) then
   Result := nil
 else
   if not isNull(Val) then
     Result := RetVal;
end;


выделена строка на которой я торможу отладчик, и дальше выполняется эта get_double_value, вот она:

function get_double_value(v:PDSC):double;
var
  valdouble: ISC_double;
  begin
       case v^.dsc_dtype of
       
        dtype_real:    begin
                            valdouble:= v.dsc_address;
                            result:=valdouble^;
                       end;

 dtype_double:  begin
                            valdouble:=v.dsc_address;
                            result:=valdouble^;
                       end;

       end;
  end;


Типы:

ISC_double = ^double;

  TDSC = packed record
          dsc_dtype,
          dsc_scale: byte;
          dsc_length,
          dsc_sub_type,
          dsc_flags: word;
          dsc_address: pointer;
        end;


При вызове get_double_value - v^.dsc_dtype определяет как dtype_double и выполняет блок
 dtype_double:  begin
                            valdouble:=v.dsc_address;
                            result:=valdouble^;
                       end;


и возвращает значение: 4,8272411094e-268. В БД эта запись (одна единственная в таблице) равна 100.45


 
Компромисс1   (2013-04-12 14:24) [13]

Вроде все правильно.

Я нашел вот такую штуку

http://www.delphimaster.net/view/3-1092399856

Есть расхождение в структуре record, но оно несущественно (знаковые типа вместо беззнаковых).
Буду думать...


 
Компромисс1   (2013-04-12 14:28) [14]

Вроде соглашение вызова должно быть cdecl. Хотя странно, как оно может влиять


 
ПЛОВ ©   (2013-04-12 14:32) [15]

Так в моей ф-ции оно такое и есть:
function udf_ebf_Test(RetVal, Val: PDSC): PDSC; cdecl; export;


 
Компромисс1   (2013-04-12 14:38) [16]

Отсюда скачал?  http://www.ibase.ru/download/tbudf.zip

Меня смущает, что double и real одинаково обрабатываются. Если они в FB не различаются, то зачем 2 разные константы в структуре FB???


 
ПЛОВ ©   (2013-04-12 14:40) [17]

Да, качал оттуда. Сейчас пытаюсь выяснить что определяется значением dsc_length.


 
Компромисс1   (2013-04-12 14:45) [18]

В firebird DOUBLE PRECISION
Вещественные данные. Диапазон от -3.40E+308 до 3.40E+308.


в delphi double
Он поддерживает приблизительно 15 цифр точности в диапазоне от 2.23 x 10-308 до 1.79 x 10308

Судя по слегка разным значениям, форматы могут отличаться.
Для проверки советую убедиться, что нет никаких проблем с передачей каких-нибудь integer и string. Тогда точно проблема в формате данных double, а не в чем-либо еще.
И придется поискать, как перевести double значение из firebird в delphi.


 
ПЛОВ ©   (2013-04-12 14:59) [19]

Только что проверил передачу Integer - нет проблем! Все передается правильно.


 
Далла Пиккола   (2013-04-12 15:15) [20]

Может я чего-то не понимаю, но в объявлении

DECLARE EXTERNAL FUNCTION

параметры объявлены как DOUBLE PRECISION,  а функция принимает указатель на структуру.


 
ПЛОВ ©   (2013-04-12 15:19) [21]

Переделал ф-цию в UDF под один double prec. параметр - и ура! он правильно передался. Потом добавил второй - первый читается правильно, второй - то самое -3.блаблаблаЕ308. В запросе в качетве обеих параметров использовалось одно и то же поле. Все параметры после первого не читаются...


 
Компромисс1   (2013-04-12 15:21) [22]

When a double value in Delphi is very very very small it will be 0 in IB even if passing it as Double Precision. I have not confirmed this conclusively so it is an opinion.

http://www.firebirdsql.org/en/writing-udfs-in-delphi-for-interbase-firebird/

Это для by value, правда. Но вроде как разница в формате есть.


 
Компромисс1   (2013-04-12 15:23) [23]

ПЛОВ ©   (12.04.13 15:19) [21]

Очень странно. Похоже на баг


 
Далла Пиккола   (2013-04-12 15:27) [24]

Я правильно понимаю, что функция принимает 2 параметра, каждый из которых - указатель на СТРУКТУРУ

TDSC = packed record
         dsc_dtype,
         dsc_scale: byte;
         dsc_length,
         dsc_sub_type,
         dsc_flags: word;
         dsc_address: pointer;
       end;

А в декларации UDF Вы сообщаете, что это вовсе никакие и не указатели, а тип DOUBLE PRECISION?


 
ПЛОВ ©   (2013-04-12 15:30) [25]


> Далла Пиккола   (12.04.13 15:27) [24]

DOUBLE PRECISION BY DESCRIPTOR - это означает что в UDF передается указатель на структуру.


 
Далла Пиккола   (2013-04-12 15:47) [26]

Понял.
Но вот я посмотрел в руководство.

Там

DECLARE EXTERNAL FUNCTION
name [datatype;| CSTRING (int) | DESCRIPTOR [, datatype| CSTRING (int) ...]DESCRIPTOR]
RETURNS {datatype[BY VALUE] | CSTRING (int) | PARAMETER n}
[FREE_IT] ENTRY_POINT "entryname" MODULE_NAME "modulename";

Меня смущает то, что если входные параметры могут передаваться с типом DESCRIPTOR, в возвращаемом значении я такого синтаксиса вообще не вижу. И мне непонятно, почему перед DESCRIPTOR Вы указываете DOUBLE PRECISION ?

Мне кажется проблема где-то в синтаксисе объявления, а не в самой dll. Там все, похоже. правильно.


 
Далла Пиккола   (2013-04-12 15:52) [27]

Извините, что задаю глупые вопросы. Но иногда, отвечая на глупые вопросы, можно найти, в чем проблема.


 
ПЛОВ ©   (2013-04-12 15:55) [28]

name [datatype;| CSTRING (int) | DESCRIPTOR [, datatype| CSTRING (int) ...]DESCRIPTOR]
Тип все таки нужен.


 
Далла Пиккола   (2013-04-12 15:56) [29]

То есть либо datatype, либо DESCRIPTOR. Слово BY там вообще не упоминается. Может у меня версия руководства не та?
Как у Вас описаны требования к объявлению UDF?


 
Далла Пиккола   (2013-04-12 15:57) [30]

| означает ЛИБО в этом описании синтаксиса. [ означает МОЖЕТ


 
Далла Пиккола   (2013-04-12 15:58) [31]

Может случиться, что парсер не видит ошибку. И глотает такое определение, понимая его не как DESCRIPTOR, а как DOUBLE PRECISION.
Попробуйте оставить только DESCRIPTOR  в объявлении


 
ПЛОВ ©   (2013-04-12 16:01) [32]

http://www.firebirdsql.org/refdocs/langrefupd15-declareext.html
вот тут есть

DECLARE EXTERNAL FUNCTION localname
  [<type_decl> [, <type_decl> ...]]
  RETURNS {<return_type_decl> | PARAMETER 1-based_pos} [FREE_IT]
  ENTRY_POINT "function_name" MODULE_NAME "library_name"

<type_decl>         ::=  sqltype [BY DESCRIPTOR] | CSTRING(length)
<return_type_decl>  ::=  sqltype [BY {DESCRIPTOR|VALUE}] | CSTRING(length)


 
Далла Пиккола   (2013-04-12 16:02) [33]

Или же здесь смысл именно в том, чтобы передать в качестве параметра DOBLE PRECISION, но в составе структуры, чтобы он там сам как-нибудь засунул это внутрь структуры? Кстати, а как Вы потом это все запускаете?
Как выглядит сам SQL-запрос?


 
Далла Пиккола   (2013-04-12 16:03) [34]

Да, вижу. У меня, видно, старое описание.


 
Далла Пиккола   (2013-04-12 16:06) [35]

С резервированием памяти под структуру и очисткой (FREE_IT) все верно? Там нет ошибки?


 
ПЛОВ ©   (2013-04-12 16:06) [36]

Смысл в том что нужно узнать имеет ли поле значение (и какое) или оно равно NULL. Отличить NULL от 0 внутри UDF можно только передав указатель на структуру.
Запрос выглядит стандартно:
select функция(поле1, поле2) from таблица


 
ПЛОВ ©   (2013-04-12 16:08) [37]


> С резервированием памяти под структуру и очисткой (FREE_IT)
> все верно? Там нет ошибки?


я не указывал необходимость очистки


 
Далла Пиккола   (2013-04-12 16:08) [38]

Я бы сначала попробовал заполнить структуру искусственно (внутри dll), пока проигнорировав входные параметры вообще. Убедился бы, что механизм "возврата значения" работает как надо. Потом бы разбирался с тем, как я их туда передаю.


 
Далла Пиккола   (2013-04-12 16:10) [39]

Да, я уже понял, что Вы хотите сделать и как этого добиться. Предлагаю сначала попробовать вернуть значение, создав его внутри dll искусственно. Чтобы уменьшить число мест, где могут быть грабли.


 
Компромисс1   (2013-04-12 16:11) [40]

ПЛОВ

В firebird есть какие-то "драйвера", которые можно обновить? Может, этот баг пофиксили уже?



Страницы: 1 2 вся ветка

Форум: "Прочее";
Текущий архив: 2013.09.22;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.55 MB
Время: 0.003 c
15-1366110720
Demo
2013-04-16 15:12
2013.09.22
События в FoxPro MSDOS


15-1365770544
O'ShinW
2013-04-12 16:42
2013.09.22
Oracle. Почему так заводится юзер?


15-1365397776
jimm_kerry
2013-04-08 09:09
2013.09.22
свой канал для кабельного ТВ гостиницы


15-1363286070
Bitt
2013-03-14 22:34
2013.09.22
TImage рисование точки


2-1357650272
Pcrepair
2013-01-08 17:04
2013.09.22
Как правильно из потока менять Переменные в другом потоке?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский