Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions doc/sql.extensions/README.declared_local_temporary_tables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Declared Local Temporary Tables in Packages (FB 6.0)

Firebird 6.0 supports declaring local temporary tables in SQL packages.

These tables are declared in package metadata. Internally, they are stored as package-owned persistent temporary table
metadata, identified through `RDB$PACKAGE_NAME`. Their data remains temporary: transaction-local for
`ON COMMIT DELETE ROWS` and connection-local for `ON COMMIT PRESERVE ROWS`.

## Syntax

Declared local temporary tables can be used in package declarations.

```sql
CREATE PACKAGE <package_name>
AS
BEGIN
[{<package_item> ;}...]
END

<package_item> ::=
<declared_local_temporary_table> |
<package_procedure_declaration> |
<package_function_declaration>

<declared_local_temporary_table> ::=
DECLARE LOCAL TEMPORARY TABLE <table_name>
(
<column_definition> [, ...]
)
[ON COMMIT {DELETE | PRESERVE} ROWS]
[{[UNIQUE] [ASC | DESC] INDEX <index_name> (<column_name>)}...]
```

They can also appear in the declaration section of `CREATE PACKAGE BODY`.

```sql
CREATE PACKAGE BODY <package_name>
AS
BEGIN
[{<package_item> ;}...]
END

<package_body_item> ::=
<declared_local_temporary_table> |
<package_procedure_definition> |
<package_function_definition>
```

## Semantics

- `ON COMMIT DELETE ROWS` (default): rows are transaction-local and cleared at transaction end.
- `ON COMMIT PRESERVE ROWS`: rows are connection-local and preserved across transactions in the same attachment.

The table definition is part of package metadata and is persistent like other package members; table data remains
temporary.

## Visibility and Name Resolution

Visibility depends on where the table is declared:

- Tables declared in `CREATE PACKAGE` (header) are public package members.
- Tables declared in `CREATE PACKAGE BODY` are private to that package body.
- Unqualified references to a matching declared table name inside package routines resolve to the package table.

External SQL access rules:

- Header tables can be accessed externally as `[{schema_name}.package_name.table_name` (for example, `pkg.t_pub`).
- Body tables cannot be accessed externally and are only valid inside routines of the same package body.

Index DDL rules:

- Packaged declared local temporary table indexes must be declared inline in `DECLARE LOCAL TEMPORARY TABLE`.
- Standalone index DDL commands are not allowed for packaged tables:
`CREATE INDEX`, `ALTER INDEX`, `DROP INDEX`, `SET STATISTICS INDEX`.

## Name Isolation

Declared local temporary table names are isolated by package context.

- Different packages may declare tables with the same name.
- A package table name may also match a regular table name in the same schema.

Resolution inside package routines prefers the package-local declaration.

## Dependencies and DDL lifecycle

Declared local temporary tables participate in package dependency handling.

- Package routines depending on declared table columns are tracked as package dependencies.
- `DROP PACKAGE` removes package body/header dependencies and package members (routines and declared tables) in package
scope.
- `ALTER PACKAGE` and `CREATE OR ALTER PACKAGE` recreate packaged declared local temporary tables that belong to the
package header.
- `RECREATE PACKAGE BODY` and `CREATE OR ALTER PACKAGE BODY` recreate packaged declared local temporary tables that
belong to the package body.

## System metadata changes

Packaged declared local temporary tables add package ownership and visibility information to system metadata. Tools
that inspect metadata should use these columns when present.

| Table | Column | Meaning |
|----------------------|--------------------|-----------------------------------------------------------------|
| `RDB$RELATIONS` | `RDB$PACKAGE_NAME` | Owning package of the declared temporary table |
| `RDB$RELATIONS` | `RDB$PRIVATE_FLAG` | `PUBLIC` (`0`) for header tables, `PRIVATE` (`1`) for body tables |
| `RDB$RELATION_FIELDS`| `RDB$PACKAGE_NAME` | Owning package of the declared temporary table columns |
| `RDB$INDICES` | `RDB$PACKAGE_NAME` | Owning package of inline indexes declared for packaged tables |
| `RDB$INDEX_SEGMENTS` | `RDB$PACKAGE_NAME` | Owning package of the packaged table index segments |
| `MON$TABLE_STATS` | `MON$PACKAGE_NAME` | Owning package reported in runtime table statistics |

## Example

```sql
set term !;

recreate package pkg
as
begin
declare local temporary table t_pub(
id integer
) on commit preserve rows
index idx_t_pub_id (id);

procedure p1(n integer);
procedure p2 returns (n integer);
end!

create package body pkg
as
begin
declare local temporary table t_priv(
id integer
) on commit preserve rows
unique index uq_t_priv_id (id);

procedure p1(n integer)
as
begin
insert into t_pub(id) values (:n);
insert into t_priv(id) values (:n);
end

procedure p2 returns (n integer)
as
begin
for select id from t_pub into :n do
suspend;
end
end!

set term ;!

-- use package routines
execute procedure pkg.p1(10);
select * from pkg.p2;

-- header-declared table: allowed
select * from pkg.t_pub;

-- body-declared table: not allowed
-- select * from pkg.t_priv;

-- not allowed for packaged tables:
-- create index idx_cmd on pkg.t_pub(id);
-- alter index idx_t_pub_id active;
-- drop index idx_t_pub_id;
```

## Notes

