Skip to content

Commit dba2f95

Browse files
add on_conflict query arg in insert_one
1 parent d5ff4a6 commit dba2f95

File tree

7 files changed

+127
-22
lines changed

7 files changed

+127
-22
lines changed

cmd/eywagen/eywatest/eywa_generated.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func testTable_AgeVar(val *int) eywa.Field[testTable] {
3838
Value: eywa.QueryVar("testTable_Age", eywa.NullableIntVar[*int](val)),
3939
}
4040
}
41+
const testTable_PkeyConstraint eywa.Constraint[testTable] = "testTable_pkey"
4142
const testTable_ID eywa.FieldName[testTable] = "id"
4243

4344
func testTable_IDField(val int) eywa.Field[testTable] {
@@ -53,19 +54,19 @@ func testTable_IDVar(val int) eywa.Field[testTable] {
5354
Value: eywa.QueryVar("testTable_ID", eywa.IntVar[int](val)),
5455
}
5556
}
56-
const testTable_iD eywa.FieldName[testTable] = "idd"
57+
const testTable_IDd eywa.FieldName[testTable] = "idd"
5758

58-
func testTable_iDField(val int32) eywa.Field[testTable] {
59+
func testTable_IDdField(val int32) eywa.Field[testTable] {
5960
return eywa.Field[testTable]{
6061
Name: "idd",
6162
Value: val,
6263
}
6364
}
6465

65-
func testTable_iDVar(val int32) eywa.Field[testTable] {
66+
func testTable_IDdVar(val int32) eywa.Field[testTable] {
6667
return eywa.Field[testTable]{
6768
Name: "idd",
68-
Value: eywa.QueryVar("testTable_iD", eywa.IntVar[int32](val)),
69+
Value: eywa.QueryVar("testTable_IDd", eywa.IntVar[int32](val)),
6970
}
7071
}
7172
const testTable_custom eywa.FieldName[testTable] = "custom"
@@ -155,6 +156,7 @@ func testTable_FVar(val X[string, int]) eywa.Field[testTable] {
155156
}
156157
}
157158

159+
const testTable2_PkeyConstraint eywa.Constraint[testTable2] = "testTable2_pkey"
158160
const testTable2_ID eywa.FieldName[testTable2] = "id"
159161

160162
func testTable2_IDField(val uuid.UUID) eywa.Field[testTable2] {
@@ -163,3 +165,18 @@ func testTable2_IDField(val uuid.UUID) eywa.Field[testTable2] {
163165
Value: val,
164166
}
165167
}
168+
const testTable2_Age eywa.FieldName[testTable2] = "age"
169+
170+
func testTable2_AgeField(val int) eywa.Field[testTable2] {
171+
return eywa.Field[testTable2]{
172+
Name: "age",
173+
Value: val,
174+
}
175+
}
176+
177+
func testTable2_AgeVar(val int) eywa.Field[testTable2] {
178+
return eywa.Field[testTable2]{
179+
Name: "age",
180+
Value: eywa.QueryVar("testTable2_Age", eywa.IntVar[int](val)),
181+
}
182+
}

cmd/eywagen/eywatest/eywa_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,43 @@ id
154154
assert.Equal(t, &testTable2{ID: id}, resp)
155155
}
156156
}
157+
158+
func TestInsertOneQueryOnConflict(t *testing.T) {
159+
id := uuid.New()
160+
accessKey := os.Getenv("TEST_HGE_ACCESS_KEY")
161+
c := eywa.NewClient("https://aware-cowbird-80.hasura.app/v1/graphql", &eywa.ClientOpts{
162+
Headers: map[string]string{
163+
"x-hasura-access-key": accessKey,
164+
},
165+
})
166+
q := eywa.InsertOne(
167+
testTable2_IDField(id),
168+
testTable2_AgeField(10),
169+
).Select(
170+
testTable2_ID,
171+
)
172+
q.Exec(c)
173+
174+
q2 := eywa.InsertOne(
175+
testTable2_IDField(id),
176+
testTable2_AgeField(20),
177+
).OnConflict(
178+
testTable2_PkeyConstraint,
179+
testTable2_Age,
180+
).Select(
181+
testTable2_ID,
182+
testTable2_Age,
183+
)
184+
expected := fmt.Sprintf(`mutation insert_test_table2_one {
185+
insert_test_table2_one(object: {age: 20, id: "%s"}, on_conflict: {constraint: testTable2_pkey, update_columns: [age]}) {
186+
age
187+
id
188+
}
189+
}`, id.String())
190+
191+
if assert.Equal(t, expected, q2.Query()) {
192+
resp, err := q2.Exec(c)
193+
assert.NoError(t, err)
194+
assert.Equal(t, &testTable2{ID: id, Age: 20}, resp)
195+
}
196+
}

