Skip to content

Commit bcf1e7a

Browse files
committed
Feature #8974 - Declared Local Temporary Tables in Packages
1 parent b137a21 commit bcf1e7a

39 files changed

+1383
-410
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# Declared Local Temporary Tables in Packages (FB 6.0)
2+
3+
Firebird 6.0 supports declaring local temporary tables in SQL packages.
4+
5+
These tables are declared in package metadata. Internally, they are stored as package-owned persistent temporary table
6+
metadata, identified through `RDB$PACKAGE_NAME`. Their data remains temporary: transaction-local for
7+
`ON COMMIT DELETE ROWS` and connection-local for `ON COMMIT PRESERVE ROWS`.
8+
9+
## Syntax
10+
11+
Declared local temporary tables can be used in package declarations.
12+
13+
```sql
14+
CREATE PACKAGE <package_name>
15+
AS
16+
BEGIN
17+
[{<package_item> ;}...]
18+
END
19+
20+
<package_item> ::=
21+
<declared_local_temporary_table> |
22+
<package_procedure_declaration> |
23+
<package_function_declaration>
24+
25+
<declared_local_temporary_table> ::=
26+
DECLARE LOCAL TEMPORARY TABLE <table_name>
27+
(
28+
<column_definition> [, ...]
29+
)
30+
[ON COMMIT {DELETE | PRESERVE} ROWS]
31+
[{[UNIQUE] [ASC | DESC] INDEX <index_name> (<column_name>)}...]
32+
```
33+
34+
They can also appear in the declaration section of `CREATE PACKAGE BODY`.
35+
36+
```sql
37+
CREATE PACKAGE BODY <package_name>
38+
AS
39+
BEGIN
40+
[{<package_item> ;}...]
41+
END
42+
43+
<package_body_item> ::=
44+
<declared_local_temporary_table> |
45+
<package_procedure_definition> |
46+
<package_function_definition>
47+
```
48+
49+
## Semantics
50+
51+
- `ON COMMIT DELETE ROWS` (default): rows are transaction-local and cleared at transaction end.
52+
- `ON COMMIT PRESERVE ROWS`: rows are connection-local and preserved across transactions in the same attachment.
53+
54+
The table definition is part of package metadata and is persistent like other package members; table data remains
55+
temporary.
56+
57+
## Visibility and Name Resolution
58+
59+
Visibility depends on where the table is declared:
60+
61+
- Tables declared in `CREATE PACKAGE` (header) are public package members.
62+
- Tables declared in `CREATE PACKAGE BODY` are private to that package body.
63+
- Unqualified references to a matching declared table name inside package routines resolve to the package table.
64+
65+
External SQL access rules:
66+
67+
- Header tables can be accessed externally as `[{schema_name}.package_name.table_name` (for example, `pkg.t_pub`).
68+
- Body tables cannot be accessed externally and are only valid inside routines of the same package body.
69+
70+
Index DDL rules:
71+
72+
- Packaged declared local temporary table indexes must be declared inline in `DECLARE LOCAL TEMPORARY TABLE`.
73+
- Standalone index DDL commands are not allowed for packaged tables:
74+
`CREATE INDEX`, `ALTER INDEX`, `DROP INDEX`, `SET STATISTICS INDEX`.
75+
76+
## Name Isolation
77+
78+
Declared local temporary table names are isolated by package context.
79+
80+
- Different packages may declare tables with the same name.
81+
- A package table name may also match a regular table name in the same schema.
82+
83+
Resolution inside package routines prefers the package-local declaration.
84+
85+
## Dependencies and DDL lifecycle
86+
87+
Declared local temporary tables participate in package dependency handling.
88+
89+
- Package routines depending on declared table columns are tracked as package dependencies.
90+
- `DROP PACKAGE` removes package body/header dependencies and package members (routines and declared tables) in package
91+
scope.
92+
- `ALTER PACKAGE` and `CREATE OR ALTER PACKAGE` recreate packaged declared local temporary tables that belong to the
93+
package header.
94+
- `RECREATE PACKAGE BODY` and `CREATE OR ALTER PACKAGE BODY` recreate packaged declared local temporary tables that
95+
belong to the package body.
96+
97+
## System metadata changes
98+
99+
Packaged declared local temporary tables add package ownership and visibility information to system metadata. Tools
100+
that inspect metadata should use these columns when present.
101+
102+
| Table | Column | Meaning |
103+
|----------------------|--------------------|-----------------------------------------------------------------|
104+
| `RDB$RELATIONS` | `RDB$PACKAGE_NAME` | Owning package of the declared temporary table |
105+
| `RDB$RELATIONS` | `RDB$PRIVATE_FLAG` | `PUBLIC` (`0`) for header tables, `PRIVATE` (`1`) for body tables |
106+
| `RDB$RELATION_FIELDS`| `RDB$PACKAGE_NAME` | Owning package of the declared temporary table columns |
107+
| `RDB$INDICES` | `RDB$PACKAGE_NAME` | Owning package of inline indexes declared for packaged tables |
108+
| `RDB$INDEX_SEGMENTS` | `RDB$PACKAGE_NAME` | Owning package of the packaged table index segments |
109+
| `MON$TABLE_STATS` | `MON$PACKAGE_NAME` | Owning package reported in runtime table statistics |
110+
111+
## Example
112+
113+
```sql
114+
set term !;
115+
116+
recreate package pkg
117+
as
118+
begin
119+
declare local temporary table t_pub(
120+
id integer
121+
) on commit preserve rows
122+
index idx_t_pub_id (id);
123+
124+
procedure p1(n integer);
125+
procedure p2 returns (n integer);
126+
end!
127+
128+
create package body pkg
129+
as
130+
begin
131+
declare local temporary table t_priv(
132+
id integer
133+
) on commit preserve rows
134+
unique index uq_t_priv_id (id);
135+
136+
procedure p1(n integer)
137+
as
138+
begin
139+
insert into t_pub(id) values (:n);
140+
insert into t_priv(id) values (:n);
141+
end
142+
143+
procedure p2 returns (n integer)
144+
as
145+
begin
146+
for select id from t_pub into :n do
147+
suspend;
148+
end
149+
end!
150+
151+
set term ;!
152+
153+
-- use package routines
154+
execute procedure pkg.p1(10);
155+
select * from pkg.p2;
156+
157+
-- header-declared table: allowed
158+
select * from pkg.t_pub;
159+
160+
-- body-declared table: not allowed
161+
-- select * from pkg.t_priv;
162+
163+
-- not allowed for packaged tables:
164+
-- create index idx_cmd on pkg.t_pub(id);
165+
-- alter index idx_t_pub_id active;
166+
-- drop index idx_t_pub_id;
167+
```
168+
169+
## Notes
170+
171+
- This feature is distinct from SQL-created local temporary tables (`CREATE LOCAL TEMPORARY TABLE ...`), which are
172+
attachment-private DDL objects.
173+
- Packaged declared temporary tables are not attachment-private created LTTs. They use persistent temporary-table
174+
metadata associated with the package through `RDB$PACKAGE_NAME`.
175+
- Declared package temporary tables are package DDL items and follow package compilation, visibility, and dependency
176+
rules.

