Skip to content

Commit 135c490

Browse files
committed
Added a new configuration key include-generated-columns (or environment variable DB_DUMP_INCLUDE_GENERATED_COLUMNS) to allow users to control whether generated columns should be included in database dumps.
Signed-off-by: chenen <itfunx@hotmail.com>
1 parent ec3f9b3 commit 135c490

File tree

7 files changed

+156
-59
lines changed

7 files changed

+156
-59
lines changed

CHANGES_GENERATED_COLUMNS.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Add Configuration Option to Include Generated Columns
2+
3+
## Overview
4+
Added a new configuration key `include-generated-columns` (or environment variable `DB_DUMP_INCLUDE_GENERATED_COLUMNS`) to allow users to control whether generated columns should be included in database dumps.
5+
6+
## Problem
7+
Previously, the code would always exclude columns marked as `VIRTUAL` or `GENERATED` in the "Extra" field of MySQL's `SHOW COLUMNS` output. This was hardcoded behavior with no way to override it.
8+
9+
## Solution
10+
Implemented a configurable option to control this behavior by:
11+
12+
1. Adding a new `IncludeGeneratedColumns` boolean field throughout the configuration chain
13+
2. Modifying the column filtering logic to respect this setting
14+
15+
## Files Modified
16+
17+
### 1. `pkg/database/mysql/dump.go`
18+
- Added `IncludeGeneratedColumns bool` field to the `Data` struct
19+
- This field controls whether generated columns should be included in dumps
20+
21+
### 2. `pkg/database/mysql/table.go`
22+
- Modified `initColumnData()` function to use `IncludeGeneratedColumns` setting
23+
- **Before**: Columns with `GENERATED` or `VIRTUAL` were always excluded
24+
- **After**:
25+
- Columns with `VIRTUAL` are always excluded (computed columns that can't be stored)
26+
- Columns with `GENERATED` are excluded UNLESS `IncludeGeneratedColumns` is `true`
27+
28+
### 3. `pkg/database/dump.go`
29+
- Added `IncludeGeneratedColumns bool` to `DumpOpts` struct
30+
- Updated the `Dump()` function to pass this setting to `mysql.Data`
31+
32+
### 4. `pkg/core/dumpoptions.go`
33+
- Added `IncludeGeneratedColumns bool` to `DumpOptions` struct
34+
- This carries the setting from CLI/config through to the database layer
35+
36+
### 5. `pkg/core/dump.go`
37+
- Updated `Dump()` function to include `IncludeGeneratedColumns` when creating `database.DumpOpts`
38+
39+
### 6. `cmd/dump.go`
40+
- Added `--include-generated-columns` CLI flag (boolean, defaults to false)
41+
- Added parsing logic to read the flag value
42+
- Added `includeGeneratedColumns` variable to the command execution path
43+
- Passed the setting to `core.DumpOptions` struct
44+
45+
## Usage
46+
47+
### Command Line
48+
```bash
49+
# Enable inclusion of generated columns in dump
50+
db-backup dump --server myhost --target file:///backups --include-generated-columns
51+
52+
# Or using environment variable
53+
export DB_DUMP_INCLUDE_GENERATED_COLUMNS=true
54+
db-backup dump --server myhost --target file:///backups
55+
```
56+
57+
### Default Behavior
58+
By default (`--include-generated-columns` is not set), the behavior is unchanged:
59+
- `VIRTUAL` columns are excluded
60+
- `GENERATED` columns are excluded
61+
62+
### With the Flag
63+
When `--include-generated-columns` is set:
64+
- `VIRTUAL` columns are still excluded (they cannot be restored)
65+
- `GENERATED` columns are included in the dump
66+
67+
## Environment Variable
68+
The setting can also be controlled via environment variable:
69+
- **Variable name**: `DB_DUMP_INCLUDE_GENERATED_COLUMNS`
70+
- **Values**: `true` or `false`
71+
- **Example**: `export DB_DUMP_INCLUDE_GENERATED_COLUMNS=true`
72+
73+
## Notes
74+
- Config file support would require updates to the external `api.Dump` type in the databacker/api repository
75+
- The change maintains backward compatibility; existing behavior is preserved when the flag is not used
76+
- `VIRTUAL` columns are always excluded as they cannot be dumped and restored

cmd/dump.go

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ func dumpCmd(passedExecs execs, cmdConfig *cmdConfiguration) (*cobra.Command, er
118118
if !v.IsSet("skip-extended-insert") && dumpConfig != nil && dumpConfig.SkipExtendedInsert != nil {
119119
skipExtendedInsert = *dumpConfig.SkipExtendedInsert
120120
}
121+
includeGeneratedColumns := v.GetBool("include-generated-columns")
122+
// Note: config file support for include-generated-columns would require updates to api.Dump
121123
compact := v.GetBool("compact")
122124
if !v.IsSet("compact") && dumpConfig != nil && dumpConfig.Compact != nil {
123125
compact = *dumpConfig.Compact
@@ -251,24 +253,25 @@ func dumpCmd(passedExecs execs, cmdConfig *cmdConfiguration) (*cobra.Command, er
251253
defer dumpSpan.End()
252254
uid := uuid.New()
253255
dumpOpts := core.DumpOptions{
254-
Targets: targets,
255-
Safechars: safechars,
256-
DBNames: include,
257-
DBConn: cmdConfig.dbconn,
258-
Compressor: compressor,
259-
Encryptor: encryptor,
260-
Exclude: exclude,
261-
PreBackupScripts: preBackupScripts,
262-
PostBackupScripts: postBackupScripts,
263-
SuppressUseDatabase: noDatabaseName,
264-
SkipExtendedInsert: skipExtendedInsert,
265-
Compact: compact,
266-
Triggers: triggers,
267-
Routines: routines,
268-
MaxAllowedPacket: maxAllowedPacket,
269-
Run: uid,
270-
FilenamePattern: filenamePattern,
271-
Parallelism: parallel,
256+
Targets: targets,
257+
Safechars: safechars,
258+
DBNames: include,
259+
DBConn: cmdConfig.dbconn,
260+
Compressor: compressor,
261+
Encryptor: encryptor,
262+
Exclude: exclude,
263+
PreBackupScripts: preBackupScripts,
264+
PostBackupScripts: postBackupScripts,
265+
SuppressUseDatabase: noDatabaseName,
266+
SkipExtendedInsert: skipExtendedInsert,
267+
Compact: compact,
268+
Triggers: triggers,
269+
Routines: routines,
270+
IncludeGeneratedColumns: includeGeneratedColumns,
271+
MaxAllowedPacket: maxAllowedPacket,
272+
Run: uid,
273+
FilenamePattern: filenamePattern,
274+
Parallelism: parallel,
272275
}
273276
_, err := executor.Dump(tracerCtx, dumpOpts)
274277
if err != nil {
@@ -315,6 +318,9 @@ S3: If it is a URL of the format s3://bucketname/path then it will connect via S
315318
// skip extended insert in dump; instead, one INSERT per record in each table
316319
flags.Bool("skip-extended-insert", false, "Skip extended insert in dump; instead, one INSERT per record in each table.")
317320

321+
// include generated columns in dump
322+
flags.Bool("include-generated-columns", false, "Include generated columns in the dump. By default, generated and virtual columns are excluded.")
323+
318324
// frequency
319325
flags.Int("frequency", defaultFrequency, "how often to run backups, in minutes")
320326

pkg/core/dump.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,15 @@ func (e *Executor) Dump(ctx context.Context, opts DumpOptions) (DumpResults, err
108108
results.DumpStart = time.Now()
109109
dbDumpCtx, dbDumpSpan := tracer.Start(ctx, "database_dump")
110110
if err := database.Dump(dbDumpCtx, dbconn, database.DumpOpts{
111-
Compact: compact,
112-
Triggers: triggers,
113-
Routines: routines,
114-
SuppressUseDatabase: suppressUseDatabase,
115-
SkipExtendedInsert: skipExtendedInsert,
116-
MaxAllowedPacket: maxAllowedPacket,
117-
PostDumpDelay: opts.PostDumpDelay,
118-
Parallelism: parallelism,
111+
Compact: compact,
112+
Triggers: triggers,
113+
Routines: routines,
114+
SuppressUseDatabase: suppressUseDatabase,
115+
SkipExtendedInsert: skipExtendedInsert,
116+
MaxAllowedPacket: maxAllowedPacket,
117+
IncludeGeneratedColumns: opts.IncludeGeneratedColumns,
118+
PostDumpDelay: opts.PostDumpDelay,
119+
Parallelism: parallelism,
119120
}, dw); err != nil {
120121
dbDumpSpan.SetStatus(codes.Error, err.Error())
121122
dbDumpSpan.End()

pkg/core/dumpoptions.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type DumpOptions struct {
2525
Routines bool
2626
SuppressUseDatabase bool
2727
SkipExtendedInsert bool
28+
IncludeGeneratedColumns bool
2829
MaxAllowedPacket int
2930
Run uuid.UUID
3031
FilenamePattern string

pkg/database/dump.go

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ import (
1010
)
1111

1212
type DumpOpts struct {
13-
Compact bool
14-
Triggers bool
15-
Routines bool
16-
SuppressUseDatabase bool
17-
SkipExtendedInsert bool
18-
MaxAllowedPacket int
13+
Compact bool
14+
Triggers bool
15+
Routines bool
16+
SuppressUseDatabase bool
17+
SkipExtendedInsert bool
18+
MaxAllowedPacket int
19+
IncludeGeneratedColumns bool
1920
// PostDumpDelay after each dump is complete, while holding connection open. Do not use outside of tests.
2021
PostDumpDelay time.Duration
2122
Parallelism int
@@ -52,17 +53,18 @@ func Dump(ctx context.Context, dbconn *Connection, opts DumpOpts, writers []Dump
5253
defer func() { <-sem }()
5354
for _, schema := range writer.Schemas {
5455
dumper := &mysql.Data{
55-
Out: writer.Writer,
56-
Connection: db,
57-
Schema: schema,
58-
Host: dbconn.Host,
59-
Compact: opts.Compact,
60-
Triggers: opts.Triggers,
61-
Routines: opts.Routines,
62-
SuppressUseDatabase: opts.SuppressUseDatabase,
63-
SkipExtendedInsert: opts.SkipExtendedInsert,
64-
MaxAllowedPacket: opts.MaxAllowedPacket,
65-
PostDumpDelay: opts.PostDumpDelay,
56+
Out: writer.Writer,
57+
Connection: db,
58+
Schema: schema,
59+
Host: dbconn.Host,
60+
Compact: opts.Compact,
61+
Triggers: opts.Triggers,
62+
Routines: opts.Routines,
63+
SuppressUseDatabase: opts.SuppressUseDatabase,
64+
SkipExtendedInsert: opts.SkipExtendedInsert,
65+
MaxAllowedPacket: opts.MaxAllowedPacket,
66+
PostDumpDelay: opts.PostDumpDelay,
67+
IncludeGeneratedColumns: opts.IncludeGeneratedColumns,
6668
}
6769
// return on any error
6870
if err := dumper.Dump(); err != nil {

pkg/database/mysql/dump.go

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,22 @@ Data struct to configure dump behavior
3939
LockTables: Lock all tables for the duration of the dump
4040
*/
4141
type Data struct {
42-
Out io.Writer
43-
Connection *sql.DB
44-
IgnoreTables []string
45-
MaxAllowedPacket int
46-
LockTables bool
47-
Schema string
48-
Compact bool
49-
Triggers bool
50-
Routines bool
51-
Host string
52-
SuppressUseDatabase bool
53-
SkipExtendedInsert bool
54-
Charset string
55-
Collation string
56-
PostDumpDelay time.Duration
42+
Out io.Writer
43+
Connection *sql.DB
44+
IgnoreTables []string
45+
MaxAllowedPacket int
46+
LockTables bool
47+
Schema string
48+
Compact bool
49+
Triggers bool
50+
Routines bool
51+
Host string
52+
SuppressUseDatabase bool
53+
SkipExtendedInsert bool
54+
Charset string
55+
Collation string
56+
PostDumpDelay time.Duration
57+
IncludeGeneratedColumns bool
5758

5859
tx *sql.Tx
5960
headerTmpl *template.Template

pkg/database/mysql/table.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,17 @@ func (table *baseTable) initColumnData() error {
132132
// Ignore the virtual columns and generated columns
133133
// if there is an Extra column and it is a valid string, then only include this column if
134134
// the column is not marked as VIRTUAL or GENERATED
135-
if !info[extraIndex].Valid || (!strings.Contains(info[extraIndex].String, "VIRTUAL") && !strings.Contains(info[extraIndex].String, "GENERATED")) {
135+
// Unless IncludeGeneratedColumns is true, in which case include them
136+
shouldInclude := true
137+
if info[extraIndex].Valid {
138+
extra := info[extraIndex].String
139+
if strings.Contains(extra, "VIRTUAL") {
140+
shouldInclude = false
141+
} else if strings.Contains(extra, "GENERATED") && !table.data.IncludeGeneratedColumns {
142+
shouldInclude = false
143+
}
144+
}
145+
if shouldInclude {
136146
result = append(result, info[fieldIndex].String)
137147
}
138148
}

0 commit comments

Comments
 (0)