- This feature is distinct from SQL-created local temporary tables (`CREATE LOCAL TEMPORARY TABLE ...`), which are
attachment-private DDL objects.
- Packaged declared temporary tables are not attachment-private created LTTs. They use persistent temporary-table
metadata associated with the package through `RDB$PACKAGE_NAME`.
- Declared package temporary tables are package DDL items and follow package compilation, visibility, and dependency
rules.
1 change: 1 addition & 0 deletions src/auth/SecurityDatabase/LegacyServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const UCHAR PWD_REQUEST[] =
blr_rse, 1,
blr_relation3,
14, 'P', 'L', 'G', '$', 'L', 'E', 'G', 'A', 'C', 'Y', '_', 'S', 'E', 'C', // PLG_LEGACY_SEC_SCHEMA
0,
9, 'P', 'L', 'G', '$', 'U', 'S', 'E', 'R', 'S',
0,
0,
Expand Down
21 changes: 20 additions & 1 deletion src/burp/backup.epp
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,9 @@ int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name)
if (relation->rel_name.schema.hasData())
put_text(att_relation_schema_name, relation->rel_name.schema);

if (relation->rel_name.package.hasData())
put_text(att_relation_package_name, relation->rel_name.package);

put_text(att_relation_name, relation->rel_name.object);

put(tdgbl, att_end);
Expand Down Expand Up @@ -673,6 +676,7 @@ burp_fld* get_fields( burp_rel* relation)
X.RDB$FIELD_SOURCE_SCHEMA_NAME EQUIV Y.RDB$SCHEMA_NAME AND
X.RDB$FIELD_SOURCE = Y.RDB$FIELD_NAME AND
X.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND
X.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND
X.RDB$RELATION_NAME EQ relation->rel_name.object.c_str()
{
field = (burp_fld*) BURP_alloc_zero(sizeof(burp_fld));
Expand Down Expand Up @@ -1659,6 +1663,7 @@ void put_index( burp_rel* relation)
FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle1)
X IN RDB$INDICES WITH
X.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND
X.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND
X.RDB$RELATION_NAME EQ relation->rel_name.object.c_str() AND
X.RDB$INDEX_NAME NOT STARTING TEMP_DEPEND
{
Expand All @@ -1668,8 +1673,10 @@ void put_index( burp_rel* relation)
RFR IN RDB$RELATION_FIELDS WITH
I_S.RDB$FIELD_NAME = RFR.RDB$FIELD_NAME AND
I_S.RDB$SCHEMA_NAME EQUIV RFR.RDB$SCHEMA_NAME AND
I_S.RDB$PACKAGE_NAME EQUIV RFR.RDB$PACKAGE_NAME AND
I_S.RDB$INDEX_NAME = X.RDB$INDEX_NAME AND
RFR.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND
RFR.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND
RFR.RDB$RELATION_NAME = relation->rel_name.object.c_str()
{
count++;
Expand All @@ -1687,7 +1694,8 @@ void put_index( burp_rel* relation)

put(tdgbl, rec_index);
PUT_TEXT(att_index_name, X.RDB$INDEX_NAME);
BURP_verbose(151, QualifiedMetaString(X.RDB$INDEX_NAME, relation->rel_name.schema).toQuotedString());
BURP_verbose(151, QualifiedMetaString(
X.RDB$INDEX_NAME, relation->rel_name.schema, relation->rel_name.package).toQuotedString());
// msg 151 writing index %s

put_int32 (att_segment_count, X.RDB$SEGMENT_COUNT);
Expand All @@ -1697,6 +1705,7 @@ void put_index( burp_rel* relation)
FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle5)
Y IN RDB$INDEX_SEGMENTS WITH
Y.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND
Y.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND
Y.RDB$INDEX_NAME EQ X.RDB$INDEX_NAME
SORTED BY Y.RDB$FIELD_POSITION
{
Expand Down Expand Up @@ -4049,6 +4058,7 @@ void write_rel_constraints()
X IN RDB$RELATION_CONSTRAINTS CROSS
REL IN RDB$RELATIONS
WITH REL.RDB$SCHEMA_NAME EQUIV X.RDB$SCHEMA_NAME AND
REL.RDB$PACKAGE_NAME MISSING AND
REL.RDB$RELATION_NAME EQ X.RDB$RELATION_NAME AND
(REL.RDB$SYSTEM_FLAG MISSING OR REL.RDB$SYSTEM_FLAG NE 1)
{
Expand Down Expand Up @@ -4124,6 +4134,15 @@ void write_relations()
name.schema = X.RDB$SCHEMA_NAME;
}

if (!X.RDB$PACKAGE_NAME.NULL)
{
PUT_TEXT(att_relation_package_name, X.RDB$PACKAGE_NAME);
name.package = X.RDB$PACKAGE_NAME;
}

if (!X.RDB$PRIVATE_FLAG.NULL)
put_int32(att_relation_private_flag, X.RDB$PRIVATE_FLAG);

PUT_TEXT(att_relation_name, X.RDB$RELATION_NAME);
name.object = X.RDB$RELATION_NAME;

Expand Down
9 changes: 9 additions & 0 deletions src/burp/burp.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,13 @@ Version 11: FB4.0.

Version 12: FB6.0.
Schemas.

Packaged tables:
RDB$RELATIONS.RDB$PACKAGE_NAME,
RDB$RELATIONS.RDB$PRIVATE_FLAG,
RDB$RELATION_FIELDSS.RDB$PACKAGE_NAME,
RDB$INDICES.RDB$PACKAGE_NAME and
RDB$INDEX_SEGMENTS.RDB$PACKAGE_NAME.
*/

inline constexpr int ATT_BACKUP_FORMAT = 12;
Expand Down Expand Up @@ -291,6 +298,8 @@ enum att_type {
att_relation_sql_security_deprecated, // can be removed later
att_relation_sql_security,
att_relation_schema_name,
att_relation_package_name,
att_relation_private_flag,

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

Expand Down
Loading
Loading