|
| 1 | +# Created Local Temporary Tables (FB 6.0) |
| 2 | + |
| 3 | +Firebird 6.0 introduces support for SQL Created Local Temporary Tables (LTT). Unlike Global Temporary Tables (GTT), |
| 4 | +which have permanent metadata stored in the system catalogue, Local Temporary Tables exist only within the connection |
| 5 | +that created them. The table definition is private to the creating connection and is automatically discarded when the |
| 6 | +connection ends. The data lifecycle depends on the `ON COMMIT` clause: with `ON COMMIT DELETE ROWS` (the default), data |
| 7 | +is private to each transaction and deleted when the transaction ends; with `ON COMMIT PRESERVE ROWS`, data is shared |
| 8 | +across all transactions in the connection and persists until the connection ends. |
| 9 | + |
| 10 | +## Why Local Temporary Tables? |
| 11 | + |
| 12 | +Local Temporary Tables are useful in scenarios where you need temporary storage without affecting the database metadata: |
| 13 | + |
| 14 | +### Temporary storage in read-only databases |
| 15 | + |
| 16 | +Since LTT definitions are not stored in the database, they can be created and used even when the database is read-only. |
| 17 | + |
| 18 | +Global Temporary Tables (GTTs), on the other hand, cannot be created in read-only databases because their creation |
| 19 | +requires metadata changes. Regarding usage (DML operations), only ON DELETE ROWS GTTs are allowed in a read-only |
| 20 | +database. |
| 21 | + |
| 22 | +### Session-private definitions |
| 23 | + |
| 24 | +Each connection can create its own temporary tables with the same names without conflicts. The table definitions are |
| 25 | +completely isolated between connections. |
| 26 | + |
| 27 | +### Ad-hoc temporary storage |
| 28 | + |
| 29 | +LTTs provide a quick way to create temporary storage during a session for intermediate results, data transformations, |
| 30 | +or other temporary processing needs without leaving any trace in the database after disconnection. |
| 31 | + |
| 32 | +## Comparison with Global Temporary Tables |
| 33 | + |
| 34 | +| Feature | GTT | LTT | |
| 35 | +|-----------------------------|--------------------------------------|---------------------------------------| |
| 36 | +| Metadata storage | System tables (`RDB$RELATIONS`, etc.)| Connection memory only | |
| 37 | +| Visibility of definition | All connections | Creating connection only | |
| 38 | +| Persistence of definition | Permanent (until explicitly dropped) | Until connection ends | |
| 39 | +| Creation in read-only DB | No | Yes | |
| 40 | +| Used in read-only DB | ON DELETE ROWS only | ON COMMIT ROWS / ON DELETE ROWS | |
| 41 | +| Schema support | Yes | Yes | |
| 42 | +| Indexes | Full support | Basic support (no expression/partial) | |
| 43 | +| DDL triggers | Fire on CREATE/DROP/ALTER | Do not fire | |
| 44 | +| DML triggers support | Yes | Not supported | |
| 45 | +| Field level NOT NULL | Supported | Supported | |
| 46 | +| PK, FK, CHECK constraints | Supported | Not supported | |
| 47 | +| Named constraints | Supported | Not supported | |
| 48 | +| Explicit privileges | Supported | Not supported | |
| 49 | + |
| 50 | +## Syntax |
| 51 | + |
| 52 | +### CREATE LOCAL TEMPORARY TABLE |
| 53 | + |
| 54 | +```sql |
| 55 | +{CREATE | RECREATE} LOCAL TEMPORARY TABLE [IF NOT EXISTS] [<schema name>.]<table name> |
| 56 | + (<column definitions>) |
| 57 | + [ON COMMIT {DELETE | PRESERVE} ROWS] |
| 58 | +``` |
| 59 | + |
| 60 | +The `ON COMMIT` clause determines the data lifecycle: |
| 61 | + |
| 62 | +- `ON COMMIT DELETE ROWS` (default): Data is private to each transaction and deleted when the transaction ends. |
| 63 | +- `ON COMMIT PRESERVE ROWS`: Data persists until the connection ends. |
| 64 | + |
| 65 | +Example: |
| 66 | + |
| 67 | +```sql |
| 68 | +-- Create a simple LTT with default ON COMMIT DELETE ROWS |
| 69 | +create local temporary table temp_results ( |
| 70 | + id integer not null, |
| 71 | + val varchar(100) |
| 72 | +); |
| 73 | + |
| 74 | +-- Create an LTT that preserves data across transactions |
| 75 | +create local temporary table session_cache ( |
| 76 | + key varchar(50) not null, |
| 77 | + data blob |
| 78 | +) on commit preserve rows; |
| 79 | + |
| 80 | +-- Create an LTT in a specific schema |
| 81 | +create local temporary table my_schema.work_table ( |
| 82 | + x integer, |
| 83 | + y integer |
| 84 | +); |
| 85 | +``` |
| 86 | + |
| 87 | +### ALTER TABLE |
| 88 | + |
| 89 | +Local Temporary Tables support the following `ALTER TABLE` operations: |
| 90 | + |
| 91 | +```sql |
| 92 | +-- Add a column |
| 93 | +ALTER TABLE <ltt name> ADD <column name> <data type> [NOT NULL]; |
| 94 | + |
| 95 | +-- Drop a column |
| 96 | +ALTER TABLE <ltt name> DROP <column name>; |
| 97 | + |
| 98 | +-- Rename a column |
| 99 | +ALTER TABLE <ltt name> ALTER COLUMN <old name> TO <new name>; |
| 100 | + |
| 101 | +-- Change column position |
| 102 | +ALTER TABLE <ltt name> ALTER COLUMN <column name> POSITION <new position>; |
| 103 | + |
| 104 | +-- Change nullability |
| 105 | +ALTER TABLE <ltt name> ALTER COLUMN <column name> {DROP | SET} NOT NULL; |
| 106 | + |
| 107 | +-- Change column type |
| 108 | +ALTER TABLE <ltt name> ALTER COLUMN <column name> TYPE <new data type>; |
| 109 | +``` |
| 110 | + |
| 111 | +Example: |
| 112 | + |
| 113 | +```sql |
| 114 | +create local temporary table temp_data (id integer); |
| 115 | + |
| 116 | +alter table temp_data add name varchar(50) not null; |
| 117 | +alter table temp_data alter column name position 1; |
| 118 | +alter table temp_data alter column name to full_name; |
| 119 | +alter table temp_data alter column id type bigint; |
| 120 | +``` |
| 121 | + |
| 122 | +### DROP TABLE |
| 123 | + |
| 124 | +```sql |
| 125 | +DROP TABLE [IF EXISTS] [<schema name>.]<ltt name> |
| 126 | +``` |
| 127 | + |
| 128 | +The same `DROP TABLE` statement is used for both regular tables and Local Temporary Tables. If the table name matches |
| 129 | +an LTT in the current connection, the LTT is dropped. |
| 130 | + |
| 131 | +### CREATE INDEX |
| 132 | + |
| 133 | +```sql |
| 134 | +CREATE [UNIQUE] [ASC[ENDING] | DESC[ENDING]] INDEX [IF NOT EXISTS] [<schema name>.]<index name> |
| 135 | + ON [<schema name>.]<ltt name> (<column list>) |
| 136 | +``` |
| 137 | + |
| 138 | +Example: |
| 139 | + |
| 140 | +```sql |
| 141 | +create local temporary table temp_orders ( |
| 142 | + order_id integer not null, |
| 143 | + customer_id integer, |
| 144 | + order_date date |
| 145 | +); |
| 146 | + |
| 147 | +create unique index idx_temp_orders_pk on temp_orders (order_id); |
| 148 | +create descending index idx_temp_orders_date on temp_orders (order_date); |
| 149 | +create index idx_temp_orders_cust on temp_orders (customer_id); |
| 150 | +``` |
| 151 | + |
| 152 | +### ALTER INDEX |
| 153 | + |
| 154 | +```sql |
| 155 | +ALTER INDEX <index name> {ACTIVE | INACTIVE} |
| 156 | +``` |
| 157 | + |
| 158 | +Indexes on LTTs can be deactivated and reactivated, which may be useful when performing bulk inserts in |
| 159 | +`ON COMMIT PRESERVE ROWS` tables. |
| 160 | + |
| 161 | +### DROP INDEX |
| 162 | + |
| 163 | +```sql |
| 164 | +DROP INDEX [IF EXISTS] <index name> |
| 165 | +``` |
| 166 | + |
| 167 | +## Limitations |
| 168 | + |
| 169 | +Local Temporary Tables have the following restrictions: |
| 170 | + |
| 171 | +### Column restrictions |
| 172 | + |
| 173 | +- **DEFAULT values**: Columns cannot have default values. |
| 174 | +- **COMPUTED BY columns**: Computed columns are not supported. |
| 175 | +- **IDENTITY columns**: Identity (auto-increment) columns are not supported. |
| 176 | +- **ARRAY types**: Array type columns are not supported. |
| 177 | +- **Domain changes**: Columns can be defined using domains, but changes to the domain definition are not propagated to |
| 178 | + existing LTT columns. The column retains the domain's characteristics as they were at the time the column was created. |
| 179 | + |
| 180 | +### Constraint restrictions |
| 181 | + |
| 182 | +- **PRIMARY KEY**: Primary key constraints are not supported. |
| 183 | +- **FOREIGN KEY**: Foreign key constraints are not supported. |
| 184 | +- **CHECK constraints**: Check constraints are not supported. |
| 185 | +- **NOT NULL constraints**: Only unnamed NOT NULL constraints are supported. |
| 186 | + |
| 187 | +### Index restrictions |
| 188 | + |
| 189 | +- **Expression-based indexes**: Indexes based on expressions are not supported. |
| 190 | +- **Partial indexes**: Partial (filtered) indexes are not supported. |
| 191 | + |
| 192 | +### Other restrictions |
| 193 | + |
| 194 | +- **EXTERNAL FILE**: LTTs cannot be linked to external files. |
| 195 | +- **SQL SECURITY clause**: The SQL SECURITY clause is not applicable to LTTs. |
| 196 | +- **Max number of LTTs**: The maximum number of active Local Temporary Tables per connection is 1024. |
| 197 | +- **Persistent metadata references**: LTTs cannot be directly referenced in stored procedures, triggers, views, or |
| 198 | + other persistent database objects. Attempting to do so will raise an error. However, LTTs can be accessed via |
| 199 | + `EXECUTE STATEMENT` inside persistent objects, since the SQL text is parsed at runtime. |
| 200 | + |
| 201 | +## Data Lifecycle |
| 202 | + |
| 203 | +### ON COMMIT DELETE ROWS |
| 204 | + |
| 205 | +When a Local Temporary Table is created with `ON COMMIT DELETE ROWS` (the default), data is private to each transaction |
| 206 | +and automatically deleted when the transaction ends, whether by commit or rollback. Since Firebird supports multiple |
| 207 | +concurrent transactions within the same connection, each transaction has its own isolated view of the data. |
| 208 | + |
| 209 | +```sql |
| 210 | +create local temporary table temp_work (id integer); |
| 211 | + |
| 212 | +insert into temp_work values (1); |
| 213 | +insert into temp_work values (2); |
| 214 | +select count(*) from temp_work; -- Returns 2 |
| 215 | + |
| 216 | +commit; |
| 217 | + |
| 218 | +select count(*) from temp_work; -- Returns 0 (data was deleted) |
| 219 | +``` |
| 220 | + |
| 221 | +### ON COMMIT PRESERVE ROWS |
| 222 | + |
| 223 | +When created with `ON COMMIT PRESERVE ROWS`, data persists across transaction boundaries and remains available until |
| 224 | +the connection ends. |
| 225 | + |
| 226 | +```sql |
| 227 | +create local temporary table session_data (id integer) on commit preserve rows; |
| 228 | + |
| 229 | +insert into session_data values (1); |
| 230 | +commit; |
| 231 | + |
| 232 | +insert into session_data values (2); |
| 233 | +commit; |
| 234 | + |
| 235 | +select count(*) from session_data; -- Returns 2 (data preserved across commits) |
| 236 | +``` |
| 237 | + |
| 238 | +### COMMIT/ROLLBACK RETAINING |
| 239 | + |
| 240 | +Similar to Global Temporary Tables, `COMMIT RETAINING` and `ROLLBACK RETAINING` preserve the data in LTTs with |
| 241 | +`ON COMMIT DELETE ROWS`. |
| 242 | + |
| 243 | +## Transactional DDL |
| 244 | + |
| 245 | +DDL operations on Local Temporary Tables behave the same as DDL on persistent tables with respect to transactions. |
| 246 | +Changes to the table structure (CREATE, ALTER, DROP) are only made permanent when the transaction commits. If the |
| 247 | +transaction is rolled back, any LTT structural changes made within that transaction are undone. |
| 248 | + |
| 249 | +Savepoints are also supported. If a savepoint is rolled back, any LTT changes made after that savepoint are undone. |
| 250 | + |
| 251 | +```sql |
| 252 | +set autoddl off; -- ISQL feature |
| 253 | + |
| 254 | +create local temporary table t1 (id integer); |
| 255 | + |
| 256 | +savepoint sp1; |
| 257 | + |
| 258 | +alter table t1 add name varchar(50); |
| 259 | + |
| 260 | +rollback to savepoint sp1; |
| 261 | + |
| 262 | +-- The ALTER TABLE is undone; column 'name' does not exist |
| 263 | +``` |
| 264 | + |
| 265 | +This applies to all DDL operations on LTTs. |
| 266 | + |
| 267 | +## Schema Integration |
| 268 | + |
| 269 | +Local Temporary Tables are schema-bound objects, just like regular tables. They follow the same schema resolution |
| 270 | +rules: |
| 271 | + |
| 272 | +- When creating an LTT without a schema qualifier, it is created in the current schema (the first valid schema in the |
| 273 | + search path). |
| 274 | +- When referencing an LTT without a schema qualifier, the search path is used to resolve the name. |
| 275 | +- Index names for LTTs must be unique within the schema and cannot conflict with persistent index names. |
| 276 | + |
| 277 | +```sql |
| 278 | +set search_path to my_schema; |
| 279 | + |
| 280 | +-- Creates LTT in my_schema |
| 281 | +create local temporary table temp_data (id integer); |
| 282 | + |
| 283 | +-- References my_schema.temp_data |
| 284 | +select * from temp_data; |
| 285 | + |
| 286 | +-- Explicit schema qualification |
| 287 | +create local temporary table other_schema.temp_work (x integer); |
| 288 | +``` |
| 289 | + |
| 290 | +## Monitoring |
| 291 | + |
| 292 | +Since Local Temporary Tables are private to the connection and not stored in the system catalogue, they are not visible |
| 293 | +in `RDB$RELATIONS` or `RDB$RELATION_FIELDS`. Instead, two new monitoring tables are provided to inspect LTTs in the |
| 294 | +database. |
| 295 | + |
| 296 | +### MON$LOCAL_TEMPORARY_TABLES |
| 297 | + |
| 298 | +Provides information about active Local Temporary Tables across all connections. |
| 299 | + |
| 300 | +| Column Name | Data Type | Description | |
| 301 | +|---------------------|--------------|----------------------------------------------------------| |
| 302 | +| MON$ATTACHMENT_ID | BIGINT | Attachment ID | |
| 303 | +| MON$TABLE_ID | INTEGER | Internal table ID (unique within the connection) | |
| 304 | +| MON$TABLE_NAME | CHAR(63) | Table name | |
| 305 | +| MON$SCHEMA_NAME | CHAR(63) | Schema name | |
| 306 | +| MON$TABLE_TYPE | VARCHAR(32) | Table type (`PRESERVE ROWS` or `DELETE ROWS`) | |
| 307 | + |
| 308 | +### MON$LOCAL_TEMPORARY_TABLE_COLUMNS |
| 309 | + |
| 310 | +Provides information about the columns of active Local Temporary Tables across all connections. |
| 311 | + |
| 312 | +| Column Name | Data Type | Description | |
| 313 | +|--------------------------|-------------|-------------------------------------------------------| |
| 314 | +| MON$ATTACHMENT_ID | BIGINT | Attachment ID | |
| 315 | +| MON$TABLE_ID | INTEGER | Internal table ID | |
| 316 | +| MON$TABLE_NAME | CHAR(63) | Table name | |
| 317 | +| MON$SCHEMA_NAME | CHAR(63) | Schema name | |
| 318 | +| MON$FIELD_NAME | CHAR(63) | Field name | |
| 319 | +| MON$FIELD_POSITION | SMALLINT | Field position | |
| 320 | +| MON$FIELD_TYPE | SMALLINT | Field type | |
| 321 | +| MON$NOT_NULL | SMALLINT | Nullability flag (1 for NOT NULL, 0 for NULL) | |
| 322 | +| MON$CHARACTER_SET_ID | SMALLINT | Character set ID | |
| 323 | +| MON$COLLATION_ID | SMALLINT | Collation ID | |
| 324 | +| MON$FIELD_LENGTH | SMALLINT | Field length | |
| 325 | +| MON$FIELD_SCALE | SMALLINT | Field scale | |
| 326 | +| MON$FIELD_PRECISION | SMALLINT | Field precision | |
| 327 | +| MON$FIELD_SUB_TYPE | SMALLINT | Field sub-type | |
| 328 | +| MON$CHAR_LENGTH | INTEGER | Character length | |
| 329 | + |
| 330 | +Example: |
| 331 | + |
| 332 | +```sql |
| 333 | +-- See all LTTs in the database |
| 334 | +select * from MON$LOCAL_TEMPORARY_TABLES; |
| 335 | + |
| 336 | +-- See fields of a specific LTT in the current connection |
| 337 | +select * |
| 338 | + from MON$LOCAL_TEMPORARY_TABLE_COLUMNS |
| 339 | + where MON$TABLE_NAME = 'TEMP_DATA' and MON$ATTACHMENT_ID = CURRENT_CONNECTION; |
| 340 | +``` |
| 341 | + |
| 342 | +## Implementation Notes |
| 343 | + |
| 344 | +Local Temporary Tables store their data and indexes in temporary files, similar to Global Temporary Tables. Each |
| 345 | +connection has its own temporary file space for LTT data. |
| 346 | + |
| 347 | +When a connection ends (either normally or due to an error), all Local Temporary Tables created by that connection |
| 348 | +are automatically discarded along with their data. No explicit cleanup is required. |
0 commit comments