Skip to content

Commit 21c3507

Browse files
committed
Add support for returning table alias on Columns().
Currently the driver discards the column alias and the original table name. This change adds a new configuration parameter to include the table alias with the column name. This is useful because when mapping columns to structs, it is impossible to distinguish between duplicate column names when doing JOIN queries.
1 parent 9543750 commit 21c3507

File tree

7 files changed

+54
-19
lines changed

7 files changed

+54
-19
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Michael Woolnough <michael.woolnough at gmail.com>
2929
Nicola Peduzzi <thenikso at gmail.com>
3030
Xiaobing Jiang <s7v7nislands at gmail.com>
3131
Xiuming Chen <cc at cxm.cc>
32+
Chris Moos <chris at tech9computers.com>
3233

3334
# Organizations
3435

README.md

+15
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,21 @@ Default: false
166166

167167
`clientFoundRows=true` causes an UPDATE to return the number of matching rows instead of the number of rows changed.
168168

169+
##### `columnsWithAlias`
170+
171+
```
172+
Type: bool
173+
Valid Values: true, false
174+
Default: false
175+
```
176+
177+
When `columnsWithAlias` is true, calls to `sql.Rows.Columns()` will return the table alias and the column name separated by a dot. For example:
178+
179+
```
180+
SELECT u.id FROM users as u
181+
```
182+
183+
will return `u.id` instead of just `id` if `columnsWithAlias=true`.
169184

170185
##### `loc`
171186

connection.go

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type config struct {
4545
allowAllFiles bool
4646
allowOldPasswords bool
4747
clientFoundRows bool
48+
columnsWithAlias bool
4849
}
4950

5051
// Handles parameters set in DSN after the connection is established

packets.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -530,17 +530,19 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
530530
pos += n
531531

532532
// Table [len coded string]
533-
n, err = skipLengthEncodedString(data[pos:])
533+
tableName, _, n, err := readLengthEncodedString(data[pos:])
534534
if err != nil {
535535
return nil, err
536536
}
537+
columns[i].tableName = string(tableName)
537538
pos += n
538539

539540
// Original table [len coded string]
540-
n, err = skipLengthEncodedString(data[pos:])
541+
originalTableName, _, n, err := readLengthEncodedString(data[pos:])
541542
if err != nil {
542543
return nil, err
543544
}
545+
columns[i].originalTableName = string(originalTableName)
544546
pos += n
545547

546548
// Name [len coded string]

rows.go

+12-5
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ import (
1414
)
1515

1616
type mysqlField struct {
17-
name string
18-
flags fieldFlag
19-
fieldType byte
20-
decimals byte
17+
tableName string
18+
originalTableName string
19+
name string
20+
flags fieldFlag
21+
fieldType byte
22+
decimals byte
2123
}
2224

2325
type mysqlRows struct {
@@ -37,8 +39,13 @@ type emptyRows struct{}
3739

3840
func (rows *mysqlRows) Columns() []string {
3941
columns := make([]string, len(rows.columns))
42+
includeAlias := rows.mc.cfg.columnsWithAlias
4043
for i := range columns {
41-
columns[i] = rows.columns[i].name
44+
if includeAlias {
45+
columns[i] = rows.columns[i].tableName + "." + rows.columns[i].name
46+
} else {
47+
columns[i] = rows.columns[i].name
48+
}
4249
}
4350
return columns
4451
}

utils.go

+8
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ func parseDSN(dsn string) (cfg *config, err error) {
7676
cfg = &config{
7777
loc: time.UTC,
7878
collation: defaultCollation,
79+
columnsWithAlias: false,
7980
}
8081

8182
// TODO: use strings.IndexByte when we can depend on Go 1.2
@@ -215,6 +216,13 @@ func parseDSNParams(cfg *config, params string) (err error) {
215216
}
216217
cfg.collation = collation
217218
break
219+
220+
case "columnsWithAlias":
221+
var isBool bool
222+
cfg.columnsWithAlias, isBool = readBool(value)
223+
if !isBool {
224+
return fmt.Errorf("Invalid Bool value: %s", value)
225+
}
218226

219227
// Time Location
220228
case "loc":

utils_test.go

+13-12
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,19 @@ var testDSNs = []struct {
2121
out string
2222
loc *time.Location
2323
}{
24-
{"username:password@protocol(address)/dbname?param=value", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
25-
{"user@unix(/path/to/socket)/dbname?charset=utf8", "&{user:user passwd: net:unix addr:/path/to/socket dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
26-
{"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
27-
{"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8mb4,utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
28-
{"user:password@/dbname?loc=UTC&timeout=30s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{user:user passwd:password net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:30000000000 collation:224 allowAllFiles:true allowOldPasswords:true clientFoundRows:true}", time.UTC},
29-
{"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{user:user passwd:p@ss(word) net:tcp addr:[de:ad:be:ef::ca:fe]:80 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.Local},
30-
{"/dbname", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
31-
{"@/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
32-
{"/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
33-
{"", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
34-
{"user:p@/ssword@/", "&{user:user passwd:p@/ssword net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
35-
{"unix/?arg=%2Fsome%2Fpath.ext", "&{user: passwd: net:unix addr:/tmp/mysql.sock dbname: params:map[arg:/some/path.ext] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false}", time.UTC},
24+
{"username:password@protocol(address)/dbname?param=value", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false}", time.UTC},
25+
{"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true", "&{user:username passwd:password net:protocol addr:address dbname:dbname params:map[param:value] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:true}", time.UTC},
26+
{"user@unix(/path/to/socket)/dbname?charset=utf8", "&{user:user passwd: net:unix addr:/path/to/socket dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false}", time.UTC},
27+
{"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false}", time.UTC},
28+
{"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify", "&{user:user passwd:password net:tcp addr:localhost:5555 dbname:dbname params:map[charset:utf8mb4,utf8] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false}", time.UTC},
29+
{"user:password@/dbname?loc=UTC&timeout=30s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci", "&{user:user passwd:password net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:30000000000 collation:224 allowAllFiles:true allowOldPasswords:true clientFoundRows:true columnsWithAlias:false}", time.UTC},
30+
{"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local", "&{user:user passwd:p@ss(word) net:tcp addr:[de:ad:be:ef::ca:fe]:80 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false}", time.Local},
31+
{"/dbname", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname:dbname params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false}", time.UTC},
32+
{"@/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false}", time.UTC},
33+
{"/", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false}", time.UTC},
34+
{"", "&{user: passwd: net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false}", time.UTC},
35+
{"user:p@/ssword@/", "&{user:user passwd:p@/ssword net:tcp addr:127.0.0.1:3306 dbname: params:map[] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false}", time.UTC},
36+
{"unix/?arg=%2Fsome%2Fpath.ext", "&{user: passwd: net:unix addr:/tmp/mysql.sock dbname: params:map[arg:/some/path.ext] loc:%p tls:<nil> timeout:0 collation:33 allowAllFiles:false allowOldPasswords:false clientFoundRows:false columnsWithAlias:false}", time.UTC},
3637
}
3738

3839
func TestDSNParser(t *testing.T) {

0 commit comments

Comments
 (0)