Skip to content

Commit 3c9ef73

Browse files
authored
feat(postgresql): Add support for PostgreSQL multi-dimensional arrays (#2338)
This PR adds support for generating model fields from multi-dimensional postgresql array columns. The approach here is to pass around an additional column attribute array_bounds representing the dimensions of the postgres array column. When generating code, the array_bounds is checked along is_array to determine the dimensions of the resulting struct field. Would be happy to hear alternative approaches or solutions here as well. https://www.postgresql.org/docs/14/arrays.html Fixes issue: #1494 Replaces #1651 #2308 #2309
1 parent 8424410 commit 3c9ef73

File tree

31 files changed

+15008
-7355
lines changed

31 files changed

+15008
-7355
lines changed

internal/cmd/shim.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,12 @@ func pluginCatalog(c *catalog.Catalog) *plugin.Catalog {
169169
Schema: c.Type.Schema,
170170
Name: c.Type.Name,
171171
},
172-
Comment: c.Comment,
173-
NotNull: c.IsNotNull,
174-
Unsigned: c.IsUnsigned,
175-
IsArray: c.IsArray,
176-
Length: int32(l),
172+
Comment: c.Comment,
173+
NotNull: c.IsNotNull,
174+
Unsigned: c.IsUnsigned,
175+
IsArray: c.IsArray,
176+
ArrayDims: int32(c.ArrayDims),
177+
Length: int32(l),
177178
Table: &plugin.Identifier{
178179
Catalog: t.Rel.Catalog,
179180
Schema: t.Rel.Schema,
@@ -252,6 +253,7 @@ func pluginQueryColumn(c *compiler.Column) *plugin.Column {
252253
NotNull: c.NotNull,
253254
Unsigned: c.Unsigned,
254255
IsArray: c.IsArray,
256+
ArrayDims: int32(c.ArrayDims),
255257
Length: int32(l),
256258
IsNamedParam: c.IsNamedParam,
257259
IsFuncCall: c.IsFuncCall,

internal/codegen/golang/go_type.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package golang
22

33
import (
4+
"strings"
5+
46
"github.com/sqlc-dev/sqlc/internal/codegen/sdk"
57
"github.com/sqlc-dev/sqlc/internal/plugin"
68
)
@@ -48,9 +50,12 @@ func goType(req *plugin.CodeGenRequest, col *plugin.Column) string {
4850
}
4951
}
5052
typ := goInnerType(req, col)
51-
if col.IsArray || col.IsSqlcSlice {
53+
if col.IsSqlcSlice {
5254
return "[]" + typ
5355
}
56+
if col.IsArray {
57+
return strings.Repeat("[]", int(col.ArrayDims)) + typ
58+
}
5459
return typ
5560
}
5661

internal/compiler/output_columns.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func (c *Compiler) OutputColumns(stmt ast.Node) ([]*catalog.Column, error) {
3131
IsNotNull: col.NotNull,
3232
IsUnsigned: col.Unsigned,
3333
IsArray: col.IsArray,
34+
ArrayDims: col.ArrayDims,
3435
Comment: col.Comment,
3536
Length: col.Length,
3637
})
@@ -289,6 +290,7 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
289290
NotNull: c.NotNull,
290291
Unsigned: c.Unsigned,
291292
IsArray: c.IsArray,
293+
ArrayDims: c.ArrayDims,
292294
Length: c.Length,
293295
})
294296
}
@@ -626,6 +628,7 @@ func outputColumnRefs(res *ast.ResTarget, tables []*Table, node *ast.ColumnRef)
626628
NotNull: c.NotNull,
627629
Unsigned: c.Unsigned,
628630
IsArray: c.IsArray,
631+
ArrayDims: c.ArrayDims,
629632
Length: c.Length,
630633
EmbedTable: c.EmbedTable,
631634
OriginalName: c.Name,

