FirebirdSQL / firebird

Firebird server, client and tools
https://www.firebirdsql.org/
1.24k stars 212 forks source link

GBAK can write uninitialized data into RDB$RETURN_ARGUMENT and RDB$ARGUMENT_POSITION fields #7869

Closed dmitry-lipetsk closed 8 months ago

dmitry-lipetsk commented 10 months ago

GBAK does not initialize RDB$RETURN_ARGUMENT, RDB$ARGUMENT_POSITION fields and can write unexpected values into restored database when backup does not have data for ones.

See these two functions:

  1. get_function https://github.com/FirebirdSQL/firebird/blob/cf98c6e97f6fa25eabe81b23b1a7ac6900e51be5/src/burp/restore.epp#L4947

  2. get_function_arg https://github.com/FirebirdSQL/firebird/blob/cf98c6e97f6fa25eabe81b23b1a7ac6900e51be5/src/burp/restore.epp#L5333

I offer to add the initialization of these fields with NULL value.

aafemt commented 10 months ago

Backup file without these values is malformed. An error must be raised on attempt to restore such backup. To accomplish that the fields must be declared with NOT NULL.

dmitry-lipetsk commented 10 months ago

Without initialization to NULL in GBAK, this "NOT NULL" field will be assigned with NOT NULL garbage.

Am I right?

pavel-zotov commented 8 months ago

Maybe i did not understand properly: what RDB$ table must be checked ?

Initial DDL:

set list on;
set bail on;
shell del g:\temp\tmp4test.fdb 2>nul;
create database 'localhost:g:\temp\tmp4test.fdb' user sysdba password 'masterkey';

set term ^;
create or alter function fn_test(a_x int, a_y timestamp, a_z boolean) returns varchar(50) character set utf8 as
begin
    return '';
end
^
set term ;^
commit;

select f.rdb$return_argument from rdb$functions f where f.rdb$function_name = upper('fn_test');

select g.rdb$argument_name, g.rdb$argument_position from rdb$function_arguments g where g.rdb$function_name = upper('fn_test');

2) do backup and restore: gbak -b localhost:g:\temp\tmp4test.fdb stdout | gbak -rep stdin localhost:g:\temp\tmp4test.tmp.fdb

3) check what we have in restored DB:

set list on;
select f.rdb$return_argument from rdb$functions f where f.rdb$function_name = upper('fn_test');

select g.rdb$argument_name, g.rdb$argument_position from rdb$function_arguments g where g.rdb$function_name = upper('fn_test');

Result for both init and check scripts:

RDB$RETURN_ARGUMENT             0

RDB$ARGUMENT_NAME               <null>
RDB$ARGUMENT_POSITION           0

RDB$ARGUMENT_NAME               A_X                                                                                                                                                                                                                                                         
RDB$ARGUMENT_POSITION           1

RDB$ARGUMENT_NAME               A_Y                                                                                                                                                                                                                                                         
RDB$ARGUMENT_POSITION           2

RDB$ARGUMENT_NAME               A_Z                                                                                                                                                                                                                                                         
RDB$ARGUMENT_POSITION           3

(i.e. no differences)

PS. WI-T6.0.0.230 Firebird 6.0 Initial

dmitry-lipetsk commented 8 months ago

Maybe i did not understand properly: what RDB$ table must be checked ?

Your test looks correctly.

One moment - I think, select statements must have "rdb$package_name is null" condition in WHERE sections.

Or select a value of this field.

Just to suppress a paranoia :)

pavel-zotov commented 8 months ago

select statements must have "rdb$package_name is null" condition in WHERE sections. Or select a value of this field.

I still no see any problem. Please consider results from attached .zip gh-7869-test.zip

Difference file ( gh-7869-diff.log ) shows that gbak does NOT write nulls in some places (in contrary to initial .sql). Rather, these fields are filled with either zeroes or empty strings during restore:

Comparing files gh-7869-init.log and GH-7869-CHK.LOG
***** gh-7869-init.log
  212:  RDB$FUNCTION_NAME               PG_FUNC                                                                                        
  215:  RDB$FUNCTION_TYPE               <null>
  216:  RDB$QUERY_NAME                  <null>
  217:  RDB$DESCRIPTION                 <null>
***** GH-7869-CHK.LOG
  212:  RDB$FUNCTION_NAME               PG_FUNC                                                                                        
  215:  RDB$FUNCTION_TYPE               0
  216:  RDB$QUERY_NAME                                                                                                                 
  219:  RDB$DESCRIPTION                 <null>
*****
. . .

***** gh-7869-init.log
 1091:  RDB$ARGUMENT_POSITION           1
 1092:  RDB$MECHANISM                   <null>
 1093:  RDB$FIELD_TYPE                  <null>
 1094:  RDB$FIELD_SCALE                 <null>
 1095:  RDB$FIELD_LENGTH                <null>
 1096:  RDB$FIELD_SUB_TYPE              <null>
 1097:  RDB$CHARACTER_SET_ID            <null>
***** GH-7869-CHK.LOG
 1095:  RDB$ARGUMENT_POSITION           1
 1096:  RDB$MECHANISM                   0
 1097:  RDB$FIELD_TYPE                  0
 1098:  RDB$FIELD_SCALE                 0
 1099:  RDB$FIELD_LENGTH                0
 1100:  RDB$FIELD_SUB_TYPE              0
 1101:  RDB$CHARACTER_SET_ID            <null>

PS. Checked also on 6.0.0.135, build date: 21-nov-2023.

Logs in attachment: gh-7869-init.log -- log after we create packaged and standalone functions and query rdb$ tables; gh-7869-chk.log -- log after we do backup and restore of that DB and again query rdb$ tables. gh-7869-diff.log -- diff

gh-7869-diff