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  `