-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdescribe_columns.go
134 lines (123 loc) · 3.74 KB
/
describe_columns.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package pgutil
import (
"context"
"fmt"
)
type ColumnDescription struct {
Name string
Type string
IsNullable bool
Default string
CharacterMaximumLength int
IsIdentity bool
IdentityGeneration string
IsGenerated bool
GenerationExpression string
}
func (d ColumnDescription) Equals(other ColumnDescription) bool {
return true &&
d.Name == other.Name &&
d.Type == other.Type &&
d.IsNullable == other.IsNullable &&
d.Default == other.Default &&
d.CharacterMaximumLength == other.CharacterMaximumLength &&
d.IsIdentity == other.IsIdentity &&
d.IdentityGeneration == other.IdentityGeneration &&
d.IsGenerated == other.IsGenerated &&
d.GenerationExpression == other.GenerationExpression
}
type column struct {
Namespace string
TableName string
Name string
Type string
IsNullable bool
Default *string
CharacterMaximumLength *int
IsIdentity bool
IdentityGeneration *string
IsGenerated bool
GenerationExpression *string
}
var scanColumns = NewSliceScanner(func(s Scanner) (c column, _ error) {
var (
isNullable string
isIdentity string
isGenerated string
)
err := s.Scan(
&c.Namespace,
&c.TableName,
&c.Name,
&c.Type,
&isNullable,
&c.Default,
&c.CharacterMaximumLength,
&isIdentity,
&c.IdentityGeneration,
&isGenerated,
&c.GenerationExpression,
)
c.IsNullable = truthy(isNullable)
c.IsIdentity = truthy(isIdentity)
c.IsGenerated = truthy(isGenerated)
return c, err
})
func describeColumns(ctx context.Context, db DB) (map[string][]ColumnDescription, error) {
columns, err := scanColumns(db.Query(ctx, RawQuery(`
SELECT
c.table_schema AS namespace,
c.table_name AS name,
c.column_name AS column_name,
CASE
WHEN c.data_type = 'ARRAY' THEN COALESCE((
SELECT e.data_type
FROM information_schema.element_types e
WHERE
e.object_type = 'TABLE' AND
e.object_catalog = c.table_catalog AND
e.object_schema = c.table_schema AND
e.object_name = c.table_name AND
e.collection_type_identifier = c.dtd_identifier
)) || '[]'
WHEN c.data_type = 'USER-DEFINED' THEN c.udt_name
WHEN c.character_maximum_length != 0 THEN c.data_type || '(' || c.character_maximum_length::text || ')'
ELSE c.data_type
END AS type,
c.is_nullable AS is_nullable,
c.column_default AS default,
c.character_maximum_length AS character_maximum_length,
c.is_identity AS is_identity,
c.identity_generation AS identity_generation,
c.is_generated AS is_generated,
c.generation_expression AS generation_expression
FROM information_schema.columns c
JOIN information_schema.tables t ON
t.table_schema = c.table_schema AND
t.table_name = c.table_name
WHERE
t.table_type = 'BASE TABLE' AND
t.table_schema NOT LIKE 'pg_%' AND
t.table_schema != 'information_schema'
ORDER BY c.table_schema, c.table_name, c.column_name
`)))
if err != nil {
return nil, err
}
columnMap := map[string][]ColumnDescription{}
for _, column := range columns {
key := fmt.Sprintf("%q.%q", column.Namespace, column.TableName)
columnMap[key] = append(columnMap[key], ColumnDescription{
Name: column.Name,
Type: column.Type,
IsNullable: column.IsNullable,
Default: deref(column.Default),
CharacterMaximumLength: deref(column.CharacterMaximumLength),
IsIdentity: column.IsIdentity,
IdentityGeneration: deref(column.IdentityGeneration),
IsGenerated: column.IsGenerated,
GenerationExpression: deref(column.GenerationExpression),
})
}
return columnMap, nil
}