cmd/eywagen/eywatest/eywatest.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
type testTable struct {
1010
Name string `json:"name"`
1111
Age *int `json:"age"`
12-
ID int `json:"id,omitempty"`
12+
ID int `json:"id,omitempty",eywa:"pkey"`
1313
IDd int32 `json:"idd,omitempty"`
1414
custom *customType `json:"custom"`
1515
testTable2 *testTable2 `json:"test_table2"`
@@ -36,7 +36,8 @@ func (t testTable) ModelName() string {
3636
type customType struct{}
3737

3838
type testTable2 struct {
39-
ID uuid.UUID `json:"id"`
39+
ID uuid.UUID `json:"id",eywa:"pkey"`
40+
Age int `json:"age"`
4041
}
4142

4243
func (t testTable2) ModelName() string {

cmd/eywagen/main.go

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ func usage() {
2323
}
2424

2525
var tagPattern = re.MustCompile(`json:"([^"]+)"`)
26+
var eywaTagPattern = re.MustCompile(`eywa:"([^"]+)"`)
2627

2728
const (
2829
genHeader = "// generated by eywa. DO NOT EDIT. Any changes will be overwritten.\npackage "
@@ -66,6 +67,10 @@ func %s(subField eywa.FieldName[%s], subFields ...eywa.FieldName[%s]) eywa.Field
6667
`
6768
)
6869

70+
func pkeyConstraint(typeName string) string {
71+
return fmt.Sprintf("const %s_PkeyConstraint eywa.Constraint[%s] = \"%s_pkey\"\n", typeName, typeName, typeName)
72+
}
73+
6974
func main() {
7075
flag.Usage = usage
7176
flag.Parse()
@@ -91,7 +96,9 @@ func main() {
9196
content: bytes.NewBufferString(""),
9297
}
9398
for _, t := range types {
94-
parseType(t, pkg, contents)
99+
if err := parseType(t, pkg, contents); err != nil {
100+
panic(err)
101+
}
95102
}
96103
if len(contents.importsMap) > 0 {
97104
contents.imports.WriteString("\nimport (\n")
@@ -117,39 +124,55 @@ type fileContent struct {
117124

118125
var parsed = make(map[string]bool)
119126

120-
func parseType(typeName string, pkg *types.Package, contents *fileContent) {
127+
func parseType(typeName string, pkg *types.Package, contents *fileContent) error {
121128
if parsed[typeName] {
122-
return
129+
return nil
123130
}
124131
parsed[typeName] = true
125132

126133
typeObj := pkg.Scope().Lookup(typeName)
127134
if typeObj == nil {
128135
fmt.Printf("type %s not found in package, skipping...", typeName)
129-
return
136+
return nil
130137
}
131138
typeStruct, ok := typeObj.Type().Underlying().(*types.Struct)
132139
if !ok {
133140
fmt.Printf("type %s is not a struct, skipping...", typeName)
134-
return
141+
return nil
135142
}
136143
if types.NewMethodSet(types.NewPointer(typeObj.Type())).Lookup(pkg, "ModelName") == nil {
137144
fmt.Printf("struct type %s does not implement eywa.Model interface, skipping...", typeName)
138-
return
145+
return nil
139146
}
140147

141148
contents.content.WriteString("\n")
142149
recurseParse := make([]string, 0, typeStruct.NumFields())
150+
foundPkey := false
151+
pkey := ""
143152
for i := 0; i < typeStruct.NumFields(); i++ {
144153
tag := tagPattern.FindStringSubmatch(typeStruct.Tag(i))
145154
if tag == nil {
146155
continue
147156
}
148-
tagValue := strings.Split(tag[1], ",")
149-
if len(tagValue) == 0 {
157+
tagValues := strings.Split(tag[1], ",")
158+
if len(tagValues) == 0 {
150159
continue
151160
}
152-
fieldName := tagValue[0]
161+
fieldName := tagValues[0]
162+
if eywaTag := eywaTagPattern.FindStringSubmatch(typeStruct.Tag(i)); eywaTag != nil {
163+
if eywaTagValues := strings.Split(eywaTag[1], ","); len(eywaTagValues) != 0 {
164+
for _, v := range eywaTagValues {
165+
if v == "pkey" && foundPkey {
166+
return fmt.Errorf("model %s has two primary keys: %s, %s", typeName, pkey, fieldName)
167+
}
168+
if v == "pkey" {
169+
foundPkey = true
170+
contents.content.WriteString(pkeyConstraint(typeName))
171+
pkey = fieldName
172+
}
173+
}
174+
}
175+
}
153176
field := typeStruct.Field(i)
154177
fieldType := field.Type()
155178
importPackages, fieldTypeNameFull := parseFieldTypeName(field.Type().String(), pkg.Path())
@@ -278,8 +301,11 @@ func parseType(typeName string, pkg *types.Package, contents *fileContent) {
278301
}
279302
}
280303
for _, t := range recurseParse {
281-
parseType(t, pkg, contents)
304+
if err := parseType(t, pkg, contents); err != nil {
305+
return err
306+
}
282307
}
308+
return nil
283309
}
284310

285311
func writeToFile(filename string, contents *fileContent) error {

eywa.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,5 @@ type QuerySkeleton[M Model] struct {
104104
func (qs QuerySkeleton[M]) MarshalGQL() string {
105105
return fmt.Sprintf("%s%s", qs.ModelName, qs.queryArgs.MarshalGQL())
106106
}
107+
108+
type Constraint[M Model] string

insert_one.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ type InsertOneQueryBuilder[M Model] struct {
2323
QuerySkeleton[M]
2424
}
2525

26-
// func (iq InsertOneQueryBuilder[M]) OnConstraint(constraint eywa.Constraint[M], field FieldName[M], fields ...FieldName[M]) InsertOneQuery[M] {
27-
// return InsertOneQuery[M]{
28-
// iq: &iq,
29-
// fields: append(fields, field),
30-
// }
31-
// }
26+
func (iq InsertOneQueryBuilder[M]) OnConflict(constraint Constraint[M], fields ...FieldName[M]) InsertOneQueryBuilder[M] {
27+
iq.QuerySkeleton.queryArgs.onConflict = &onConflict[M]{
28+
constraint: constraint,
29+
updateColumns: fields,
30+
}
31+
return iq
32+
}
3233

3334
func (iq *InsertOneQueryBuilder[M]) MarshalGQL() string {
3435
return fmt.Sprintf(

query_arg.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type queryArgs[M Model] struct {
1414
orderBy *orderBy
1515
set *set[M]
1616
object *object[M]
17+
onConflict *onConflict[M]
1718
}
1819

1920
func (qa queryArgs[M]) MarshalGQL() string {
@@ -25,6 +26,7 @@ func (qa queryArgs[M]) MarshalGQL() string {
2526
args = appendArg(args, qa.orderBy)
2627
args = appendArg(args, qa.set)
2728
args = appendArg(args, qa.object)
29+
args = appendArg(args, qa.onConflict)
2830

2931
return fmt.Sprintf("(%s)", strings.Join(args, ", "))
3032
}
@@ -268,3 +270,19 @@ func (o object[M]) queryArgName() string {
268270
func (o object[M]) MarshalGQL() string {
269271
return fmt.Sprintf("%s: {%s}", o.queryArgName(), o.fields.MarshalGQL())
270272
}
273+
274+
type onConflict[M Model] struct {
275+
constraint Constraint[M]
276+
updateColumns FieldNameArray[M]
277+
}
278+
279+
func (oc onConflict[M]) queryArgName() string {
280+
return "on_conflict"
281+
}
282+
283+
func (oc onConflict[M]) MarshalGQL() string {
284+
if oc.updateColumns == nil {
285+
return fmt.Sprintf("%s: {constraint: %s}", oc.queryArgName(), string(oc.constraint))
286+
}
287+
return fmt.Sprintf("%s: {constraint: %s, update_columns: [%s]}", oc.queryArgName(), string(oc.constraint), oc.updateColumns.MarshalGQL())
288+
}

0 commit comments

Comments
 (0)