Запутался в принудительном согласовании типов при вызове функций (стековые кадры)
Рассмотрим следующий пример:
create or replace function f (n integer) return integer as
begin
return n;
end;
/
begin
dbms_output.put_line (f (3.8));
end;
/
3.8
В этом, кажется, нет смысла. Очевидно, что PL/SQL интерпретатор игнорирует спецификацию INTEGER, как при входе в функцию, так и при возврате из нее.
Является ли это просто ошибкой? Или это сознательный дизайнерский выбор разработчиков языка?
Вот что меня смутило, сравните со следующим примером:
declare
x integer := 3.8;
begin
dbms_output.put_line (x);
end;
/
4
В последнем примере спецификация типов данных соблюдена. PL/SQL не выдает ошибку и выполняет неявное приведение типов, что нарушает тип данных, объявленный для x - переменная хранит целое число 4, а не 3.8.
Как PL/SQL выполняет вызов функции в первом примере? Насколько я понимаю, всякий раз, когда компилятор или интерпретатор находит вызов функции, он создает стековый кадр, с переменными для аргументов и для возвращаемого значения. Разве эти переменные при создании стекового кадра не должны быть такого же типа данных, как указано в объявлении функции? Если в стековом кадре есть поле типа данных INTEGER для аргумента 3.8, то почему оно не преобразуется в 4 еще до того, как будет передано в функцию? И то же самое для возвращаемого значения, если функция возвращает 3.8, но в месте вызова ожидается целое число, и поэтому соответствующая переменная в стековом кадре должна быть INTEGER. Как она может принять возвращаемое значение 3.8?
И, что самое странное, почему такое поведение отличается от поведения, когда задействованы явно объявленные переменные, как во втором примере?
Свободный перевод вопроса Confused about type coercion in function calls (stack frames) от участника @mathguy
Ответы (1 шт):
Это дизайнерское решение разработчиков языка.
В главе 9.7.1.1 Formal Parameters of Constrained Subtypes документации это поведение подробно описано.
Во-первых, INTEGER в SQL это синоним для NUMBER(38). Аналогично в PL/SQL, это подтип определённый в пакете STANDARD:
SUBTYPE INTEGER IS NUMBER(38,0);
Тип данных NUMBER(38,0) имеет ограничение (constrained datatype). Цитата:
If the data type of a formal parameter is a constrained subtype, then:
[...]
If the subtype has a numeric base type, then the actual parameter inherits the range of the subtype, but not the precision or scale.Note: In a function, the clause RETURN datatype declares a hidden formal parameter and the statement RETURN value specifies the corresponding actual parameter. Therefore, if datatype is a constrained data type, then the preceding rules apply to value.
Исходя и изложеного в цитате выше, ограничение на 38 знаков не будет унаследовано актуальным параметром, то же действует для возвращаемого типа данных.
То есть, следующие определения функции равнозначны, именно по этой причине они не могут быть перегружены:
eclare
function f (n integer) return integer as
begin
return n;
end;
function f (n number) return number as
begin
return n;
end;
begin
dbms_output.put_line (f (3.8));
end;
/
Error report -
ORA-06550: line 11, column 27:
PLS-00307: too many declarations of 'F' match this call
При присвоении переменной x с типом данных NUMBER(38) числового литерала 3.8, в соответствии с правилами присвоения, значение округляется до кол-ва знаков после запятой, и 4 это ожидаемое поведение.