Skip to content

Commit 88fcd68

Browse files
committed
add StringArray type
1 parent a8f4598 commit 88fcd68

File tree

10 files changed

+351
-36
lines changed

10 files changed

+351
-36
lines changed

generator/client/golang/templates/[model].gotmpl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
type {{ $.model.Name }} struct {
1313
{{- range $c := .model.Columns }}
14-
{{ pascal $c.Name }} {{ goModelType $c.Type $c.Null }} `json:"{{ camel $c.Name }}" db:"{{ $c.Name }}"`
14+
{{ pascal $c.Name }} {{ goModelType $c.Type $c.Null $c.Array }} `json:"{{ camel $c.Name }}" db:"{{ $c.Name }}"`
1515
{{- end }}
1616
{{- range $b := $.model.BelongsTo}}
1717
{{ $b.Name | pascal }} *{{ $b.ModelName }} `json:"{{ camel $b.Name }}"`
@@ -58,8 +58,8 @@ func ({{ $m }} *{{.model.Name}}) applyChange(change *queryx.{{.model.Name}}Chang
5858
{{- range $c := $.model.Columns }}
5959
{{- $f := $c.Name | pascal }}
6060
if change.{{ $f }}.Set {
61-
{{- $t1 := goModelType $c.Type $c.Null }}
62-
{{- $t2 := printf "queryx.%s" (goType $c.Type) }}
61+
{{- $t1 := goModelType $c.Type $c.Null $c.Array }}
62+
{{- $t2 := printf "queryx.%s" (goType $c.Type $c.Null $c.Array) }}
6363
{{- if eq $t1 $t2 }}
6464
{{ $m }}.{{ pascal $c.Name}} = change.{{ $f }}
6565
{{- else }}

generator/client/golang/templates/[model]_query.gotmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212

1313
{{- define "primary_key_params" }}
1414
{{- range $i, $c := $.Columns -}}
15-
{{ if $i }}, {{ end }}{{ $c.Name | camel | goKeywordFix }} {{ goModelType $c.Type $c.Null }}
15+
{{ if $i }}, {{ end }}{{ $c.Name | camel | goKeywordFix }} {{ goModelType $c.Type $c.Null $c.Array }}
1616
{{- end -}}
1717
{{- end }}
1818

generator/client/golang/templates/queryx/[model]_change.gotmpl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package queryx
44

55
type {{ $.model.Name }}Change struct {
66
{{- range $c := .model.Columns }}
7-
{{ $c.Name | pascal }} {{ goType $c.Type }}
7+
{{ $c.Name | pascal }} {{ goType $c.Type $c.Null $c.Array }}
88
{{- end }}
99
}
1010

@@ -25,18 +25,18 @@ func (c *{{.model.Name}}Change) Changes() (columns []string, values []interface{
2525

2626
{{- range $c := $.model.Columns }}
2727
{{- $f := $c.Name | pascal }}
28-
{{- $t := $c.Type | goChangeSetType }}
28+
{{- $t := goChangeSetType $c.Type $c.Null $c.Array }}
2929
{{- $a := $c.Name | camel | goKeywordFix }}
3030

3131
func (c *{{ $.model.Name }}Change) Set{{ pascal $c.Name }}({{ $a }} {{ $t }}) *{{ $.model.Name }}Change {
32-
c.{{ $f }} = New{{ goType $c.Type }}({{ $a }})
32+
c.{{ $f }} = New{{ goType $c.Type $c.Null $c.Array }}({{ $a }})
3333
c.{{ $f }}.Set = true
3434
return c
3535
}
3636
{{- if $c.Null }}
3737

3838
func (c *{{ $.model.Name }}Change) SetNullable{{ pascal $c.Name }}({{ $a }} *{{ $t }}) *{{ $.model.Name }}Change {
39-
c.{{ $f }} = NewNullable{{ goType $c.Type }}({{ $a }})
39+
c.{{ $f }} = NewNullable{{ goType $c.Type $c.Null $c.Array }}({{ $a }})
4040
c.{{ $f }}.Set = true
4141
return c
4242
}

generator/client/golang/templates/queryx/schema.gotmpl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ type Schema struct {
66
{{- range $m := $.client.Models }}
77
{{ $m.Name }} *Table
88
{{- range .Columns }}
9-
{{ $m.Name }}{{ firstWordUpperCamel .Name }} *{{goType .Type}}Column
9+
{{ $m.Name }}{{ firstWordUpperCamel .Name }} *{{goType .Type .Null .Array}}Column
1010
{{- end}}
1111
{{- end}}
1212
}
@@ -20,7 +20,7 @@ func NewSchema() *Schema {
2020
{{- range $m := $.client.Models }}
2121
{{ $m.Name }}: {{ camel $m.Name }},
2222
{{- range $c := $m.Columns }}
23-
{{ $m.Name }}{{ firstWordUpperCamel $c.Name }}: {{ camel $m.Name }}.New{{ goType $c.Type }}Column("{{ $c.Name }}"),
23+
{{ $m.Name }}{{ firstWordUpperCamel $c.Name }}: {{ camel $m.Name }}.New{{ goType $c.Type $c.Null $c.Array }}Column("{{ $c.Name }}"),
2424
{{- end }}
2525
{{- end }}
2626
}
@@ -40,7 +40,7 @@ func (s *Schema) Change{{ $m.Name }}() *{{ $m.Name }}Change {
4040
return &{{ $m.Name }}Change{
4141
{{- range $c := $m.Columns }}
4242
{{- $f := $c.Name | pascal }}
43-
{{ $f }}: {{ goType $c.Type }}{},
43+
{{ $f }}: {{ goType $c.Type $c.Null $c.Array }}{},
4444
{{- end }}
4545
}
4646
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// Code generated by queryx, DO NOT EDIT.
2+
3+
package queryx
4+
5+
import (
6+
"database/sql/driver"
7+
"encoding/json"
8+
"fmt"
9+
"strings"
10+
)
11+
12+
type StringArray struct {
13+
Val []string
14+
Valid bool
15+
Set bool
16+
}
17+
18+
func NewStringArray(v []string) StringArray {
19+
return StringArray{Val: v, Valid: true, Set: true}
20+
}
21+
22+
func NewNullableStringArray(v *[]string) StringArray {
23+
if v != nil {
24+
return NewStringArray(*v)
25+
}
26+
return StringArray{Set: true}
27+
}
28+
29+
// Scan implements the Scanner interface.
30+
func (s *StringArray) Scan(value interface{}) error {
31+
if value == nil {
32+
s.Val, s.Valid = nil, false
33+
return nil
34+
}
35+
36+
switch v := value.(type) {
37+
case []byte:
38+
s.Valid = true
39+
return s.parseArray(string(v))
40+
case string:
41+
s.Valid = true
42+
return s.parseArray(v)
43+
case []string:
44+
s.Val, s.Valid = v, true
45+
return nil
46+
default:
47+
return fmt.Errorf("unsupported Scan type for StringArray: %T", value)
48+
}
49+
}
50+
51+
// parseArray parses a PostgreSQL array string into a Go string slice
52+
func (s *StringArray) parseArray(str string) error {
53+
if str == "{}" {
54+
s.Val = []string{}
55+
return nil
56+
}
57+
58+
// Simple parsing for basic arrays
59+
// This is a simplified implementation and might not handle all edge cases
60+
str = strings.TrimPrefix(str, "{")
61+
str = strings.TrimSuffix(str, "}")
62+
63+
// Split by comma, but respect quotes
64+
parts := []string{}
65+
inQuote := false
66+
current := ""
67+
68+
for _, r := range str {
69+
switch r {
70+
case '"':
71+
inQuote = !inQuote
72+
current += string(r)
73+
case ',':
74+
if inQuote {
75+
current += string(r)
76+
} else {
77+
parts = append(parts, current)
78+
current = ""
79+
}
80+
default:
81+
current += string(r)
82+
}
83+
}
84+
85+
if current != "" {
86+
parts = append(parts, current)
87+
}
88+
89+
// Strip quotes and unescape
90+
result := make([]string, len(parts))
91+
for i, p := range parts {
92+
p = strings.TrimSpace(p)
93+
if strings.HasPrefix(p, "\"") && strings.HasSuffix(p, "\"") {
94+
// Unquote the string
95+
p = p[1 : len(p)-1]
96+
// Unescape escaped quotes
97+
p = strings.ReplaceAll(p, "\"\"", "\"")
98+
}
99+
result[i] = p
100+
}
101+
102+
s.Val = result
103+
return nil
104+
}
105+
106+
// Value implements the driver Valuer interface.
107+
func (s StringArray) Value() (driver.Value, error) {
108+
if !s.Valid {
109+
return nil, nil
110+
}
111+
112+
if s.Val == nil {
113+
return "{}", nil
114+
}
115+
116+
// Format slice as PostgreSQL array
117+
escaped := make([]string, len(s.Val))
118+
for i, v := range s.Val {
119+
// Escape special characters and quote
120+
v = strings.ReplaceAll(v, "\"", "\"\"")
121+
escaped[i] = fmt.Sprintf("\"%s\"", v)
122+
}
123+
124+
return fmt.Sprintf("{%s}", strings.Join(escaped, ",")), nil
125+
}
126+
127+
// MarshalJSON implements the json.Marshaler interface.
128+
func (s StringArray) MarshalJSON() ([]byte, error) {
129+
if !s.Valid {
130+
return json.Marshal(nil)
131+
}
132+
return json.Marshal(s.Val)
133+
}
134+
135+
// UnmarshalJSON implements the json.Unmarshaler interface.
136+
func (s *StringArray) UnmarshalJSON(data []byte) error {
137+
s.Set = true
138+
if string(data) == "null" {
139+
return nil
140+
}
141+
s.Valid = true
142+
if err := json.Unmarshal(data, &s.Val); err != nil {
143+
return err
144+
}
145+
return nil
146+
}
147+
148+
// String implements the stringer interface.
149+
func (s StringArray) String() string {
150+
if !s.Valid {
151+
return "null"
152+
}
153+
bytes, _ := json.Marshal(s.Val)
154+
return string(bytes)
155+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Code generated by queryx, DO NOT EDIT.
2+
3+
package queryx
4+
5+
type StringArrayColumn struct {
6+
Name string
7+
Table *Table
8+
}
9+
10+
func (t *Table) NewStringArrayColumn(name string) *StringArrayColumn {
11+
return &StringArrayColumn{
12+
Table: t,
13+
Name: name,
14+
}
15+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Code generated by queryx, DO NOT EDIT.
2+
3+
package queryx
4+
5+
import (
6+
"encoding/json"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestNewStringArray(t *testing.T) {
13+
// Test with a non-empty slice
14+
s1 := NewStringArray([]string{"a", "b", "c"})
15+
require.Equal(t, []string{"a", "b", "c"}, s1.Val)
16+
require.True(t, s1.Valid)
17+
require.True(t, s1.Set)
18+
19+
// Test with an empty slice
20+
s2 := NewStringArray([]string{})
21+
require.Equal(t, []string{}, s2.Val)
22+
require.True(t, s2.Valid)
23+
require.True(t, s2.Set)
24+
25+
// Test with nullable, non-nil value
26+
v := []string{"x", "y"}
27+
s3 := NewNullableStringArray(&v)
28+
require.Equal(t, []string{"x", "y"}, s3.Val)
29+
require.True(t, s3.Valid)
30+
require.True(t, s3.Set)
31+
32+
// Test with nullable, nil value
33+
s4 := NewNullableStringArray(nil)
34+
require.False(t, s4.Valid)
35+
require.True(t, s4.Set)
36+
}
37+
38+
func TestStringArrayJSON(t *testing.T) {
39+
type Foo struct {
40+
X StringArray `json:"x"`
41+
Y StringArray `json:"y"`
42+
}
43+
44+
x := NewStringArray([]string{"a", "b", "c"})
45+
y := NewNullableStringArray(nil)
46+
s := `{"x":["a","b","c"],"y":null}`
47+
48+
f1 := Foo{X: x, Y: y}
49+
b, err := json.Marshal(f1)
50+
require.NoError(t, err)
51+
require.Equal(t, s, string(b))
52+
53+
var f2 Foo
54+
err = json.Unmarshal([]byte(s), &f2)
55+
require.NoError(t, err)
56+
require.Equal(t, x, f2.X)
57+
require.Equal(t, y, f2.Y)
58+
}
59+
60+
func TestStringArrayScan(t *testing.T) {
61+
// Test scanning from nil
62+
var s1 StringArray
63+
err := s1.Scan(nil)
64+
require.NoError(t, err)
65+
require.False(t, s1.Valid)
66+
require.Nil(t, s1.Val)
67+
68+
// Test scanning from PostgreSQL array string format
69+
var s2 StringArray
70+
err = s2.Scan(`{"a","b","c"}`)
71+
require.NoError(t, err)
72+
require.True(t, s2.Valid)
73+
require.Equal(t, []string{"a", "b", "c"}, s2.Val)
74+
75+
// Test scanning from byte slice
76+
var s3 StringArray
77+
err = s3.Scan([]byte(`{"x","y"}`))
78+
require.NoError(t, err)
79+
require.True(t, s3.Valid)
80+
require.Equal(t, []string{"x", "y"}, s3.Val)
81+
82+
// Test scanning from string slice
83+
var s4 StringArray
84+
err = s4.Scan([]string{"p", "q"})
85+
require.NoError(t, err)
86+
require.True(t, s4.Valid)
87+
require.Equal(t, []string{"p", "q"}, s4.Val)
88+
89+
// Test scanning from empty array
90+
var s5 StringArray
91+
err = s5.Scan(`{}`)
92+
require.NoError(t, err)
93+
require.True(t, s5.Valid)
94+
require.Equal(t, []string{}, s5.Val)
95+
96+
// Test scanning from unsupported type
97+
var s6 StringArray
98+
err = s6.Scan(123)
99+
require.Error(t, err)
100+
}
101+
102+
func TestStringArrayValue(t *testing.T) {
103+
// Test with valid array
104+
s1 := NewStringArray([]string{"a", "b", "c"})
105+
v1, err := s1.Value()
106+
require.NoError(t, err)
107+
require.Equal(t, `{"a","b","c"}`, v1)
108+
109+
// Test with invalid array
110+
s2 := StringArray{Valid: false}
111+
v2, err := s2.Value()
112+
require.NoError(t, err)
113+
require.Nil(t, v2)
114+
115+
// Test with nil slice but valid flag
116+
s3 := StringArray{Valid: true, Val: nil}
117+
v3, err := s3.Value()
118+
require.NoError(t, err)
119+
require.Equal(t, "{}", v3)
120+
121+
// Test with values that need escaping
122+
s4 := NewStringArray([]string{`a"quote`})
123+
v4, err := s4.Value()
124+
require.NoError(t, err)
125+
require.Equal(t, `{"a""quote"}`, v4)
126+
}
127+
128+
func TestStringArrayString(t *testing.T) {
129+
// Test with valid array
130+
s1 := NewStringArray([]string{"a", "b", "c"})
131+
require.Equal(t, `["a","b","c"]`, s1.String())
132+
133+
// Test with invalid array
134+
s2 := StringArray{Valid: false}
135+
require.Equal(t, "null", s2.String())
136+
}

0 commit comments

Comments
 (0)