github.com/ronaksoft/rony@v0.16.26-0.20230807065236-1743dbfe6959/cmd/protoc-gen-gorony/repo/cql/gen.go (about) 1 package cql 2 3 import ( 4 "fmt" 5 "strings" 6 "text/template" 7 8 "github.com/jinzhu/inflection" 9 "github.com/ronaksoft/rony/internal/codegen" 10 "github.com/ronaksoft/rony/tools" 11 "google.golang.org/protobuf/compiler/protogen" 12 ) 13 14 /* 15 Creation Time: 2021 - Jul - 13 16 Created by: (ehsan) 17 Maintainers: 18 1. Ehsan N. Moosa (E2) 19 Auditor: Ehsan N. Moosa (E2) 20 Copyright Ronak Software Group 2020 21 */ 22 23 type Generator struct { 24 f *protogen.File 25 g *protogen.GeneratedFile 26 repoPrefix string 27 } 28 29 func New(f *protogen.File, g *protogen.GeneratedFile, repoPrefix string) *Generator { 30 return &Generator{ 31 f: f, 32 g: g, 33 repoPrefix: repoPrefix, 34 } 35 } 36 37 func GenerateGo(g *Generator, arg codegen.MessageArg) { 38 if !arg.IsAggregate { 39 return 40 } 41 helperFunctions["RepoName"] = func(name string) string { 42 return fmt.Sprintf("%s%sRepo", name, tools.ToCamel(g.repoPrefix)) 43 } 44 45 g.g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "github.com/ronaksoft/rony/pools"}) 46 g.g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "github.com/ronaksoft/rony/pools/querypool"}) 47 g.g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "github.com/scylladb/gocqlx"}) 48 g.g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "github.com/scylladb/gocqlx/v2/table"}) 49 50 g.g.P(codegen.ExecTemplate( 51 template.Must(template.New("genPartKey").Funcs(helperFunctions).Parse(genPartKey)), arg), 52 ) 53 g.g.P(codegen.ExecTemplate( 54 template.Must(template.New("genRepo").Funcs(helperFunctions).Parse(genRepo)), arg), 55 ) 56 g.g.P(codegen.ExecTemplate( 57 template.Must(template.New("genCRUD").Funcs(helperFunctions).Parse(genCRUD)), arg), 58 ) 59 g.g.P(codegen.ExecTemplate( 60 template.Must(template.New("genListByPK").Funcs(helperFunctions).Parse(genListByPK)), arg), 61 ) 62 } 63 64 func GenerateCQL(g *Generator, arg codegen.MessageArg) { 65 if arg.IsAggregate { 66 g.g.P(codegen.ExecTemplate( 67 template.Must(template.New("genCQL").Funcs(helperFunctions).Parse(genCQL)), arg), 68 ) 69 } 70 } 71 72 var helperFunctions = template.FuncMap{ 73 "Singular": func(x string) string { 74 return inflection.Singular(x) 75 }, 76 "Plural": func(x string) string { 77 return inflection.Plural(x) 78 }, 79 "MVNameSC": func(m codegen.ModelKey) string { 80 alias := m.Alias() 81 if alias == "" { 82 alias = m.Names(codegen.PropFilterALL, "", "", "", codegen.None) 83 } 84 sb := strings.Builder{} 85 sb.WriteString(tools.ToSnake(m.Name())) 86 sb.WriteString("_") 87 sb.WriteString(tools.ToSnake(alias)) 88 89 return sb.String() 90 }, 91 "MVName": func(m codegen.ModelKey) string { 92 alias := m.Alias() 93 if alias == "" { 94 alias = m.Names(codegen.PropFilterALL, "", "", "", codegen.None) 95 } 96 sb := strings.Builder{} 97 sb.WriteString(m.Name()) 98 sb.WriteString(alias) 99 100 return sb.String() 101 }, 102 "MVAlias": func(m codegen.ModelKey, prefix string) string { 103 if m.Alias() != "" { 104 return m.Alias() 105 } 106 sb := strings.Builder{} 107 sb.WriteString(prefix) 108 sb.WriteString(m.Names(codegen.PropFilterALL, "", "", "", codegen.None)) 109 110 return sb.String() 111 }, 112 "Columns": func(m codegen.ModelKey) string { 113 sb := strings.Builder{} 114 sb.WriteString(m.Names(codegen.PropFilterALL, "\"", "\"", ", ", codegen.SnakeCase)) 115 sb.WriteString(", \"sdata\"") 116 117 return sb.String() 118 }, 119 "ColumnsValue": func(m codegen.ModelKey, prefix, postfix string) string { 120 textCase := codegen.LowerCamelCase 121 if prefix != "" { 122 textCase = codegen.None 123 } 124 sb := strings.Builder{} 125 sb.WriteString(m.Names(codegen.PropFilterALL, prefix, postfix, ", ", textCase)) 126 127 return sb.String() 128 }, 129 "ColumnsValuePKs": func(m codegen.ModelKey, prefix, postfix string) string { 130 textCase := codegen.LowerCamelCase 131 if prefix != "" { 132 textCase = codegen.None 133 } 134 sb := strings.Builder{} 135 sb.WriteString(m.Names(codegen.PropFilterPKs, prefix, postfix, ", ", textCase)) 136 137 return sb.String() 138 }, 139 "Where": func(m codegen.ModelKey) string { 140 sb := strings.Builder{} 141 sb.WriteString(m.Names(codegen.PropFilterALL, "qb.Eq(\"", "\")", ", ", codegen.SnakeCase)) 142 143 return sb.String() 144 }, 145 "PartKeys": func(m codegen.ModelKey) string { 146 return m.Names(codegen.PropFilterPKs, "\"", "\"", ", ", codegen.SnakeCase) 147 }, 148 "SortKeys": func(m codegen.ModelKey) string { 149 return m.Names(codegen.PropFilterCKs, "\"", "\"", ", ", codegen.SnakeCase) 150 }, 151 "FuncArgs": func(m codegen.ModelKey, prefix string) string { 152 textCase := codegen.LowerCamelCase 153 if prefix != "" { 154 textCase = codegen.None 155 } 156 157 return m.NameTypes(codegen.PropFilterALL, prefix, textCase, codegen.LangGo) 158 }, 159 "PrimaryKey": func(m codegen.ModelKey) string { 160 sb := strings.Builder{} 161 sb.WriteString("((") 162 sb.WriteString(m.Names(codegen.PropFilterPKs, "", "", ", ", codegen.SnakeCase)) 163 sb.WriteString(")") 164 if len(m.CKs()) > 0 { 165 sb.WriteString(", ") 166 sb.WriteString(m.Names(codegen.PropFilterCKs, "", "", ", ", codegen.SnakeCase)) 167 } 168 sb.WriteString(")") 169 170 return sb.String() 171 }, 172 "WithClusteringKey": func(m codegen.ModelKey) string { 173 if len(m.CKs()) == 0 { 174 return "" 175 } 176 sb := strings.Builder{} 177 sb.WriteString(" WITH CLUSTERING ORDER BY (") 178 for idx, k := range m.CKs() { 179 if idx > 0 { 180 sb.WriteString(", ") 181 } 182 sb.WriteString(tools.ToSnake(k.Name)) 183 sb.WriteRune(' ') 184 if k.Order == codegen.ASC { 185 sb.WriteString("ASC") 186 } else { 187 sb.WriteString("DESC") 188 } 189 } 190 sb.WriteString(")") 191 192 return sb.String() 193 }, 194 "MVWhere": func(m codegen.ModelKey) string { 195 sb := strings.Builder{} 196 for idx, k := range m.Keys() { 197 if idx == 0 { 198 sb.WriteString("WHERE ") 199 } else { 200 sb.WriteString("\r\n") 201 sb.WriteString("AND ") 202 } 203 sb.WriteString(tools.ToSnake(k.Name)) 204 sb.WriteString(" IS NOT null") 205 } 206 207 return sb.String() 208 }, 209 } 210 211 const genCQL = ` 212 {{$model := .}} 213 CREATE TABLE IF NOT EXISTS tab_{{Singular .NameSC}} 214 ( 215 {{- range .Table.Keys }} 216 {{.NameSC}} {{.CqlType}}, 217 {{- end }} 218 sdata blob, 219 PRIMARY KEY {{PrimaryKey .Table}} 220 ){{WithClusteringKey .Table}}; 221 {{ range .Views }} 222 CREATE MATERIALIZED VIEW IF NOT EXISTS view_{{MVNameSC .}} AS 223 SELECT * 224 FROM tab_{{Singular $model.NameSC}} 225 {{MVWhere $model.Table}} 226 PRIMARY KEY {{PrimaryKey .}} 227 {{- WithClusteringKey . -}}; 228 {{ end }} 229 ` 230 231 const genRepo = ` 232 {{$repoName := RepoName .Name}} 233 type {{$repoName}} struct { 234 qp map[string]*querypool.QueryPool 235 t *table.Table 236 v map[string]*table.Table 237 s gocqlx.Session 238 } 239 240 func New{{$repoName}}(s gocqlx.Session) *{{$repoName}} { 241 r := &{{$repoName}}{ 242 s: s, 243 t: table.New(table.Metadata{ 244 Name: "tab_{{Singular .NameSC}}", 245 Columns: []string{ {{- Columns .Table -}} }, 246 PartKey: []string{ {{- PartKeys .Table -}} }, 247 SortKey: []string{ {{- SortKeys .Table -}} }, 248 }), 249 v: map[string]*table.Table{ 250 {{- range .Views }} 251 "{{MVAlias . ""}}": table.New(table.Metadata{ 252 Name: "view_{{MVNameSC .}}", 253 Columns: []string{ {{- Columns . -}} }, 254 PartKey: []string{ {{- PartKeys . -}} }, 255 SortKey: []string{ {{- SortKeys . -}} }, 256 }), 257 {{- end }} 258 }, 259 } 260 261 r.qp = map[string]*querypool.QueryPool{ 262 "insertIF": querypool.New(func() *gocqlx.Queryx { 263 return r.t.InsertBuilder().Unique().Query(s) 264 }), 265 "insert": querypool.New(func() *gocqlx.Queryx { 266 return r.t.InsertBuilder().Query(s) 267 }), 268 "update": querypool.New(func() *gocqlx.Queryx { 269 return r.t.UpdateBuilder().Set("sdata").Query(s) 270 }), 271 "delete": querypool.New(func() *gocqlx.Queryx { 272 return r.t.DeleteBuilder().Query(s) 273 }), 274 "get": querypool.New(func() *gocqlx.Queryx { 275 return r.t.GetQuery(s) 276 }), 277 {{- range .Views }} 278 "getBy{{MVAlias . ""}}": querypool.New(func() *gocqlx.Queryx { 279 return r.v["{{MVAlias . ""}}"].GetQuery(s) 280 }), 281 {{- end }} 282 283 } 284 return r 285 } 286 287 func (r *{{$repoName}}) Table() *table.Table { 288 return r.t 289 } 290 291 func (r *{{$repoName}}) T() *table.Table { 292 return r.t 293 } 294 295 {{ range .Views }} 296 func (r *{{$repoName}}) {{MVAlias . "MV"}}() *table.Table { 297 return r.v["{{MVAlias . ""}}"] 298 } 299 {{ end }} 300 ` 301 302 const genPartKey = ` 303 {{$modelName := .Name}} 304 type {{$modelName}}PartitionKey interface { 305 make{{$modelName}}Private() 306 } 307 308 type {{$modelName}}PartKey struct { 309 {{- range .Table.PKs }} 310 {{.Name}} {{.GoType}} 311 {{- end }} 312 } 313 314 func ({{$modelName}}PartKey) make{{$modelName}}Private() {} 315 316 {{- range .Views }} 317 318 type {{MVName .}}PartKey struct { 319 {{- range .PKs }} 320 {{.Name}} {{.GoType}} 321 {{- end }} 322 } 323 324 func ({{MVName .}}PartKey) make{{$modelName}}Private() {} 325 {{ end }} 326 ` 327 328 const genCRUD = ` 329 {{$repoName := RepoName .Name}} 330 {{$modelName := .Name}} 331 func (r *{{$repoName}}) Insert(m *{{$modelName}}, replace bool) error { 332 buf := pools.Buffer.FromProto(m) 333 defer pools.Buffer.Put(buf) 334 335 var q *gocqlx.Queryx 336 if replace { 337 q = r.qp["insertIF"].GetQuery() 338 defer r.qp["insertIF"].Put(q) 339 } else { 340 q = r.qp["insert"].GetQuery() 341 defer r.qp["insert"].Put(q) 342 } 343 344 345 q.Bind({{ColumnsValue .Table "m." ""}}, *buf.Bytes()) 346 return q.Exec() 347 } 348 349 func (r *{{$repoName}}) Update(m *{{$modelName}}) error { 350 buf := pools.Buffer.FromProto(m) 351 defer pools.Buffer.Put(buf) 352 353 q := r.qp["update"].GetQuery() 354 defer r.qp["update"].Put(q) 355 356 357 q.Bind(*buf.Bytes(), {{ColumnsValue .Table "m." ""}}) 358 return q.Exec() 359 } 360 361 func (r *{{$repoName}}) Delete({{FuncArgs .Table ""}}) error { 362 q := r.qp["delete"].GetQuery() 363 defer r.qp["delete"].Put(q) 364 365 366 q.Bind({{ColumnsValue .Table "" ""}}) 367 return q.Exec() 368 } 369 370 func (r *{{$repoName}}) Get({{FuncArgs .Table ""}}, m *{{$modelName}}) (*{{$modelName}}, error) { 371 q := r.qp["get"].GetQuery() 372 defer r.qp["get"].Put(q) 373 374 if m == nil { 375 m = &{{$modelName}}{} 376 } 377 378 q.Bind({{ColumnsValue .Table "" ""}}) 379 380 var b []byte 381 err := q.Scan({{ColumnsValue .Table "&m." ""}}, &b) 382 if err != nil { 383 return m, err 384 } 385 err = m.Unmarshal(b) 386 return m, err 387 } 388 {{ range .Views }} 389 func (r *{{$repoName}}) GetBy{{MVAlias . ""}} ({{FuncArgs . ""}}, m *{{$modelName}}) (*{{$modelName}}, error) { 390 q := r.qp["getBy{{MVAlias . ""}}"].GetQuery() 391 defer r.qp["getBy{{MVAlias . ""}}"].Put(q) 392 393 if m == nil { 394 m = &{{$modelName}}{} 395 } 396 397 q.Bind({{ColumnsValue . "" ""}}) 398 399 var b []byte 400 err := q.Scan({{ColumnsValue . "&m." ""}}, &b) 401 if err != nil { 402 return m, err 403 } 404 err = m.Unmarshal(b) 405 return m, err 406 } 407 408 {{ end }} 409 ` 410 411 const genListByPK = ` 412 {{$repoName := RepoName .Name}} 413 {{$modelName := .Name}} 414 func (r *{{$repoName}}) List(pk {{$modelName}}PartitionKey, limit uint) ([]*{{$modelName}}, error) { 415 var ( 416 q *gocqlx.Queryx 417 res []*{{$modelName}} 418 err error 419 ) 420 421 switch pk := pk.(type) { 422 case {{$modelName}}PartKey: 423 q = r.t.SelectBuilder("sdata").Limit(limit).Query(r.s) 424 q.Bind({{ColumnsValuePKs .Table "pk." ""}}) 425 {{ range .Views }} 426 case {{MVName .}}PartKey: 427 q = r.v["{{MVAlias . ""}}"].SelectBuilder("sdata").Limit(limit).Query(r.s) 428 q.Bind({{ColumnsValuePKs . "pk." ""}}) 429 {{ end }} 430 default: 431 panic("BUG!! incorrect mount key") 432 } 433 434 buf := pools.Buffer.GetCap(1024) 435 defer pools.Buffer.Put(buf) 436 iter := q.Iter() 437 for iter.Scan(buf.Bytes()) { 438 m := &{{$modelName}}{} 439 err = m.Unmarshal(*buf.Bytes()) 440 res = append(res, m) 441 buf.Reset() 442 } 443 err = iter.Close() 444 445 return res, err 446 } 447 `