src/auth/SecurityDatabase/LegacyServer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ const UCHAR PWD_REQUEST[] =
6666
blr_rse, 1,
6767
blr_relation3,
6868
14, 'P', 'L', 'G', '$', 'L', 'E', 'G', 'A', 'C', 'Y', '_', 'S', 'E', 'C', // PLG_LEGACY_SEC_SCHEMA
69+
0,
6970
9, 'P', 'L', 'G', '$', 'U', 'S', 'E', 'R', 'S',
7071
0,
7172
0,

src/burp/backup.epp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,9 @@ int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name)
424424
if (relation->rel_name.schema.hasData())
425425
put_text(att_relation_schema_name, relation->rel_name.schema);
426426

427+
if (relation->rel_name.package.hasData())
428+
put_text(att_relation_package_name, relation->rel_name.package);
429+
427430
put_text(att_relation_name, relation->rel_name.object);
428431

429432
put(tdgbl, att_end);
@@ -673,6 +676,7 @@ burp_fld* get_fields( burp_rel* relation)
673676
X.RDB$FIELD_SOURCE_SCHEMA_NAME EQUIV Y.RDB$SCHEMA_NAME AND
674677
X.RDB$FIELD_SOURCE = Y.RDB$FIELD_NAME AND
675678
X.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND
679+
X.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND
676680
X.RDB$RELATION_NAME EQ relation->rel_name.object.c_str()
677681
{
678682
field = (burp_fld*) BURP_alloc_zero(sizeof(burp_fld));
@@ -1659,6 +1663,7 @@ void put_index( burp_rel* relation)
16591663
FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle1)
16601664
X IN RDB$INDICES WITH
16611665
X.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND
1666+
X.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND
16621667
X.RDB$RELATION_NAME EQ relation->rel_name.object.c_str() AND
16631668
X.RDB$INDEX_NAME NOT STARTING TEMP_DEPEND
16641669
{
@@ -1668,8 +1673,10 @@ void put_index( burp_rel* relation)
16681673
RFR IN RDB$RELATION_FIELDS WITH
16691674
I_S.RDB$FIELD_NAME = RFR.RDB$FIELD_NAME AND
16701675
I_S.RDB$SCHEMA_NAME EQUIV RFR.RDB$SCHEMA_NAME AND
1676+
I_S.RDB$PACKAGE_NAME EQUIV RFR.RDB$PACKAGE_NAME AND
16711677
I_S.RDB$INDEX_NAME = X.RDB$INDEX_NAME AND
16721678
RFR.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND
1679+
RFR.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND
16731680
RFR.RDB$RELATION_NAME = relation->rel_name.object.c_str()
16741681
{
16751682
count++;
@@ -1687,7 +1694,8 @@ void put_index( burp_rel* relation)
16871694

16881695
put(tdgbl, rec_index);
16891696
PUT_TEXT(att_index_name, X.RDB$INDEX_NAME);
1690-
BURP_verbose(151, QualifiedMetaString(X.RDB$INDEX_NAME, relation->rel_name.schema).toQuotedString());
1697+
BURP_verbose(151, QualifiedMetaString(
1698+
X.RDB$INDEX_NAME, relation->rel_name.schema, relation->rel_name.package).toQuotedString());
16911699
// msg 151 writing index %s
16921700

16931701
put_int32 (att_segment_count, X.RDB$SEGMENT_COUNT);
@@ -1697,6 +1705,7 @@ void put_index( burp_rel* relation)
16971705
FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle5)
16981706
Y IN RDB$INDEX_SEGMENTS WITH
16991707
Y.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND
1708+
Y.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND
17001709
Y.RDB$INDEX_NAME EQ X.RDB$INDEX_NAME
17011710
SORTED BY Y.RDB$FIELD_POSITION
17021711
{
@@ -4049,6 +4058,7 @@ void write_rel_constraints()
40494058
X IN RDB$RELATION_CONSTRAINTS CROSS
40504059
REL IN RDB$RELATIONS
40514060
WITH REL.RDB$SCHEMA_NAME EQUIV X.RDB$SCHEMA_NAME AND
4061+
REL.RDB$PACKAGE_NAME MISSING AND
40524062
REL.RDB$RELATION_NAME EQ X.RDB$RELATION_NAME AND
40534063
(REL.RDB$SYSTEM_FLAG MISSING OR REL.RDB$SYSTEM_FLAG NE 1)
40544064
{
@@ -4124,6 +4134,15 @@ void write_relations()
41244134
name.schema = X.RDB$SCHEMA_NAME;
41254135
}
41264136

4137+
if (!X.RDB$PACKAGE_NAME.NULL)
4138+
{
4139+
PUT_TEXT(att_relation_package_name, X.RDB$PACKAGE_NAME);
4140+
name.package = X.RDB$PACKAGE_NAME;
4141+
}
4142+
4143+
if (!X.RDB$PRIVATE_FLAG.NULL)
4144+
put_int32(att_relation_private_flag, X.RDB$PRIVATE_FLAG);
4145+
41274146
PUT_TEXT(att_relation_name, X.RDB$RELATION_NAME);
41284147
name.object = X.RDB$RELATION_NAME;
41294148

src/burp/burp.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,13 @@ Version 11: FB4.0.
212212
213213
Version 12: FB6.0.
214214
Schemas.
215+
216+
Packaged tables:
217+
RDB$RELATIONS.RDB$PACKAGE_NAME,
218+
RDB$RELATIONS.RDB$PRIVATE_FLAG,
219+
RDB$RELATION_FIELDSS.RDB$PACKAGE_NAME,
220+
RDB$INDICES.RDB$PACKAGE_NAME and
221+
RDB$INDEX_SEGMENTS.RDB$PACKAGE_NAME.
215222
*/
216223

217224
inline constexpr int ATT_BACKUP_FORMAT = 12;
@@ -291,6 +298,8 @@ enum att_type {
291298
att_relation_sql_security_deprecated, // can be removed later
292299
att_relation_sql_security,
293300
att_relation_schema_name,
301+
att_relation_package_name,
302+
att_relation_private_flag,
294303

295304
// Field attributes (used for both global and local fields)
296305

0 commit comments

Comments
 (0)