internal/compiler/query.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type Column struct {
2121
NotNull bool
2222
Unsigned bool
2323
IsArray bool
24+
ArrayDims int
2425
Comment string
2526
Length *int
2627
IsNamedParam bool

internal/compiler/query_catalog.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,15 @@ func (comp *Compiler) buildQueryCatalog(c *catalog.Catalog, node ast.Node, embed
5252

5353
func ConvertColumn(rel *ast.TableName, c *catalog.Column) *Column {
5454
return &Column{
55-
Table: rel,
56-
Name: c.Name,
57-
DataType: dataType(&c.Type),
58-
NotNull: c.IsNotNull,
59-
Unsigned: c.IsUnsigned,
60-
IsArray: c.IsArray,
61-
Type: &c.Type,
62-
Length: c.Length,
55+
Table: rel,
56+
Name: c.Name,
57+
DataType: dataType(&c.Type),
58+
NotNull: c.IsNotNull,
59+
Unsigned: c.IsUnsigned,
60+
IsArray: c.IsArray,
61+
ArrayDims: c.ArrayDims,
62+
Type: &c.Type,
63+
Length: c.Length,
6364
}
6465
}
6566

internal/compiler/resolve.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ func (comp *Compiler) resolveCatalogRefs(qc *QueryCatalog, rvs []*ast.RangeVar,
218218
NotNull: p.NotNull(),
219219
Unsigned: c.IsUnsigned,
220220
IsArray: c.IsArray,
221+
ArrayDims: c.ArrayDims,
221222
Length: c.Length,
222223
Table: table,
223224
IsNamedParam: isNamed,
@@ -283,6 +284,7 @@ func (comp *Compiler) resolveCatalogRefs(qc *QueryCatalog, rvs []*ast.RangeVar,
283284
NotNull: p.NotNull(),
284285
Unsigned: c.IsUnsigned,
285286
IsArray: c.IsArray,
287+
ArrayDims: c.ArrayDims,
286288
Table: table,
287289
IsNamedParam: isNamed,
288290
IsSqlcSlice: p.IsSqlcSlice(),
@@ -459,6 +461,7 @@ func (comp *Compiler) resolveCatalogRefs(qc *QueryCatalog, rvs []*ast.RangeVar,
459461
NotNull: p.NotNull(),
460462
Unsigned: c.IsUnsigned,
461463
IsArray: c.IsArray,
464+
ArrayDims: c.ArrayDims,
462465
Table: &ast.TableName{Schema: schema, Name: rel},
463466
Length: c.Length,
464467
IsNamedParam: isNamed,
@@ -569,6 +572,7 @@ func (comp *Compiler) resolveCatalogRefs(qc *QueryCatalog, rvs []*ast.RangeVar,
569572
NotNull: c.IsNotNull,
570573
Unsigned: c.IsUnsigned,
571574
IsArray: c.IsArray,
575+
ArrayDims: c.ArrayDims,
572576
Table: table,
573577
IsNamedParam: isNamed,
574578
IsSqlcSlice: p.IsSqlcSlice(),

internal/compiler/to_column.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ func isArray(n *ast.TypeName) bool {
1414
return len(n.ArrayBounds.Items) > 0
1515
}
1616

17+
func arrayDims(n *ast.TypeName) int {
18+
if n == nil || n.ArrayBounds == nil {
19+
return 0
20+
}
21+
return len(n.ArrayBounds.Items)
22+
}
23+
1724
func toColumn(n *ast.TypeName) *Column {
1825
if n == nil {
1926
panic("can't build column for nil type name")
@@ -23,9 +30,10 @@ func toColumn(n *ast.TypeName) *Column {
2330
panic("toColumn: " + err.Error())
2431
}
2532
return &Column{
26-
Type: typ,
27-
DataType: strings.TrimPrefix(astutils.Join(n.Names, "."), "."),
28-
NotNull: true, // XXX: How do we know if this should be null?
29-
IsArray: isArray(n),
33+
Type: typ,
34+
DataType: strings.TrimPrefix(astutils.Join(n.Names, "."), "."),
35+
NotNull: true, // XXX: How do we know if this should be null?
36+
IsArray: isArray(n),
37+
ArrayDims: arrayDims(n),
3038
}
3139
}

0 commit comments

Comments
 (0)