From d785536c7e0161d0e1f8b51fbb9b725a23ea4c52 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 23 Aug 2023 00:44:51 +0200 Subject: [PATCH 01/18] feat: using emit_module, emit_generators and emit_async flags you can now generate query code that suites your need --- .gitignore | 1 + internal/config.go | 7 +- internal/gen.go | 385 ++++++++++++++++-------------------- internal/imports.go | 15 +- internal/poet/poet.go | 7 + internal/printer/printer.go | 59 ++++-- 6 files changed, 242 insertions(+), 232 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..917660a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.wasm \ No newline at end of file diff --git a/internal/config.go b/internal/config.go index 009cb04..4f44501 100644 --- a/internal/config.go +++ b/internal/config.go @@ -1,9 +1,12 @@ package python type Config struct { + EmitModule bool `json:"emit_module"` // If true emits functions in module, else wraps in a class. + EmitGenerators bool `json:"emit_generators"` // Will we use generators or lists, defaults to true + EmitAsync bool `json:"emit_async"` // Emits async code instead of sync EmitExactTableNames bool `json:"emit_exact_table_names"` - EmitSyncQuerier bool `json:"emit_sync_querier"` - EmitAsyncQuerier bool `json:"emit_async_querier"` + EmitSyncQuerier bool `json:"emit_sync_querier"` // DEPRECATED ALIAS FOR: emit_type = 'class', emit_generators = True + EmitAsyncQuerier bool `json:"emit_async_querier"` // DEPRECATED ALIAS FOR: emit_type = 'class', emit_generators = True Package string `json:"package"` Out string `json:"out"` EmitPydanticModels bool `json:"emit_pydantic_models"` diff --git a/internal/gen.go b/internal/gen.go index ebe34b0..74f23fe 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -638,7 +638,7 @@ func typeRefNode(base string, parts ...string) *pyast.Node { return n } -func connMethodNode(method, name string, arg *pyast.Node) *pyast.Node { +func connMethodNode(method *pyast.Node, name string, arg *pyast.Node) *pyast.Node { args := []*pyast.Node{ { Node: &pyast.Node_Call{ @@ -657,7 +657,7 @@ func connMethodNode(method, name string, arg *pyast.Node) *pyast.Node { return &pyast.Node{ Node: &pyast.Node_Call{ Call: &pyast.Call{ - Func: typeRefNode("self", "_conn", method), + Func: method, Args: args, }, }, @@ -753,47 +753,9 @@ func buildModelsTree(ctx *pyTmplCtx, i *importer) *pyast.Node { return &pyast.Node{Node: &pyast.Node_Module{Module: mod}} } -func querierClassDef() *pyast.ClassDef { +func querierClassDef(name string, connectionAnnotation *pyast.Node) *pyast.ClassDef { return &pyast.ClassDef{ - Name: "Querier", - Body: []*pyast.Node{ - { - Node: &pyast.Node_FunctionDef{ - FunctionDef: &pyast.FunctionDef{ - Name: "__init__", - Args: &pyast.Arguments{ - Args: []*pyast.Arg{ - { - Arg: "self", - }, - { - Arg: "conn", - Annotation: typeRefNode("sqlalchemy", "engine", "Connection"), - }, - }, - }, - Body: []*pyast.Node{ - { - Node: &pyast.Node_Assign{ - Assign: &pyast.Assign{ - Targets: []*pyast.Node{ - poet.Attribute(poet.Name("self"), "_conn"), - }, - Value: poet.Name("conn"), - }, - }, - }, - }, - }, - }, - }, - }, - } -} - -func asyncQuerierClassDef() *pyast.ClassDef { - return &pyast.ClassDef{ - Name: "AsyncQuerier", + Name: name, Body: []*pyast.Node{ { Node: &pyast.Node_FunctionDef{ @@ -805,8 +767,8 @@ func asyncQuerierClassDef() *pyast.ClassDef { Arg: "self", }, { - Arg: "conn", - Annotation: typeRefNode("sqlalchemy", "ext", "asyncio", "AsyncConnection"), + Arg: "connection", + Annotation: connectionAnnotation, }, }, }, @@ -815,9 +777,9 @@ func asyncQuerierClassDef() *pyast.ClassDef { Node: &pyast.Node_Assign{ Assign: &pyast.Assign{ Targets: []*pyast.Node{ - poet.Attribute(poet.Name("self"), "_conn"), + poet.Attribute(poet.Name("self"), "_connection"), }, - Value: poet.Name("conn"), + Value: poet.Name("connection"), }, }, }, @@ -886,188 +848,181 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { } } - if ctx.C.EmitSyncQuerier { - cls := querierClassDef() - for _, q := range ctx.Queries { - if !ctx.OutputQuery(q.SourceName) { - continue - } - f := &pyast.FunctionDef{ - Name: q.MethodName, - Args: &pyast.Arguments{ - Args: []*pyast.Arg{ - { - Arg: "self", - }, - }, - }, - } + functions := make([]*pyast.Node, 0, 10) - q.AddArgs(f.Args) - exec := connMethodNode("execute", q.ConstantName, q.ArgDictNode()) + // Define some reused types based on async or sync code + var connectionAnnotation *pyast.Node + if ctx.C.EmitAsync { + connectionAnnotation = typeRefNode("sqlalchemy", "ext", "asyncio", "AsyncConnection") + } else { + connectionAnnotation = typeRefNode("sqlalchemy", "engine", "Connection") + } - switch q.Cmd { - case ":one": - f.Body = append(f.Body, - assignNode("row", poet.Node( - &pyast.Call{ - Func: poet.Attribute(exec, "first"), - }, - )), - poet.Node( - &pyast.If{ - Test: poet.Node( - &pyast.Compare{ - Left: poet.Name("row"), - Ops: []*pyast.Node{ - poet.Is(), - }, - Comparators: []*pyast.Node{ - poet.Constant(nil), - }, - }, - ), - Body: []*pyast.Node{ - poet.Return( - poet.Constant(nil), - ), - }, - }, - ), - poet.Return(q.Ret.RowNode("row")), - ) - f.Returns = subscriptNode("Optional", q.Ret.Annotation()) - case ":many": - f.Body = append(f.Body, - assignNode("result", exec), - poet.Node( - &pyast.For{ - Target: poet.Name("row"), - Iter: poet.Name("result"), - Body: []*pyast.Node{ - poet.Expr( - poet.Yield( - q.Ret.RowNode("row"), - ), - ), - }, - }, - ), - ) - f.Returns = subscriptNode("Iterator", q.Ret.Annotation()) - case ":exec": - f.Body = append(f.Body, exec) - f.Returns = poet.Constant(nil) - case ":execrows": - f.Body = append(f.Body, - assignNode("result", exec), - poet.Return(poet.Attribute(poet.Name("result"), "rowcount")), - ) - f.Returns = poet.Name("int") - case ":execresult": - f.Body = append(f.Body, - poet.Return(exec), - ) - f.Returns = typeRefNode("sqlalchemy", "engine", "Result") - default: - panic("unknown cmd " + q.Cmd) - } + // We need to figure out how to access the SQLAlchemy connectionVar object + var connectionVar *pyast.Node + if ctx.C.EmitModule { + connectionVar = poet.Name("connection") + } else { + connectionVar = poet.Attribute(poet.Name("self"), "_connection") + } - cls.Body = append(cls.Body, poet.Node(f)) + // We loop through all queries and build our query functions + for _, q := range ctx.Queries { + if !ctx.OutputQuery(q.SourceName) { + continue + } + f := &pyast.FunctionDef{ + Name: q.MethodName, + Args: &pyast.Arguments{}, } - mod.Body = append(mod.Body, poet.Node(cls)) - } - if ctx.C.EmitAsyncQuerier { - cls := asyncQuerierClassDef() - for _, q := range ctx.Queries { - if !ctx.OutputQuery(q.SourceName) { - continue - } - f := &pyast.AsyncFunctionDef{ - Name: q.MethodName, - Args: &pyast.Arguments{ - Args: []*pyast.Arg{ - { - Arg: "self", - }, - }, - }, - } + if ctx.C.EmitModule { + f.Args.Args = append(f.Args.Args, &pyast.Arg{ + Arg: "connection", + Annotation: connectionAnnotation, + }) + } else { + f.Args.Args = append(f.Args.Args, &pyast.Arg{ + Arg: "self", + }) + } - q.AddArgs(f.Args) - exec := connMethodNode("execute", q.ConstantName, q.ArgDictNode()) + q.AddArgs(f.Args) - switch q.Cmd { - case ":one": - f.Body = append(f.Body, - assignNode("row", poet.Node( - &pyast.Call{ - Func: poet.Attribute(poet.Await(exec), "first"), - }, - )), - poet.Node( - &pyast.If{ - Test: poet.Node( - &pyast.Compare{ - Left: poet.Name("row"), - Ops: []*pyast.Node{ - poet.Is(), - }, - Comparators: []*pyast.Node{ - poet.Constant(nil), - }, + exec := poet.Expr(connMethodNode(poet.Attribute(connectionVar, "execute"), q.ConstantName, q.ArgDictNode())) + if ctx.C.EmitAsync { + exec = poet.Await(exec) + } + + switch q.Cmd { + case ":one": + f.Body = append(f.Body, + assignNode("row", poet.Node( + &pyast.Call{ + Func: poet.Attribute(exec, "first"), + }, + )), + poet.Node( + &pyast.If{ + Test: poet.Node( + &pyast.Compare{ + Left: poet.Name("row"), + Ops: []*pyast.Node{ + poet.Is(), }, - ), - Body: []*pyast.Node{ - poet.Return( + Comparators: []*pyast.Node{ poet.Constant(nil), - ), + }, }, + ), + Body: []*pyast.Node{ + poet.Return( + poet.Constant(nil), + ), }, - ), - poet.Return(q.Ret.RowNode("row")), - ) - f.Returns = subscriptNode("Optional", q.Ret.Annotation()) - case ":many": - stream := connMethodNode("stream", q.ConstantName, q.ArgDictNode()) - f.Body = append(f.Body, - assignNode("result", poet.Await(stream)), - poet.Node( - &pyast.AsyncFor{ - Target: poet.Name("row"), - Iter: poet.Name("result"), - Body: []*pyast.Node{ - poet.Expr( - poet.Yield( - q.Ret.RowNode("row"), + }, + ), + poet.Return(q.Ret.RowNode("row")), + ) + f.Returns = subscriptNode("Optional", q.Ret.Annotation()) + case ":many": + if ctx.C.EmitGenerators { + if ctx.C.EmitAsync { + // If we are using generators and async, we are switching to stream implementation + exec = poet.Await(connMethodNode(poet.Attribute(connectionVar, "stream"), q.ConstantName, q.ArgDictNode())) + + f.Returns = subscriptNode("AsyncIterator", q.Ret.Annotation()) + f.Body = append(f.Body, + assignNode("result", exec), + poet.Node( + &pyast.AsyncFor{ + Target: poet.Name("row"), + Iter: poet.Name("result"), + Body: []*pyast.Node{ + poet.Expr( + poet.Yield( + q.Ret.RowNode("row"), + ), + ), + }, + }, + )) + } else { + f.Returns = subscriptNode("Iterator", q.Ret.Annotation()) + f.Body = append(f.Body, + assignNode("result", exec), + poet.Node( + &pyast.For{ + Target: poet.Name("row"), + Iter: poet.Name("result"), + Body: []*pyast.Node{ + poet.Expr( + poet.Yield( + q.Ret.RowNode("row"), + ), ), - ), + }, }, - }, - ), - ) - f.Returns = subscriptNode("AsyncIterator", q.Ret.Annotation()) - case ":exec": - f.Body = append(f.Body, poet.Await(exec)) - f.Returns = poet.Constant(nil) - case ":execrows": - f.Body = append(f.Body, - assignNode("result", poet.Await(exec)), - poet.Return(poet.Attribute(poet.Name("result"), "rowcount")), - ) - f.Returns = poet.Name("int") - case ":execresult": + )) + } + } else { f.Body = append(f.Body, - poet.Return(poet.Await(exec)), - ) - f.Returns = typeRefNode("sqlalchemy", "engine", "Result") - default: - panic("unknown cmd " + q.Cmd) + assignNode("result", poet.Node( + &pyast.Call{ + Func: poet.Attribute(exec, "all"), + }, + )), + poet.Node(&pyast.Return{ + Value: poet.Node( + &pyast.For{ + Target: poet.Name("row"), + Iter: poet.Name("result"), + Body: []*pyast.Node{ + q.Ret.RowNode("row"), + }, + }, + ), + }, + )) + f.Returns = subscriptNode("List", q.Ret.Annotation()) } + case ":exec": + f.Body = append(f.Body, exec) + f.Returns = poet.Constant(nil) + case ":execrows": + f.Body = append(f.Body, + assignNode("result", exec), + poet.Return(poet.Attribute(poet.Name("result"), "rowcount")), + ) + f.Returns = poet.Name("int") + case ":execresult": + f.Body = append(f.Body, + poet.Return(exec), + ) + f.Returns = typeRefNode("sqlalchemy", "engine", "Result") + default: + panic("unknown cmd " + q.Cmd) + } - cls.Body = append(cls.Body, poet.Node(f)) + // If we are emitting async code, we have to swap our sync func for an async one and fix the connection annotation. + if ctx.C.EmitAsync { + functions = append(functions, poet.Node(&pyast.AsyncFunctionDef{ + Name: f.Name, + Args: f.Args, + Body: f.Body, + Returns: f.Returns, + })) + } else { + functions = append(functions, poet.Node(f)) } + } + + // Lets see how to add all functions + if ctx.C.EmitModule { + mod.Body = append(mod.Body, functions...) + } else { + cls := querierClassDef("Querier", connectionAnnotation) + cls.Body = append(cls.Body, functions...) mod.Body = append(mod.Body, poet.Node(cls)) } @@ -1099,6 +1054,14 @@ func Generate(_ context.Context, req *plugin.CodeGenRequest) (*plugin.CodeGenRes } } + // TODO: Remove when when we drop support for deprecated EmitSyncQuerier and EmitAsyncQuerier options + if conf.EmitAsyncQuerier || conf.EmitSyncQuerier { + conf.EmitModule = false + conf.EmitGenerators = true + conf.EmitAsync = conf.EmitAsyncQuerier + // TODO/NOTE: We now have a breaking change because we emit only one flavor. What do we want to do? + } + enums := buildEnums(req) models := buildModels(conf, req) queries, err := buildQueries(conf, req, models) diff --git a/internal/imports.go b/internal/imports.go index b78f9bd..de9cc1b 100644 --- a/internal/imports.go +++ b/internal/imports.go @@ -151,7 +151,7 @@ func (i *importer) queryImportSpecs(fileName string) (map[string]importSpec, map pkg := make(map[string]importSpec) pkg["sqlalchemy"] = importSpec{Module: "sqlalchemy"} - if i.C.EmitAsyncQuerier { + if i.C.EmitAsync { pkg["sqlalchemy.ext.asyncio"] = importSpec{Module: "sqlalchemy.ext.asyncio"} } @@ -185,11 +185,14 @@ func (i *importer) queryImportSpecs(fileName string) (map[string]importSpec, map std["typing.Optional"] = importSpec{Module: "typing", Name: "Optional"} } if q.Cmd == ":many" { - if i.C.EmitSyncQuerier { - std["typing.Iterator"] = importSpec{Module: "typing", Name: "Iterator"} - } - if i.C.EmitAsyncQuerier { - std["typing.AsyncIterator"] = importSpec{Module: "typing", Name: "AsyncIterator"} + if i.C.EmitGenerators { + if i.C.EmitAsync { + std["typing.AsyncIterator"] = importSpec{Module: "typing", Name: "AsyncIterator"} + } else { + std["typing.Iterator"] = importSpec{Module: "typing", Name: "Iterator"} + } + } else { + std["typing.List"] = importSpec{Module: "typing", Name: "List"} } } queryValueModelImports(q.Ret) diff --git a/internal/poet/poet.go b/internal/poet/poet.go index 22b488c..fc22995 100644 --- a/internal/poet/poet.go +++ b/internal/poet/poet.go @@ -161,6 +161,13 @@ func Node(node proto) *ast.Node { // case *ast.Node_Subscript: // w.printSubscript(n.Subscript, indent) + case *ast.Return: + return &ast.Node{ + Node: &ast.Node_Return{ + Return: n, + }, + } + case *ast.Yield: return &ast.Node{ Node: &ast.Node_Yield{ diff --git a/internal/printer/printer.go b/internal/printer/printer.go index 0660c6a..e57ad63 100644 --- a/internal/printer/printer.go +++ b/internal/printer/printer.go @@ -83,7 +83,7 @@ func (w *writer) printNode(node *ast.Node, indent int32) { w.printNode(n.Expr.Value, indent) case *ast.Node_For: - w.printFor(n.For, indent) + w.printFor(n.For, false, indent) case *ast.Node_FunctionDef: w.printFunctionDef(n.FunctionDef, indent) @@ -162,12 +162,11 @@ func (w *writer) printAssign(a *ast.Assign, indent int32) { } func (w *writer) printAsyncFor(n *ast.AsyncFor, indent int32) { - w.print("async ") w.printFor(&ast.For{ Target: n.Target, Iter: n.Iter, Body: n.Body, - }, indent) + }, true, indent) } func (w *writer) printAsyncFunctionDef(afd *ast.AsyncFunctionDef, indent int32) { @@ -341,17 +340,45 @@ func (w *writer) printDict(d *ast.Dict, indent int32) { w.print("}") } -func (w *writer) printFor(n *ast.For, indent int32) { - w.print("for ") - w.printNode(n.Target, indent) - w.print(" in ") - w.printNode(n.Iter, indent) - w.print(":\n") - for i, node := range n.Body { +func (w *writer) printFor(n *ast.For, isAsync bool, indent int32) { + // We should always have a body + if len(n.Body) <= 0 { + panic(n) + } + + // TODO: How to better support list comprehension? Maybe add a flag to AST node ForNode and AsyncNode? + _, isCall := n.Body[0].Node.(*ast.Node_Call) + if len(n.Body) == 1 && isCall { + w.print("[\n") w.printIndent(indent + 1) - w.printNode(node, indent+1) - if i != len(n.Body)-1 { - w.print("\n") + w.printNode(n.Body[0], indent+1) + w.print("\n") + w.printIndent(indent + 1) + if isAsync { + w.print("async ") + } + w.print("for ") + w.printNode(n.Target, indent) + w.print(" in ") + w.printNode(n.Iter, indent) + w.print("\n") + w.printIndent(indent) + w.print("]\n") + } else { + if isAsync { + w.print("async ") + } + w.print("for ") + w.printNode(n.Target, indent) + w.print(" in ") + w.printNode(n.Iter, indent) + w.print(":\n") + for i, node := range n.Body { + w.printIndent(indent + 1) + w.printNode(node, indent+1) + if i != len(n.Body)-1 { + w.print("\n") + } } } } @@ -455,14 +482,20 @@ func (w *writer) printModule(mod *ast.Module, indent int32) { _, isImport := mod.Body[i-1].Node.(*ast.Node_ImportGroup) prevIsImport = isImport } + _, isClassDef := node.Node.(*ast.Node_ClassDef) + _, isFunctionDef := node.Node.(*ast.Node_FunctionDef) + _, isAsyncFunctionDef := node.Node.(*ast.Node_AsyncFunctionDef) _, isAssign := node.Node.(*ast.Node_Assign) + if isClassDef || isAssign { if prevIsImport { w.print("\n") } else { w.print("\n\n") } + } else if isAsyncFunctionDef || isFunctionDef { + w.print("\n\n\n") } w.printNode(node, indent) if isAssign { From c7137dfacd2381316c51128c40190c316bfb5ef5 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 23 Aug 2023 00:48:51 +0200 Subject: [PATCH 02/18] docs: udated README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7359dce..5c9a66e 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ sql: plugin: py options: package: authors - emit_sync_querier: true - emit_async_querier: true + emit_module: false + emit_generators: true + emit_async: false ``` From ba256bd0b0e9aec7b3ebc5221c28b110ff894899 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 23 Aug 2023 00:58:53 +0200 Subject: [PATCH 03/18] chore: updated go version to 1.21.0 and uploading of plugin as artifact --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0941f54..626d650 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,5 +12,9 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: - go-version: '1.21.0-rc.2' + go-version: '1.21.0' - run: make + - uses: actions/upload-artifact@v3 + with: + name: sqlc-gen-python + path: plugin/sqlc-gen-python.wasm \ No newline at end of file From bfa71a905b9c2ef794867bcee4127fd6cfb96a72 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Thu, 24 Aug 2023 11:27:32 +0200 Subject: [PATCH 04/18] feat: added output_models_file_name and output_querier_file options to configure output files --- README.md | 11 +++++++++++ internal/config.go | 15 +++++++++------ internal/gen.go | 44 +++++++++++++++++++++++++++++--------------- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 5c9a66e..aa5cfa7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +# README ## Usage ```yaml @@ -20,3 +21,13 @@ sql: emit_generators: true emit_async: false ``` + +## Multiple packages +If you have have a mono-repository setup you may want to split the output of queries and models. You can achieve this by using the `output_models_file_name` +and `output_querier_file` fields. If `output_models_file_name` is set to `null` for it will not output the `models.py` file. Setting `output_querier_file` to false will prevent outputting any query files. Combining these you can set one codegen to only output models while the other codegen outputs only queries. Make sure the `package` configuration is set equally so the query files import correctly the models. + +SQLC needs at least one query, so you may need to add a unused query like the following in your schema and reuse the `schema` as `queries`. +```sql +-- name: Skip :one +SELECT 1; +``` \ No newline at end of file diff --git a/internal/config.go b/internal/config.go index 4f44501..e7e1d00 100644 --- a/internal/config.go +++ b/internal/config.go @@ -1,15 +1,18 @@ package python +// TODO: Where are these properly documented? type Config struct { - EmitModule bool `json:"emit_module"` // If true emits functions in module, else wraps in a class. - EmitGenerators bool `json:"emit_generators"` // Will we use generators or lists, defaults to true - EmitAsync bool `json:"emit_async"` // Emits async code instead of sync + EmitAsync bool `json:"emit_async"` // Emits async code instead of sync EmitExactTableNames bool `json:"emit_exact_table_names"` + EmitGenerators bool `json:"emit_generators"` // Will we use generators or lists, defaults to true + EmitModule bool `json:"emit_module"` // If true emits functions in module, else wraps in a class. + EmitPydanticModels bool `json:"emit_pydantic_models"` EmitSyncQuerier bool `json:"emit_sync_querier"` // DEPRECATED ALIAS FOR: emit_type = 'class', emit_generators = True EmitAsyncQuerier bool `json:"emit_async_querier"` // DEPRECATED ALIAS FOR: emit_type = 'class', emit_generators = True - Package string `json:"package"` + InflectionExcludeTableNames []string `json:"inflection_exclude_table_names"` Out string `json:"out"` - EmitPydanticModels bool `json:"emit_pydantic_models"` + OutputModelsFileName *string `json:"output_models_file_name,omitempty"` // Can be string or null to exclude generating models file + OutputQuerierFile bool `json:"output_querier_file,omitempty"` // Skips outputting queries + Package string `json:"package"` QueryParameterLimit *int32 `json:"query_parameter_limit"` - InflectionExcludeTableNames []string `json:"inflection_exclude_table_names"` } diff --git a/internal/gen.go b/internal/gen.go index 74f23fe..d588c8c 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -1047,7 +1047,13 @@ func HashComment(s string) string { } func Generate(_ context.Context, req *plugin.CodeGenRequest) (*plugin.CodeGenResponse, error) { - var conf Config + // Setup our defaults for our Config struct and parse our config file + defaultModelsFileName := "models.py" + conf := Config{ + OutputModelsFileName: &defaultModelsFileName, + OutputQuerierFile: true, + } + if len(req.PluginOptions) > 0 { if err := json.Unmarshal(req.PluginOptions, &conf); err != nil { return nil, err @@ -1086,26 +1092,34 @@ func Generate(_ context.Context, req *plugin.CodeGenRequest) (*plugin.CodeGenRes } output := map[string]string{} - result := pyprint.Print(buildModelsTree(&tctx, i), pyprint.Options{}) - tctx.SourceName = "models.py" - output["models.py"] = string(result.Python) - files := map[string]struct{}{} - for _, q := range queries { - files[q.SourceName] = struct{}{} + // Generate the model file. + if conf.OutputModelsFileName != nil { + result := pyprint.Print(buildModelsTree(&tctx, i), pyprint.Options{}) + tctx.SourceName = *conf.OutputModelsFileName + output[*conf.OutputModelsFileName] = string(result.Python) } - for source := range files { - tctx.SourceName = source - result := pyprint.Print(buildQueryTree(&tctx, i, source), pyprint.Options{}) - name := source - if !strings.HasSuffix(name, ".py") { - name = strings.TrimSuffix(name, ".sql") - name += ".py" + // Generate for each .sql file a corresponding Python query file. + if conf.OutputQuerierFile { + files := map[string]struct{}{} + for _, q := range queries { + files[q.SourceName] = struct{}{} + } + + for source := range files { + tctx.SourceName = source + result := pyprint.Print(buildQueryTree(&tctx, i, source), pyprint.Options{}) + name := source + if !strings.HasSuffix(name, ".py") { + name = strings.TrimSuffix(name, ".sql") + name += ".py" + } + output[name] = string(result.Python) } - output[name] = string(result.Python) } + // Finally we send our outputs back to SQLC resp := plugin.CodeGenResponse{} for filename, code := range output { From f2390827d3f80b30dd4b227374eebe25eef06849 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Thu, 24 Aug 2023 20:05:01 +0200 Subject: [PATCH 05/18] feat: support sqlc.embed --- internal/gen.go | 102 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 9 deletions(-) diff --git a/internal/gen.go b/internal/gen.go index d588c8c..3431c26 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -53,6 +53,8 @@ type Field struct { Name string Type pyType Comment string + // EmbedFields contains the embedded fields that require scanning. + EmbedFields []Field } type Struct struct { @@ -105,14 +107,42 @@ func (v QueryValue) RowNode(rowVar string) *pyast.Node { call := &pyast.Call{ Func: v.Annotation(), } - for i, f := range v.Struct.Fields { - call.Keywords = append(call.Keywords, &pyast.Keyword{ - Arg: f.Name, - Value: subscriptNode( + rowIndex := 0 // We need to keep track of the index in the row variable. + for _, f := range v.Struct.Fields { + + var valueNode *pyast.Node + // Check if we are using sqlc.embed, if so we need to create a new object. + if len(f.EmbedFields) > 0 { + // We keep this separate so we can easily add all arguments. + embed_call := &pyast.Call{Func: f.Type.Annotation()} + + // Now add all field Initializers for the embedded model that index into the original row. + for i, embedField := range f.EmbedFields { + embed_call.Keywords = append(embed_call.Keywords, &pyast.Keyword{ + Arg: embedField.Name, + Value: subscriptNode( + rowVar, + constantInt(rowIndex+i), + ), + }) + } + + valueNode = &pyast.Node{ + Node: &pyast.Node_Call{ + Call: embed_call, + }, + } + + rowIndex += len(f.EmbedFields) + } else { + valueNode = subscriptNode( rowVar, - constantInt(i), - ), - }) + constantInt(rowIndex), + ) + rowIndex++ + } + + call.Keywords = append(call.Keywords, &pyast.Keyword{Arg: f.Name, Value: valueNode}) } return &pyast.Node{ Node: &pyast.Node_Call{ @@ -336,6 +366,47 @@ func paramName(p *plugin.Parameter) string { type pyColumn struct { id int32 *plugin.Column + embed *pyEmbed +} + +type pyEmbed struct { + modelType string + modelName string + fields []Field +} + +// Taken from https://github.com/sqlc-dev/sqlc/blob/8c59fbb9938a0bad3d9971fc2c10ea1f83cc1d0b/internal/codegen/golang/result.go#L123-L126 +// look through all the structs and attempt to find a matching one to embed +// We need the name of the struct and its field names. +func newGoEmbed(embed *plugin.Identifier, structs []Struct, defaultSchema string) *pyEmbed { + if embed == nil { + return nil + } + + for _, s := range structs { + embedSchema := defaultSchema + if embed.Schema != "" { + embedSchema = embed.Schema + } + + // compare the other attributes + if embed.Catalog != s.Table.Catalog || embed.Name != s.Table.Name || embedSchema != s.Table.Schema { + continue + } + + fields := make([]Field, len(s.Fields)) + for i, f := range s.Fields { + fields[i] = f + } + + return &pyEmbed{ + modelType: s.Name, + modelName: s.Name, + fields: fields, + } + } + + return nil } func columnsToStruct(req *plugin.CodeGenRequest, name string, columns []pyColumn) *Struct { @@ -359,10 +430,22 @@ func columnsToStruct(req *plugin.CodeGenRequest, name string, columns []pyColumn if suffix > 0 { fieldName = fmt.Sprintf("%s_%d", fieldName, suffix) } - gs.Fields = append(gs.Fields, Field{ + + f := Field{ Name: fieldName, Type: makePyType(req, c.Column), - }) + } + + if c.embed != nil { + f.Type = pyType{ + InnerType: "models." + modelName(c.embed.modelType, req.Settings), + IsArray: false, + IsNull: false, + } + f.EmbedFields = c.embed.fields + } + + gs.Fields = append(gs.Fields, f) seen[colName]++ } return &gs @@ -476,6 +559,7 @@ func buildQueries(conf Config, req *plugin.CodeGenRequest, structs []Struct) ([] columns = append(columns, pyColumn{ id: int32(i), Column: c, + embed: newGoEmbed(c.EmbedTable, structs, req.Catalog.DefaultSchema), }) } gs = columnsToStruct(req, query.Name+"Row", columns) From 0d706793c6f923d8511a4308385b2dd01be0e4e0 Mon Sep 17 00:00:00 2001 From: Kyle Gray Date: Thu, 2 Nov 2023 15:54:23 -0700 Subject: [PATCH 06/18] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7359dce..d7e0d75 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ version: '2' plugins: - name: py wasm: - url: https://downloads.sqlc.dev/plugin/sqlc-gen-python_1.1.0.wasm - sha256: ef58f143a8c116781091441770c7166caaf361dd645f62b8f05f462e9f95c3b2 + url: https://downloads.sqlc.dev/plugin/sqlc-gen-python_1.2.0.wasm + sha256: a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e sql: - schema: "schema.sql" queries: "query.sql" From 34be14051b41df3a21f068bb31fbeddd0ab26757 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Thu, 2 Nov 2023 15:55:34 -0700 Subject: [PATCH 07/18] docs: Update examples to 1.2.0 --- examples/sqlc.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/sqlc.yaml b/examples/sqlc.yaml index 36a9b9e..712c5db 100644 --- a/examples/sqlc.yaml +++ b/examples/sqlc.yaml @@ -2,8 +2,8 @@ version: '2' plugins: - name: py wasm: - url: https://downloads.sqlc.dev/plugin/sqlc-gen-python_1.1.0.wasm - sha256: ef58f143a8c116781091441770c7166caaf361dd645f62b8f05f462e9f95c3b2 + url: https://downloads.sqlc.dev/plugin/sqlc-gen-python_1.2.0.wasm + sha256: a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e sql: - schema: "src/authors/schema.sql" queries: "src/authors/query.sql" From ad5adcaf33a2d167a65f702a392ca919a0f99b9a Mon Sep 17 00:00:00 2001 From: Kyle Gray Date: Wed, 22 Nov 2023 09:51:04 -0800 Subject: [PATCH 08/18] build: Upgrade to sqlc v1.24.0 (#34) * build: Upgrade to sqlc v1.24.0 * Update test data --- .github/workflows/ci.yml | 7 ++++--- examples/src/authors/models.py | 2 +- examples/src/authors/query.py | 2 +- examples/src/booktest/models.py | 2 +- examples/src/booktest/query.py | 2 +- examples/src/jets/models.py | 2 +- examples/src/jets/query-building.py | 2 +- examples/src/ondeck/city.py | 2 +- examples/src/ondeck/models.py | 2 +- examples/src/ondeck/venue.py | 2 +- .../endtoend/testdata/emit_pydantic_models/db/models.py | 2 +- .../endtoend/testdata/emit_pydantic_models/db/query.py | 2 +- internal/endtoend/testdata/exec_result/python/models.py | 2 +- internal/endtoend/testdata/exec_result/python/query.py | 2 +- internal/endtoend/testdata/exec_rows/python/models.py | 2 +- internal/endtoend/testdata/exec_rows/python/query.py | 2 +- .../inflection_exclude_table_names/python/models.py | 2 +- .../inflection_exclude_table_names/python/query.py | 2 +- .../testdata/query_parameter_limit_two/python/models.py | 2 +- .../testdata/query_parameter_limit_two/python/query.py | 2 +- .../query_parameter_limit_undefined/python/models.py | 2 +- .../query_parameter_limit_undefined/python/query.py | 2 +- .../testdata/query_parameter_limit_zero/python/models.py | 2 +- .../testdata/query_parameter_limit_zero/python/query.py | 2 +- 24 files changed, 27 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 365479d..805e9f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,9 +13,10 @@ jobs: - uses: actions/setup-go@v4 with: go-version: '1.21' - - uses: sqlc-dev/setup-sqlc@v4 - with: - sqlc-version: '1.23.0' + - run: go install github.com/sqlc-dev/sqlc/cmd/sqlc@52beae33148eafd4fc3d56f9ae95dcb529d9e4a1 + # - uses: sqlc-dev/setup-sqlc@v4 + # with: + # sqlc-version: '1.23.0' - run: make - run: make test - run: sqlc diff diff --git a/examples/src/authors/models.py b/examples/src/authors/models.py index a8d25d0..93b34e9 100644 --- a/examples/src/authors/models.py +++ b/examples/src/authors/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 import dataclasses from typing import Optional diff --git a/examples/src/authors/query.py b/examples/src/authors/query.py index 58eccb9..5e396ab 100644 --- a/examples/src/authors/query.py +++ b/examples/src/authors/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 # source: query.sql from typing import AsyncIterator, Iterator, Optional diff --git a/examples/src/booktest/models.py b/examples/src/booktest/models.py index 712f166..a9f658e 100644 --- a/examples/src/booktest/models.py +++ b/examples/src/booktest/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 import dataclasses import datetime import enum diff --git a/examples/src/booktest/query.py b/examples/src/booktest/query.py index 94b8565..b2d9f10 100644 --- a/examples/src/booktest/query.py +++ b/examples/src/booktest/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 # source: query.sql import dataclasses import datetime diff --git a/examples/src/jets/models.py b/examples/src/jets/models.py index 7d3063a..0917fbd 100644 --- a/examples/src/jets/models.py +++ b/examples/src/jets/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 import dataclasses diff --git a/examples/src/jets/query-building.py b/examples/src/jets/query-building.py index 6fe42df..26b45b0 100644 --- a/examples/src/jets/query-building.py +++ b/examples/src/jets/query-building.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 # source: query-building.sql from typing import AsyncIterator, Optional diff --git a/examples/src/ondeck/city.py b/examples/src/ondeck/city.py index e32873e..2313b6b 100644 --- a/examples/src/ondeck/city.py +++ b/examples/src/ondeck/city.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 # source: city.sql from typing import AsyncIterator, Optional diff --git a/examples/src/ondeck/models.py b/examples/src/ondeck/models.py index 71c98c1..c860501 100644 --- a/examples/src/ondeck/models.py +++ b/examples/src/ondeck/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 import dataclasses import datetime import enum diff --git a/examples/src/ondeck/venue.py b/examples/src/ondeck/venue.py index 88e4a56..4cab516 100644 --- a/examples/src/ondeck/venue.py +++ b/examples/src/ondeck/venue.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 # source: venue.sql import dataclasses from typing import AsyncIterator, List, Optional diff --git a/internal/endtoend/testdata/emit_pydantic_models/db/models.py b/internal/endtoend/testdata/emit_pydantic_models/db/models.py index 7ac0da3..7c0c8dd 100644 --- a/internal/endtoend/testdata/emit_pydantic_models/db/models.py +++ b/internal/endtoend/testdata/emit_pydantic_models/db/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 import pydantic from typing import Optional diff --git a/internal/endtoend/testdata/emit_pydantic_models/db/query.py b/internal/endtoend/testdata/emit_pydantic_models/db/query.py index dc04a26..a3b9620 100644 --- a/internal/endtoend/testdata/emit_pydantic_models/db/query.py +++ b/internal/endtoend/testdata/emit_pydantic_models/db/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 # source: query.sql from typing import AsyncIterator, Iterator, Optional diff --git a/internal/endtoend/testdata/exec_result/python/models.py b/internal/endtoend/testdata/exec_result/python/models.py index d2293ed..a86ba2a 100644 --- a/internal/endtoend/testdata/exec_result/python/models.py +++ b/internal/endtoend/testdata/exec_result/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 import dataclasses diff --git a/internal/endtoend/testdata/exec_result/python/query.py b/internal/endtoend/testdata/exec_result/python/query.py index ceccd51..34c5b30 100644 --- a/internal/endtoend/testdata/exec_result/python/query.py +++ b/internal/endtoend/testdata/exec_result/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 # source: query.sql import sqlalchemy import sqlalchemy.ext.asyncio diff --git a/internal/endtoend/testdata/exec_rows/python/models.py b/internal/endtoend/testdata/exec_rows/python/models.py index d2293ed..a86ba2a 100644 --- a/internal/endtoend/testdata/exec_rows/python/models.py +++ b/internal/endtoend/testdata/exec_rows/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 import dataclasses diff --git a/internal/endtoend/testdata/exec_rows/python/query.py b/internal/endtoend/testdata/exec_rows/python/query.py index 904f428..6dbca4c 100644 --- a/internal/endtoend/testdata/exec_rows/python/query.py +++ b/internal/endtoend/testdata/exec_rows/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 # source: query.sql import sqlalchemy import sqlalchemy.ext.asyncio diff --git a/internal/endtoend/testdata/inflection_exclude_table_names/python/models.py b/internal/endtoend/testdata/inflection_exclude_table_names/python/models.py index b01f524..328460f 100644 --- a/internal/endtoend/testdata/inflection_exclude_table_names/python/models.py +++ b/internal/endtoend/testdata/inflection_exclude_table_names/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 import dataclasses diff --git a/internal/endtoend/testdata/inflection_exclude_table_names/python/query.py b/internal/endtoend/testdata/inflection_exclude_table_names/python/query.py index 100bef3..e013dfb 100644 --- a/internal/endtoend/testdata/inflection_exclude_table_names/python/query.py +++ b/internal/endtoend/testdata/inflection_exclude_table_names/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 # source: query.sql from typing import Optional diff --git a/internal/endtoend/testdata/query_parameter_limit_two/python/models.py b/internal/endtoend/testdata/query_parameter_limit_two/python/models.py index 9bc595f..58f6a61 100644 --- a/internal/endtoend/testdata/query_parameter_limit_two/python/models.py +++ b/internal/endtoend/testdata/query_parameter_limit_two/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 import dataclasses diff --git a/internal/endtoend/testdata/query_parameter_limit_two/python/query.py b/internal/endtoend/testdata/query_parameter_limit_two/python/query.py index 3ca9cba..82db283 100644 --- a/internal/endtoend/testdata/query_parameter_limit_two/python/query.py +++ b/internal/endtoend/testdata/query_parameter_limit_two/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 # source: query.sql import sqlalchemy import sqlalchemy.ext.asyncio diff --git a/internal/endtoend/testdata/query_parameter_limit_undefined/python/models.py b/internal/endtoend/testdata/query_parameter_limit_undefined/python/models.py index 5e2f655..4c99954 100644 --- a/internal/endtoend/testdata/query_parameter_limit_undefined/python/models.py +++ b/internal/endtoend/testdata/query_parameter_limit_undefined/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 import dataclasses diff --git a/internal/endtoend/testdata/query_parameter_limit_undefined/python/query.py b/internal/endtoend/testdata/query_parameter_limit_undefined/python/query.py index 4dccfa9..f481eb1 100644 --- a/internal/endtoend/testdata/query_parameter_limit_undefined/python/query.py +++ b/internal/endtoend/testdata/query_parameter_limit_undefined/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 # source: query.sql import sqlalchemy import sqlalchemy.ext.asyncio diff --git a/internal/endtoend/testdata/query_parameter_limit_zero/python/models.py b/internal/endtoend/testdata/query_parameter_limit_zero/python/models.py index 9bc595f..58f6a61 100644 --- a/internal/endtoend/testdata/query_parameter_limit_zero/python/models.py +++ b/internal/endtoend/testdata/query_parameter_limit_zero/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 import dataclasses diff --git a/internal/endtoend/testdata/query_parameter_limit_zero/python/query.py b/internal/endtoend/testdata/query_parameter_limit_zero/python/query.py index 2a42517..cee76d2 100644 --- a/internal/endtoend/testdata/query_parameter_limit_zero/python/query.py +++ b/internal/endtoend/testdata/query_parameter_limit_zero/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.23.0 +# sqlc v1.24.0 # source: query.sql import dataclasses From c283b5eae268f1dd4a161ea46a27909f54f42ecd Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Wed, 22 Nov 2023 11:35:39 -0800 Subject: [PATCH 09/18] build: Install v1.24.0 via setup-sql --- .github/workflows/ci.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 805e9f4..3f76722 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,11 +13,10 @@ jobs: - uses: actions/setup-go@v4 with: go-version: '1.21' - - run: go install github.com/sqlc-dev/sqlc/cmd/sqlc@52beae33148eafd4fc3d56f9ae95dcb529d9e4a1 - # - uses: sqlc-dev/setup-sqlc@v4 - # with: - # sqlc-version: '1.23.0' + - uses: sqlc-dev/setup-sqlc@v4 + with: + sqlc-version: '1.24.0' - run: make - run: make test - run: sqlc diff - working-directory: examples \ No newline at end of file + working-directory: examples From 4a12779791a18634326cb65a78c4ba856f8ca3af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 10:46:02 -0800 Subject: [PATCH 10/18] build(deps): bump actions/setup-python from 4 to 5 (#37) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/db.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/db.yml b/.github/workflows/db.yml index 8b10271..46d0115 100644 --- a/.github/workflows/db.yml +++ b/.github/workflows/db.yml @@ -24,7 +24,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install python dependencies From 8f60b07ea197ea5e44a0df712422a0c2d987ec0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 10:46:22 -0800 Subject: [PATCH 11/18] build(deps): bump actions/setup-go from 4 to 5 (#36) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f76722..deea801 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: '1.21' - uses: sqlc-dev/setup-sqlc@v4 From cbc45e795616cbbe79a61409efa15cf14bd31093 Mon Sep 17 00:00:00 2001 From: Kyle Gray Date: Wed, 3 Jan 2024 16:02:40 -0800 Subject: [PATCH 12/18] build(deps): Upgrade to sqlc v1.25.0 (#40) --- .github/workflows/ci.yml | 7 ++++--- examples/src/authors/models.py | 2 +- examples/src/authors/query.py | 2 +- examples/src/booktest/models.py | 2 +- examples/src/booktest/query.py | 2 +- examples/src/jets/models.py | 2 +- examples/src/jets/query-building.py | 2 +- examples/src/ondeck/city.py | 2 +- examples/src/ondeck/models.py | 2 +- examples/src/ondeck/venue.py | 2 +- .../endtoend/testdata/emit_pydantic_models/db/models.py | 2 +- .../endtoend/testdata/emit_pydantic_models/db/query.py | 2 +- internal/endtoend/testdata/exec_result/python/models.py | 2 +- internal/endtoend/testdata/exec_result/python/query.py | 2 +- internal/endtoend/testdata/exec_rows/python/models.py | 2 +- internal/endtoend/testdata/exec_rows/python/query.py | 2 +- .../inflection_exclude_table_names/python/models.py | 2 +- .../inflection_exclude_table_names/python/query.py | 2 +- .../testdata/query_parameter_limit_two/python/models.py | 2 +- .../testdata/query_parameter_limit_two/python/query.py | 2 +- .../query_parameter_limit_undefined/python/models.py | 2 +- .../query_parameter_limit_undefined/python/query.py | 2 +- .../testdata/query_parameter_limit_zero/python/models.py | 2 +- .../testdata/query_parameter_limit_zero/python/query.py | 2 +- 24 files changed, 27 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index deea801..243b51b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,9 +13,10 @@ jobs: - uses: actions/setup-go@v5 with: go-version: '1.21' - - uses: sqlc-dev/setup-sqlc@v4 - with: - sqlc-version: '1.24.0' + - run: go install github.com/sqlc-dev/sqlc/cmd/sqlc@883c70a44e3c9e260598ae09034c92b51f2c9129 + # - uses: sqlc-dev/setup-sqlc@v4 + # with: + # sqlc-version: '1.24.0' - run: make - run: make test - run: sqlc diff diff --git a/examples/src/authors/models.py b/examples/src/authors/models.py index 93b34e9..b0e9679 100644 --- a/examples/src/authors/models.py +++ b/examples/src/authors/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 import dataclasses from typing import Optional diff --git a/examples/src/authors/query.py b/examples/src/authors/query.py index 5e396ab..0963009 100644 --- a/examples/src/authors/query.py +++ b/examples/src/authors/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 # source: query.sql from typing import AsyncIterator, Iterator, Optional diff --git a/examples/src/booktest/models.py b/examples/src/booktest/models.py index a9f658e..df11dd5 100644 --- a/examples/src/booktest/models.py +++ b/examples/src/booktest/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 import dataclasses import datetime import enum diff --git a/examples/src/booktest/query.py b/examples/src/booktest/query.py index b2d9f10..e9f0f0a 100644 --- a/examples/src/booktest/query.py +++ b/examples/src/booktest/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 # source: query.sql import dataclasses import datetime diff --git a/examples/src/jets/models.py b/examples/src/jets/models.py index 0917fbd..084ce2f 100644 --- a/examples/src/jets/models.py +++ b/examples/src/jets/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 import dataclasses diff --git a/examples/src/jets/query-building.py b/examples/src/jets/query-building.py index 26b45b0..7a04f13 100644 --- a/examples/src/jets/query-building.py +++ b/examples/src/jets/query-building.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 # source: query-building.sql from typing import AsyncIterator, Optional diff --git a/examples/src/ondeck/city.py b/examples/src/ondeck/city.py index 2313b6b..0510fe2 100644 --- a/examples/src/ondeck/city.py +++ b/examples/src/ondeck/city.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 # source: city.sql from typing import AsyncIterator, Optional diff --git a/examples/src/ondeck/models.py b/examples/src/ondeck/models.py index c860501..6bcad5f 100644 --- a/examples/src/ondeck/models.py +++ b/examples/src/ondeck/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 import dataclasses import datetime import enum diff --git a/examples/src/ondeck/venue.py b/examples/src/ondeck/venue.py index 4cab516..036af6d 100644 --- a/examples/src/ondeck/venue.py +++ b/examples/src/ondeck/venue.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 # source: venue.sql import dataclasses from typing import AsyncIterator, List, Optional diff --git a/internal/endtoend/testdata/emit_pydantic_models/db/models.py b/internal/endtoend/testdata/emit_pydantic_models/db/models.py index 7c0c8dd..488223a 100644 --- a/internal/endtoend/testdata/emit_pydantic_models/db/models.py +++ b/internal/endtoend/testdata/emit_pydantic_models/db/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 import pydantic from typing import Optional diff --git a/internal/endtoend/testdata/emit_pydantic_models/db/query.py b/internal/endtoend/testdata/emit_pydantic_models/db/query.py index a3b9620..ca58210 100644 --- a/internal/endtoend/testdata/emit_pydantic_models/db/query.py +++ b/internal/endtoend/testdata/emit_pydantic_models/db/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 # source: query.sql from typing import AsyncIterator, Iterator, Optional diff --git a/internal/endtoend/testdata/exec_result/python/models.py b/internal/endtoend/testdata/exec_result/python/models.py index a86ba2a..8b64866 100644 --- a/internal/endtoend/testdata/exec_result/python/models.py +++ b/internal/endtoend/testdata/exec_result/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 import dataclasses diff --git a/internal/endtoend/testdata/exec_result/python/query.py b/internal/endtoend/testdata/exec_result/python/query.py index 34c5b30..bc547c9 100644 --- a/internal/endtoend/testdata/exec_result/python/query.py +++ b/internal/endtoend/testdata/exec_result/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 # source: query.sql import sqlalchemy import sqlalchemy.ext.asyncio diff --git a/internal/endtoend/testdata/exec_rows/python/models.py b/internal/endtoend/testdata/exec_rows/python/models.py index a86ba2a..8b64866 100644 --- a/internal/endtoend/testdata/exec_rows/python/models.py +++ b/internal/endtoend/testdata/exec_rows/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 import dataclasses diff --git a/internal/endtoend/testdata/exec_rows/python/query.py b/internal/endtoend/testdata/exec_rows/python/query.py index 6dbca4c..3cd0598 100644 --- a/internal/endtoend/testdata/exec_rows/python/query.py +++ b/internal/endtoend/testdata/exec_rows/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 # source: query.sql import sqlalchemy import sqlalchemy.ext.asyncio diff --git a/internal/endtoend/testdata/inflection_exclude_table_names/python/models.py b/internal/endtoend/testdata/inflection_exclude_table_names/python/models.py index 328460f..44bc293 100644 --- a/internal/endtoend/testdata/inflection_exclude_table_names/python/models.py +++ b/internal/endtoend/testdata/inflection_exclude_table_names/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 import dataclasses diff --git a/internal/endtoend/testdata/inflection_exclude_table_names/python/query.py b/internal/endtoend/testdata/inflection_exclude_table_names/python/query.py index e013dfb..14cd7f2 100644 --- a/internal/endtoend/testdata/inflection_exclude_table_names/python/query.py +++ b/internal/endtoend/testdata/inflection_exclude_table_names/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 # source: query.sql from typing import Optional diff --git a/internal/endtoend/testdata/query_parameter_limit_two/python/models.py b/internal/endtoend/testdata/query_parameter_limit_two/python/models.py index 58f6a61..f7cc057 100644 --- a/internal/endtoend/testdata/query_parameter_limit_two/python/models.py +++ b/internal/endtoend/testdata/query_parameter_limit_two/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 import dataclasses diff --git a/internal/endtoend/testdata/query_parameter_limit_two/python/query.py b/internal/endtoend/testdata/query_parameter_limit_two/python/query.py index 82db283..6b8c983 100644 --- a/internal/endtoend/testdata/query_parameter_limit_two/python/query.py +++ b/internal/endtoend/testdata/query_parameter_limit_two/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 # source: query.sql import sqlalchemy import sqlalchemy.ext.asyncio diff --git a/internal/endtoend/testdata/query_parameter_limit_undefined/python/models.py b/internal/endtoend/testdata/query_parameter_limit_undefined/python/models.py index 4c99954..8742aea 100644 --- a/internal/endtoend/testdata/query_parameter_limit_undefined/python/models.py +++ b/internal/endtoend/testdata/query_parameter_limit_undefined/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 import dataclasses diff --git a/internal/endtoend/testdata/query_parameter_limit_undefined/python/query.py b/internal/endtoend/testdata/query_parameter_limit_undefined/python/query.py index f481eb1..89808c7 100644 --- a/internal/endtoend/testdata/query_parameter_limit_undefined/python/query.py +++ b/internal/endtoend/testdata/query_parameter_limit_undefined/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 # source: query.sql import sqlalchemy import sqlalchemy.ext.asyncio diff --git a/internal/endtoend/testdata/query_parameter_limit_zero/python/models.py b/internal/endtoend/testdata/query_parameter_limit_zero/python/models.py index 58f6a61..f7cc057 100644 --- a/internal/endtoend/testdata/query_parameter_limit_zero/python/models.py +++ b/internal/endtoend/testdata/query_parameter_limit_zero/python/models.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 import dataclasses diff --git a/internal/endtoend/testdata/query_parameter_limit_zero/python/query.py b/internal/endtoend/testdata/query_parameter_limit_zero/python/query.py index cee76d2..fb854ad 100644 --- a/internal/endtoend/testdata/query_parameter_limit_zero/python/query.py +++ b/internal/endtoend/testdata/query_parameter_limit_zero/python/query.py @@ -1,6 +1,6 @@ # Code generated by sqlc. DO NOT EDIT. # versions: -# sqlc v1.24.0 +# sqlc v1.25.0 # source: query.sql import dataclasses From ba3801e2fa0f9c40b740150776463be48395f198 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 08:23:39 -0800 Subject: [PATCH 13/18] build(deps): bump google.golang.org/protobuf from 1.31.0 to 1.32.0 (#38) Bumps google.golang.org/protobuf from 1.31.0 to 1.32.0. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4894ab0..3c7384e 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/jinzhu/inflection v1.0.0 github.com/sqlc-dev/plugin-sdk-go v1.23.0 - google.golang.org/protobuf v1.31.0 + google.golang.org/protobuf v1.32.0 ) require ( diff --git a/go.sum b/go.sum index 6339a96..9017af9 100644 --- a/go.sum +++ b/go.sum @@ -21,5 +21,5 @@ google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= From 8a24f3afc576e252831b54e739e99e53921d50f1 Mon Sep 17 00:00:00 2001 From: Kyle Gray Date: Thu, 4 Jan 2024 08:37:22 -0800 Subject: [PATCH 14/18] Update workflow to use setup-sqlc (#41) --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 243b51b..b6e5983 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,10 +13,10 @@ jobs: - uses: actions/setup-go@v5 with: go-version: '1.21' - - run: go install github.com/sqlc-dev/sqlc/cmd/sqlc@883c70a44e3c9e260598ae09034c92b51f2c9129 - # - uses: sqlc-dev/setup-sqlc@v4 - # with: - # sqlc-version: '1.24.0' + # - run: go install github.com/sqlc-dev/sqlc/cmd/sqlc@main + - uses: sqlc-dev/setup-sqlc@v4 + with: + sqlc-version: '1.25.0' - run: make - run: make test - run: sqlc diff From 449d140f5941bf13be39eabd79a86fec4fe3eaec Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 21 Feb 2024 10:26:34 +0100 Subject: [PATCH 15/18] feat: added type annotations --- internal/ast/ast.pb.go | 12 ++++++++++-- internal/gen.go | 33 ++++++++++++++++++++++++++++----- internal/postgresql_type.go | 2 +- internal/printer/printer.go | 4 ++++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/internal/ast/ast.pb.go b/internal/ast/ast.pb.go index bdb4c69..80329d1 100644 --- a/internal/ast/ast.pb.go +++ b/internal/ast/ast.pb.go @@ -647,8 +647,9 @@ type AnnAssign struct { Target *Name `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"` Annotation *Node `protobuf:"bytes,2,opt,name=annotation,proto3" json:"annotation,omitempty"` - Simple int32 `protobuf:"varint,3,opt,name=simple,proto3" json:"simple,omitempty"` - Comment string `protobuf:"bytes,4,opt,name=Comment,json=comment,proto3" json:"Comment,omitempty"` + Value *Node `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` + Simple int32 `protobuf:"varint,4,opt,name=simple,proto3" json:"simple,omitempty"` + Comment string `protobuf:"bytes,5,opt,name=Comment,json=comment,proto3" json:"Comment,omitempty"` } func (x *AnnAssign) Reset() { @@ -697,6 +698,13 @@ func (x *AnnAssign) GetAnnotation() *Node { return nil } +func (x *AnnAssign) GetValue() *Node { + if x != nil { + return x.Value + } + return nil +} + func (x *AnnAssign) GetSimple() int32 { if x != nil { return x.Simple diff --git a/internal/gen.go b/internal/gen.go index 3431c26..1a50aa6 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -702,12 +702,23 @@ func pydanticNode(name string) *pyast.ClassDef { } } -func fieldNode(f Field) *pyast.Node { +func fieldNode(f Field, defaultNone bool) *pyast.Node { + // TODO: Current AST is showing limitation as annotated assign does not support a value :'(, manually edited :'( + var value *pyast.Node = nil + if defaultNone && f.Type.IsNull { + value = &pyast.Node{ + Node: &pyast.Node_Name{ + Name: &pyast.Name{Id: "None"}, + }, + } + } + return &pyast.Node{ Node: &pyast.Node_AnnAssign{ AnnAssign: &pyast.AnnAssign{ Target: &pyast.Name{Id: f.Name}, Annotation: f.Type.Annotation(), + Value: value, Comment: f.Comment, }, }, @@ -825,7 +836,7 @@ func buildModelsTree(ctx *pyTmplCtx, i *importer) *pyast.Node { }) } for _, f := range m.Fields { - def.Body = append(def.Body, fieldNode(f)) + def.Body = append(def.Body, fieldNode(f, false)) } mod.Body = append(mod.Body, &pyast.Node{ Node: &pyast.Node_ClassDef{ @@ -904,6 +915,8 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { } queryText := fmt.Sprintf("-- name: %s \\\\%s\n%s\n", q.MethodName, q.Cmd, q.SQL) mod.Body = append(mod.Body, assignNode(q.ConstantName, poet.Constant(queryText))) + + // Generate params structures for _, arg := range q.Args { if arg.EmitStruct() { var def *pyast.ClassDef @@ -912,8 +925,18 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { } else { def = dataclassNode(arg.Struct.Name) } - for _, f := range arg.Struct.Fields { - def.Body = append(def.Body, fieldNode(f)) + + // We need a copy as we want to make sure that nullable params are at the end of the dataclass + fields := make([]Field, len(arg.Struct.Fields)) + copy(fields, arg.Struct.Fields) + + // Place all nullable fields at the end and try to keep the original order as much as possible + sort.SliceStable(fields, func(i int, j int) bool { + return (fields[j].Type.IsNull && fields[i].Type.IsNull != fields[j].Type.IsNull) || i < j + }) + + for _, f := range fields { + def.Body = append(def.Body, fieldNode(f, true)) } mod.Body = append(mod.Body, poet.Node(def)) } @@ -926,7 +949,7 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { def = dataclassNode(q.Ret.Struct.Name) } for _, f := range q.Ret.Struct.Fields { - def.Body = append(def.Body, fieldNode(f)) + def.Body = append(def.Body, fieldNode(f, false)) } mod.Body = append(mod.Body, poet.Node(def)) } diff --git a/internal/postgresql_type.go b/internal/postgresql_type.go index 0053154..ba72841 100644 --- a/internal/postgresql_type.go +++ b/internal/postgresql_type.go @@ -22,7 +22,7 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { case "json", "jsonb": return "Any" case "bytea", "blob", "pg_catalog.bytea": - return "memoryview" + return "bytes" case "date": return "datetime.date" case "pg_catalog.time", "pg_catalog.timetz": diff --git a/internal/printer/printer.go b/internal/printer/printer.go index e57ad63..b761b54 100644 --- a/internal/printer/printer.go +++ b/internal/printer/printer.go @@ -140,6 +140,10 @@ func (w *writer) printAnnAssign(aa *ast.AnnAssign, indent int32) { w.printName(aa.Target, indent) w.print(": ") w.printNode(aa.Annotation, indent) + if aa.Value != nil { + w.print(" = ") + w.printNode(aa.Value, indent) + } } func (w *writer) printArg(a *ast.Arg, indent int32) { From 3e513130d69348b81c25f47bbb6f72317b9df0ca Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 21 Feb 2024 11:30:30 +0100 Subject: [PATCH 16/18] fix: ensure we are fully backwards compatible --- .gitignore | 1 - internal/config.go | 2 +- internal/endtoend/endtoend_test.go | 3 +- .../testdata/emit_pydantic_models/sqlc.yaml | 2 +- .../endtoend/testdata/exec_result/sqlc.yaml | 2 +- .../endtoend/testdata/exec_rows/sqlc.yaml | 2 +- .../inflection_exclude_table_names/sqlc.yaml | 2 +- .../query_parameter_limit_two/sqlc.yaml | 2 +- .../query_parameter_limit_undefined/sqlc.yaml | 2 +- .../query_parameter_limit_zero/sqlc.yaml | 2 +- .../query_parameter_no_limit/sqlc.yaml | 2 +- internal/gen.go | 212 ++++++++++-------- internal/imports.go | 10 +- 13 files changed, 136 insertions(+), 108 deletions(-) diff --git a/.gitignore b/.gitignore index 0c43e36..70bf0a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ bin -*.wasm # Devenv .envrc diff --git a/internal/config.go b/internal/config.go index e7e1d00..24009a2 100644 --- a/internal/config.go +++ b/internal/config.go @@ -4,7 +4,7 @@ package python type Config struct { EmitAsync bool `json:"emit_async"` // Emits async code instead of sync EmitExactTableNames bool `json:"emit_exact_table_names"` - EmitGenerators bool `json:"emit_generators"` // Will we use generators or lists, defaults to true + EmitGenerators bool `json:"emit_generators"` // Will we use generators or lists, defaults to false EmitModule bool `json:"emit_module"` // If true emits functions in module, else wraps in a class. EmitPydanticModels bool `json:"emit_pydantic_models"` EmitSyncQuerier bool `json:"emit_sync_querier"` // DEPRECATED ALIAS FOR: emit_type = 'class', emit_generators = True diff --git a/internal/endtoend/endtoend_test.go b/internal/endtoend/endtoend_test.go index bd66c27..62c9a8e 100644 --- a/internal/endtoend/endtoend_test.go +++ b/internal/endtoend/endtoend_test.go @@ -100,8 +100,9 @@ func TestGenerate(t *testing.T) { cmd := exec.Command(sqlc, "diff") cmd.Dir = dir got, err := cmd.CombinedOutput() + // TODO: We are diffing patches! Does this make sense and what should we provide to the end user? if diff := cmp.Diff(string(want), string(got)); diff != "" { - t.Errorf("sqlc diff mismatch (-want +got):\n%s", diff) + t.Errorf("sqlc diff mismatch (-want +got):\n%s", string(got)) } if len(want) == 0 && err != nil { t.Error(err) diff --git a/internal/endtoend/testdata/emit_pydantic_models/sqlc.yaml b/internal/endtoend/testdata/emit_pydantic_models/sqlc.yaml index 180ce29..d879adf 100644 --- a/internal/endtoend/testdata/emit_pydantic_models/sqlc.yaml +++ b/internal/endtoend/testdata/emit_pydantic_models/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/exec_result/sqlc.yaml b/internal/endtoend/testdata/exec_result/sqlc.yaml index 2adbd31..b097b32 100644 --- a/internal/endtoend/testdata/exec_result/sqlc.yaml +++ b/internal/endtoend/testdata/exec_result/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/exec_rows/sqlc.yaml b/internal/endtoend/testdata/exec_rows/sqlc.yaml index 2adbd31..b097b32 100644 --- a/internal/endtoend/testdata/exec_rows/sqlc.yaml +++ b/internal/endtoend/testdata/exec_rows/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/inflection_exclude_table_names/sqlc.yaml b/internal/endtoend/testdata/inflection_exclude_table_names/sqlc.yaml index aba5400..7659ffe 100644 --- a/internal/endtoend/testdata/inflection_exclude_table_names/sqlc.yaml +++ b/internal/endtoend/testdata/inflection_exclude_table_names/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/query_parameter_limit_two/sqlc.yaml b/internal/endtoend/testdata/query_parameter_limit_two/sqlc.yaml index e389988..961d105 100644 --- a/internal/endtoend/testdata/query_parameter_limit_two/sqlc.yaml +++ b/internal/endtoend/testdata/query_parameter_limit_two/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/query_parameter_limit_undefined/sqlc.yaml b/internal/endtoend/testdata/query_parameter_limit_undefined/sqlc.yaml index 66d7a14..9bea901 100644 --- a/internal/endtoend/testdata/query_parameter_limit_undefined/sqlc.yaml +++ b/internal/endtoend/testdata/query_parameter_limit_undefined/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/query_parameter_limit_zero/sqlc.yaml b/internal/endtoend/testdata/query_parameter_limit_zero/sqlc.yaml index 274f730..6f3bb75 100644 --- a/internal/endtoend/testdata/query_parameter_limit_zero/sqlc.yaml +++ b/internal/endtoend/testdata/query_parameter_limit_zero/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/endtoend/testdata/query_parameter_no_limit/sqlc.yaml b/internal/endtoend/testdata/query_parameter_no_limit/sqlc.yaml index b563730..d5c4da5 100644 --- a/internal/endtoend/testdata/query_parameter_no_limit/sqlc.yaml +++ b/internal/endtoend/testdata/query_parameter_no_limit/sqlc.yaml @@ -3,7 +3,7 @@ plugins: - name: py wasm: url: file://../../../../bin/sqlc-gen-python.wasm - sha256: "a6c5d174c407007c3717eea36ff0882744346e6ba991f92f71d6ab2895204c0e" + sha256: "c97fad53818679a948c68f3ffe84530d7ca4999f636d3f3d89202c6c08ee224d" sql: - schema: schema.sql queries: query.sql diff --git a/internal/gen.go b/internal/gen.go index 22e40bf..66863ce 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -845,7 +845,7 @@ func querierClassDef(name string, connectionAnnotation *pyast.Node) *pyast.Class Arg: "self", }, { - Arg: "connection", + Arg: "conn", Annotation: connectionAnnotation, }, }, @@ -855,9 +855,9 @@ func querierClassDef(name string, connectionAnnotation *pyast.Node) *pyast.Class Node: &pyast.Node_Assign{ Assign: &pyast.Assign{ Targets: []*pyast.Node{ - poet.Attribute(poet.Name("self"), "_connection"), + poet.Attribute(poet.Name("self"), "_conn"), }, - Value: poet.Name("connection"), + Value: poet.Name("conn"), }, }, }, @@ -869,80 +869,12 @@ func querierClassDef(name string, connectionAnnotation *pyast.Node) *pyast.Class } } -func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { - mod := moduleNode(ctx.SqlcVersion, source) - std, pkg := i.queryImportSpecs(source) - mod.Body = append(mod.Body, buildImportGroup(std), buildImportGroup(pkg)) - mod.Body = append(mod.Body, &pyast.Node{ - Node: &pyast.Node_ImportGroup{ - ImportGroup: &pyast.ImportGroup{ - Imports: []*pyast.Node{ - { - Node: &pyast.Node_ImportFrom{ - ImportFrom: &pyast.ImportFrom{ - Module: ctx.C.Package, - Names: []*pyast.Node{ - poet.Alias("models"), - }, - }, - }, - }, - }, - }, - }, - }) - - for _, q := range ctx.Queries { - if !ctx.OutputQuery(q.SourceName) { - continue - } - queryText := fmt.Sprintf("-- name: %s \\\\%s\n%s\n", q.MethodName, q.Cmd, q.SQL) - mod.Body = append(mod.Body, assignNode(q.ConstantName, poet.Constant(queryText))) - - // Generate params structures - for _, arg := range q.Args { - if arg.EmitStruct() { - var def *pyast.ClassDef - if ctx.C.EmitPydanticModels { - def = pydanticNode(arg.Struct.Name) - } else { - def = dataclassNode(arg.Struct.Name) - } - - // We need a copy as we want to make sure that nullable params are at the end of the dataclass - fields := make([]Field, len(arg.Struct.Fields)) - copy(fields, arg.Struct.Fields) - - // Place all nullable fields at the end and try to keep the original order as much as possible - sort.SliceStable(fields, func(i int, j int) bool { - return (fields[j].Type.IsNull && fields[i].Type.IsNull != fields[j].Type.IsNull) || i < j - }) - - for _, f := range fields { - def.Body = append(def.Body, fieldNode(f, true)) - } - mod.Body = append(mod.Body, poet.Node(def)) - } - } - if q.Ret.EmitStruct() { - var def *pyast.ClassDef - if ctx.C.EmitPydanticModels { - def = pydanticNode(q.Ret.Struct.Name) - } else { - def = dataclassNode(q.Ret.Struct.Name) - } - for _, f := range q.Ret.Struct.Fields { - def.Body = append(def.Body, fieldNode(f, false)) - } - mod.Body = append(mod.Body, poet.Node(def)) - } - } - +func buildQuerierClass(ctx *pyTmplCtx, isAsync bool) []*pyast.Node { functions := make([]*pyast.Node, 0, 10) // Define some reused types based on async or sync code var connectionAnnotation *pyast.Node - if ctx.C.EmitAsync { + if isAsync { connectionAnnotation = typeRefNode("sqlalchemy", "ext", "asyncio", "AsyncConnection") } else { connectionAnnotation = typeRefNode("sqlalchemy", "engine", "Connection") @@ -951,9 +883,9 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { // We need to figure out how to access the SQLAlchemy connectionVar object var connectionVar *pyast.Node if ctx.C.EmitModule { - connectionVar = poet.Name("connection") + connectionVar = poet.Name("conn") } else { - connectionVar = poet.Attribute(poet.Name("self"), "_connection") + connectionVar = poet.Attribute(poet.Name("self"), "_conn") } // We loop through all queries and build our query functions @@ -968,7 +900,7 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { if ctx.C.EmitModule { f.Args.Args = append(f.Args.Args, &pyast.Arg{ - Arg: "connection", + Arg: "conn", Annotation: connectionAnnotation, }) } else { @@ -980,7 +912,7 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { q.AddArgs(f.Args) exec := poet.Expr(connMethodNode(poet.Attribute(connectionVar, "execute"), q.ConstantName, q.ArgDictNode())) - if ctx.C.EmitAsync { + if isAsync { exec = poet.Await(exec) } @@ -1017,7 +949,7 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { f.Returns = subscriptNode("Optional", q.Ret.Annotation()) case ":many": if ctx.C.EmitGenerators { - if ctx.C.EmitAsync { + if isAsync { // If we are using generators and async, we are switching to stream implementation exec = poet.Await(connMethodNode(poet.Attribute(connectionVar, "stream"), q.ConstantName, q.ArgDictNode())) @@ -1094,8 +1026,8 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { panic("unknown cmd " + q.Cmd) } - // If we are emitting async code, we have to swap our sync func for an async one and fix the connection annotation. - if ctx.C.EmitAsync { + // If we are emitting async code, we have to swap our sync func for an async one and fix the conn annotation. + if isAsync { functions = append(functions, poet.Node(&pyast.AsyncFunctionDef{ Name: f.Name, Args: f.Args, @@ -1107,13 +1039,115 @@ func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { } } - // Lets see how to add all functions + return functions +} + +func buildQueryTree(ctx *pyTmplCtx, i *importer, source string) *pyast.Node { + mod := moduleNode(ctx.SqlcVersion, source) + std, pkg := i.queryImportSpecs(source) + mod.Body = append(mod.Body, buildImportGroup(std), buildImportGroup(pkg)) + mod.Body = append(mod.Body, &pyast.Node{ + Node: &pyast.Node_ImportGroup{ + ImportGroup: &pyast.ImportGroup{ + Imports: []*pyast.Node{ + { + Node: &pyast.Node_ImportFrom{ + ImportFrom: &pyast.ImportFrom{ + Module: ctx.C.Package, + Names: []*pyast.Node{ + poet.Alias("models"), + }, + }, + }, + }, + }, + }, + }, + }) + + for _, q := range ctx.Queries { + if !ctx.OutputQuery(q.SourceName) { + continue + } + queryText := fmt.Sprintf("-- name: %s \\\\%s\n%s\n", q.MethodName, q.Cmd, q.SQL) + mod.Body = append(mod.Body, assignNode(q.ConstantName, poet.Constant(queryText))) + + // Generate params structures + for _, arg := range q.Args { + if arg.EmitStruct() { + var def *pyast.ClassDef + if ctx.C.EmitPydanticModels { + def = pydanticNode(arg.Struct.Name) + } else { + def = dataclassNode(arg.Struct.Name) + } + + // We need a copy as we want to make sure that nullable params are at the end of the dataclass + fields := make([]Field, len(arg.Struct.Fields)) + copy(fields, arg.Struct.Fields) + + // Place all nullable fields at the end and try to keep the original order as much as possible + sort.SliceStable(fields, func(i int, j int) bool { + return (fields[j].Type.IsNull && fields[i].Type.IsNull != fields[j].Type.IsNull) || i < j + }) + + for _, f := range fields { + def.Body = append(def.Body, fieldNode(f, true)) + } + mod.Body = append(mod.Body, poet.Node(def)) + } + } + if q.Ret.EmitStruct() { + var def *pyast.ClassDef + if ctx.C.EmitPydanticModels { + def = pydanticNode(q.Ret.Struct.Name) + } else { + def = dataclassNode(q.Ret.Struct.Name) + } + for _, f := range q.Ret.Struct.Fields { + def.Body = append(def.Body, fieldNode(f, false)) + } + mod.Body = append(mod.Body, poet.Node(def)) + } + } + + // Lets see how to add all functions, we can either add them to the module directly or from within a class. if ctx.C.EmitModule { - mod.Body = append(mod.Body, functions...) + mod.Body = append(mod.Body, buildQuerierClass(ctx, ctx.C.EmitAsync)...) } else { - cls := querierClassDef("Querier", connectionAnnotation) - cls.Body = append(cls.Body, functions...) - mod.Body = append(mod.Body, poet.Node(cls)) + asyncConnectionAnnotation := typeRefNode("sqlalchemy", "ext", "asyncio", "AsyncConnection") + syncConnectionAnnotation := typeRefNode("sqlalchemy", "engine", "Connection") + + // NOTE: For backwards compatibility we support generating multiple classes, but this is definitely suboptimal. + // It is much better to use the `emit_async: bool` config to select what type to emit + if ctx.C.EmitAsyncQuerier || ctx.C.EmitSyncQuerier { + + // When using these backwards compatible settings we force behavior! + ctx.C.EmitModule = false + ctx.C.EmitGenerators = true + + if ctx.C.EmitSyncQuerier { + cls := querierClassDef("Querier", syncConnectionAnnotation) + cls.Body = append(cls.Body, buildQuerierClass(ctx, false)...) + mod.Body = append(mod.Body, poet.Node(cls)) + } + if ctx.C.EmitAsyncQuerier { + cls := querierClassDef("AsyncQuerier", asyncConnectionAnnotation) + cls.Body = append(cls.Body, buildQuerierClass(ctx, true)...) + mod.Body = append(mod.Body, poet.Node(cls)) + } + } else { + var connectionAnnotation *pyast.Node + if ctx.C.EmitAsync { + connectionAnnotation = asyncConnectionAnnotation + } else { + connectionAnnotation = syncConnectionAnnotation + } + + cls := querierClassDef("Querier", connectionAnnotation) + cls.Body = append(cls.Body, buildQuerierClass(ctx, ctx.C.EmitAsync)...) + mod.Body = append(mod.Body, poet.Node(cls)) + } } return poet.Node(mod) @@ -1150,14 +1184,6 @@ func Generate(_ context.Context, req *plugin.GenerateRequest) (*plugin.GenerateR } } - // TODO: Remove when when we drop support for deprecated EmitSyncQuerier and EmitAsyncQuerier options - if conf.EmitAsyncQuerier || conf.EmitSyncQuerier { - conf.EmitModule = false - conf.EmitGenerators = true - conf.EmitAsync = conf.EmitAsyncQuerier - // TODO/NOTE: We now have a breaking change because we emit only one flavor. What do we want to do? - } - enums := buildEnums(req) models := buildModels(conf, req) queries, err := buildQueries(conf, req, models) diff --git a/internal/imports.go b/internal/imports.go index c8b2548..bf4da20 100644 --- a/internal/imports.go +++ b/internal/imports.go @@ -132,7 +132,7 @@ func (i *importer) queryImportSpecs(fileName string) (map[string]importSpec, map pkg := make(map[string]importSpec) pkg["sqlalchemy"] = importSpec{Module: "sqlalchemy"} - if i.C.EmitAsync { + if i.C.EmitAsync || i.C.EmitAsyncQuerier { pkg["sqlalchemy.ext.asyncio"] = importSpec{Module: "sqlalchemy.ext.asyncio"} } @@ -154,10 +154,12 @@ func (i *importer) queryImportSpecs(fileName string) (map[string]importSpec, map std["typing.Optional"] = importSpec{Module: "typing", Name: "Optional"} } if q.Cmd == ":many" { - if i.C.EmitGenerators { - if i.C.EmitAsync { + // NOTE: We are adding backwards compatible behavior + if i.C.EmitGenerators || i.C.EmitSyncQuerier || i.C.EmitAsyncQuerier { + if i.C.EmitAsync || i.C.EmitAsyncQuerier { std["typing.AsyncIterator"] = importSpec{Module: "typing", Name: "AsyncIterator"} - } else { + } + if !i.C.EmitAsync || i.C.EmitSyncQuerier { std["typing.Iterator"] = importSpec{Module: "typing", Name: "Iterator"} } } else { From 6efc6246c74181bd7e4415dd59fbe14b48087751 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 21 Feb 2024 11:44:23 +0100 Subject: [PATCH 17/18] fix: updated ast.proto with manual patch for the AnnAssign node --- .gitignore | 2 +- internal/ast/ast.pb.go | 365 +++++++++++++++++++++-------------------- internal/gen.go | 1 - protos/ast/ast.proto | 5 +- 4 files changed, 188 insertions(+), 185 deletions(-) diff --git a/.gitignore b/.gitignore index 70bf0a1..60b4f3d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ bin .envrc .direnv .devenv* -devenv.local.nix \ No newline at end of file +devenv.local.nix diff --git a/internal/ast/ast.pb.go b/internal/ast/ast.pb.go index df9c6d4..5eaa4fa 100644 --- a/internal/ast/ast.pb.go +++ b/internal/ast/ast.pb.go @@ -649,7 +649,7 @@ type AnnAssign struct { Target *Name `protobuf:"bytes,1,opt,name=target,proto3" json:"target,omitempty"` Annotation *Node `protobuf:"bytes,2,opt,name=annotation,proto3" json:"annotation,omitempty"` - Value *Node `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` + Value *Node `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` Simple int32 `protobuf:"varint,4,opt,name=simple,proto3" json:"simple,omitempty"` Comment string `protobuf:"bytes,5,opt,name=Comment,json=comment,proto3" json:"Comment,omitempty"` } @@ -2309,149 +2309,151 @@ var file_ast_ast_proto_rawDesc = []byte{ 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x74, - 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x74, 0x74, 0x72, 0x22, 0x8b, + 0x74, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x74, 0x74, 0x72, 0x22, 0xac, 0x01, 0x0a, 0x09, 0x41, 0x6e, 0x6e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x12, 0x21, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x29, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0a, - 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x69, - 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x69, 0x6d, 0x70, - 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x42, 0x0a, 0x03, - 0x41, 0x72, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x61, 0x72, 0x67, 0x12, 0x29, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, - 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0x55, 0x0a, 0x09, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x0a, - 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x61, 0x73, - 0x74, 0x2e, 0x41, 0x72, 0x67, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x2a, 0x0a, 0x0c, 0x6b, - 0x77, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x08, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x41, 0x72, 0x67, 0x52, 0x0a, 0x6b, 0x77, 0x6f, - 0x6e, 0x6c, 0x79, 0x61, 0x72, 0x67, 0x73, 0x22, 0x6b, 0x0a, 0x08, 0x41, 0x73, 0x79, 0x6e, 0x63, - 0x46, 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, + 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x69, 0x6d, + 0x70, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x42, 0x0a, + 0x03, 0x41, 0x72, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x61, 0x72, 0x67, 0x12, 0x29, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x22, 0x55, 0x0a, 0x09, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, + 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x61, + 0x73, 0x74, 0x2e, 0x41, 0x72, 0x67, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x2a, 0x0a, 0x0c, + 0x6b, 0x77, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x41, 0x72, 0x67, 0x52, 0x0a, 0x6b, 0x77, + 0x6f, 0x6e, 0x6c, 0x79, 0x61, 0x72, 0x67, 0x73, 0x22, 0x6b, 0x0a, 0x08, 0x41, 0x73, 0x79, 0x6e, + 0x63, 0x46, 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x04, 0x69, 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x22, 0x8e, 0x01, 0x0a, 0x10, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x46, 0x75, - 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, - 0x04, 0x41, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x73, - 0x74, 0x2e, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x04, 0x61, 0x72, 0x67, - 0x73, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, - 0x12, 0x23, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x72, 0x65, - 0x74, 0x75, 0x72, 0x6e, 0x73, 0x22, 0x68, 0x0a, 0x06, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x12, - 0x23, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x22, - 0x6e, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x1d, 0x0a, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, - 0x52, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x12, 0x1d, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x02, + 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x04, 0x69, 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x28, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4b, 0x65, - 0x79, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x22, - 0xb8, 0x01, 0x0a, 0x08, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x44, 0x65, 0x66, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x1f, 0x0a, 0x05, 0x62, 0x61, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x62, 0x61, 0x73, 0x65, - 0x73, 0x12, 0x25, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x08, - 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, - 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, - 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x31, 0x0a, 0x0e, 0x64, 0x65, 0x63, 0x6f, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0e, 0x64, 0x65, 0x63, 0x6f, - 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x22, 0x1d, 0x0a, 0x07, 0x43, 0x6f, - 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x72, 0x0a, 0x07, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x04, 0x6c, 0x65, 0x66, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6c, - 0x65, 0x66, 0x74, 0x12, 0x1b, 0x0a, 0x03, 0x6f, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x03, 0x6f, 0x70, 0x73, - 0x12, 0x2b, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, - 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x54, 0x0a, - 0x08, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x12, 0x15, 0x0a, 0x03, 0x73, 0x74, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x12, 0x12, 0x0a, 0x03, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, - 0x03, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x22, 0x48, 0x0a, 0x04, 0x44, 0x69, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x6b, - 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, - 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x21, 0x0a, 0x06, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x27, 0x0a, - 0x04, 0x45, 0x78, 0x70, 0x72, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, + 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x8e, 0x01, 0x0a, 0x10, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x46, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x22, + 0x0a, 0x04, 0x41, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, + 0x73, 0x74, 0x2e, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x04, 0x61, 0x72, + 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x12, 0x23, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x22, 0x68, 0x0a, 0x06, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, + 0x12, 0x23, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x66, 0x0a, 0x03, 0x46, 0x6f, 0x72, 0x12, 0x21, 0x0a, - 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, - 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x12, 0x1d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, - 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x72, 0x12, - 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, - 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x89, - 0x01, 0x0a, 0x0b, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x04, 0x41, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, + 0x22, 0x6e, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x1d, 0x0a, 0x04, 0x66, 0x75, 0x6e, 0x63, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x52, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x12, 0x1d, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x28, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, + 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4b, + 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x73, + 0x22, 0xb8, 0x01, 0x0a, 0x08, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x44, 0x65, 0x66, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x62, 0x61, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x62, 0x61, 0x73, + 0x65, 0x73, 0x12, 0x25, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x23, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, - 0x65, 0x52, 0x07, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x22, 0x66, 0x0a, 0x02, 0x49, 0x66, - 0x12, 0x1d, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, - 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x74, 0x65, 0x73, 0x74, 0x12, - 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, - 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x22, - 0x0a, 0x07, 0x6f, 0x72, 0x5f, 0x65, 0x6c, 0x73, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x6f, 0x72, 0x65, 0x6c, - 0x73, 0x65, 0x22, 0x29, 0x0a, 0x06, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1f, 0x0a, 0x05, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, - 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5b, 0x0a, - 0x0a, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x6d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x32, 0x0a, 0x0b, 0x49, 0x6d, - 0x70, 0x6f, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x23, 0x0a, 0x07, 0x69, 0x6d, 0x70, - 0x6f, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x22, 0x04, - 0x0a, 0x02, 0x49, 0x73, 0x22, 0x3c, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x12, - 0x10, 0x0a, 0x03, 0x61, 0x72, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x72, - 0x67, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x22, 0x27, 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x16, 0x0a, 0x04, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x61, 0x73, 0x73, 0x22, 0x29, 0x0a, 0x06, 0x52, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, + 0x08, 0x6b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x31, 0x0a, 0x0e, 0x64, 0x65, 0x63, 0x6f, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x0e, 0x64, 0x65, 0x63, + 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x22, 0x1d, 0x0a, 0x07, 0x43, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x72, 0x0a, 0x07, 0x43, 0x6f, + 0x6d, 0x70, 0x61, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x04, 0x6c, 0x65, 0x66, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, + 0x6c, 0x65, 0x66, 0x74, 0x12, 0x1b, 0x0a, 0x03, 0x6f, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x03, 0x6f, 0x70, + 0x73, 0x12, 0x2b, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x54, + 0x0a, 0x08, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x12, 0x15, 0x0a, 0x03, 0x73, 0x74, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x12, 0x12, 0x0a, 0x03, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, + 0x52, 0x03, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x22, 0x48, 0x0a, 0x04, 0x44, 0x69, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x04, + 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x21, 0x0a, 0x06, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, + 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x27, + 0x0a, 0x04, 0x45, 0x78, 0x70, 0x72, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x66, 0x0a, 0x03, 0x46, 0x6f, 0x72, 0x12, 0x21, + 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, + 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x69, 0x74, 0x65, 0x72, + 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, + 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, + 0x89, 0x01, 0x0a, 0x0b, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x04, 0x41, 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x23, 0x0a, 0x07, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, + 0x64, 0x65, 0x52, 0x07, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x22, 0x66, 0x0a, 0x02, 0x49, + 0x66, 0x12, 0x1d, 0x0a, 0x04, 0x74, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x74, 0x65, 0x73, 0x74, + 0x12, 0x1d, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, + 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, + 0x22, 0x0a, 0x07, 0x6f, 0x72, 0x5f, 0x65, 0x6c, 0x73, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x6f, 0x72, 0x65, + 0x6c, 0x73, 0x65, 0x22, 0x29, 0x0a, 0x06, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1f, 0x0a, + 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, + 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x5b, + 0x0a, 0x0a, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, + 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x32, 0x0a, 0x0b, 0x49, + 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x23, 0x0a, 0x07, 0x69, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, + 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x22, + 0x04, 0x0a, 0x02, 0x49, 0x73, 0x22, 0x3c, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x72, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, + 0x72, 0x67, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0x27, 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, + 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, + 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x16, 0x0a, 0x04, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x22, 0x06, 0x0a, 0x04, 0x50, 0x61, 0x73, 0x73, 0x22, 0x29, 0x0a, 0x06, + 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x12, 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x05, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, - 0x73, 0x6c, 0x69, 0x63, 0x65, 0x22, 0x28, 0x0a, 0x05, 0x59, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x1f, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, - 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, - 0x71, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x73, 0x74, 0x42, 0x08, 0x41, 0x73, 0x74, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x73, 0x71, 0x6c, 0x63, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x73, 0x71, 0x6c, 0x63, - 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x2f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x61, 0x73, 0x74, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, - 0x03, 0x41, 0x73, 0x74, 0xca, 0x02, 0x03, 0x41, 0x73, 0x74, 0xe2, 0x02, 0x0f, 0x41, 0x73, 0x74, - 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x03, 0x41, - 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x05, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x22, 0x28, 0x0a, 0x05, 0x59, 0x69, 0x65, 0x6c, 0x64, 0x12, + 0x1f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, + 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x42, 0x71, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x73, 0x74, 0x42, 0x08, 0x41, 0x73, 0x74, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x71, 0x6c, 0x63, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x73, 0x71, 0x6c, + 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x70, 0x79, 0x74, 0x68, 0x6f, 0x6e, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x61, 0x73, 0x74, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, + 0x02, 0x03, 0x41, 0x73, 0x74, 0xca, 0x02, 0x03, 0x41, 0x73, 0x74, 0xe2, 0x02, 0x0f, 0x41, 0x73, + 0x74, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x03, + 0x41, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2535,53 +2537,54 @@ var file_ast_ast_proto_depIdxs = []int32{ 0, // 31: ast.Attribute.value:type_name -> ast.Node 26, // 32: ast.AnnAssign.target:type_name -> ast.Name 0, // 33: ast.AnnAssign.annotation:type_name -> ast.Node - 0, // 34: ast.Arg.annotation:type_name -> ast.Node - 5, // 35: ast.Arguments.args:type_name -> ast.Arg - 5, // 36: ast.Arguments.kw_only_args:type_name -> ast.Arg - 0, // 37: ast.AsyncFor.target:type_name -> ast.Node - 0, // 38: ast.AsyncFor.iter:type_name -> ast.Node - 0, // 39: ast.AsyncFor.body:type_name -> ast.Node - 6, // 40: ast.AsyncFunctionDef.Args:type_name -> ast.Arguments - 0, // 41: ast.AsyncFunctionDef.body:type_name -> ast.Node - 0, // 42: ast.AsyncFunctionDef.returns:type_name -> ast.Node - 0, // 43: ast.Assign.targets:type_name -> ast.Node - 0, // 44: ast.Assign.value:type_name -> ast.Node - 0, // 45: ast.Call.func:type_name -> ast.Node - 0, // 46: ast.Call.args:type_name -> ast.Node - 24, // 47: ast.Call.keywords:type_name -> ast.Keyword - 0, // 48: ast.ClassDef.bases:type_name -> ast.Node - 0, // 49: ast.ClassDef.keywords:type_name -> ast.Node - 0, // 50: ast.ClassDef.body:type_name -> ast.Node - 0, // 51: ast.ClassDef.decorator_list:type_name -> ast.Node - 0, // 52: ast.Compare.left:type_name -> ast.Node - 0, // 53: ast.Compare.ops:type_name -> ast.Node - 0, // 54: ast.Compare.comparators:type_name -> ast.Node - 0, // 55: ast.Dict.keys:type_name -> ast.Node - 0, // 56: ast.Dict.values:type_name -> ast.Node - 0, // 57: ast.Expr.value:type_name -> ast.Node - 0, // 58: ast.For.target:type_name -> ast.Node - 0, // 59: ast.For.iter:type_name -> ast.Node - 0, // 60: ast.For.body:type_name -> ast.Node - 6, // 61: ast.FunctionDef.Args:type_name -> ast.Arguments - 0, // 62: ast.FunctionDef.body:type_name -> ast.Node - 0, // 63: ast.FunctionDef.returns:type_name -> ast.Node - 0, // 64: ast.If.test:type_name -> ast.Node - 0, // 65: ast.If.body:type_name -> ast.Node - 0, // 66: ast.If.or_else:type_name -> ast.Node - 0, // 67: ast.Import.names:type_name -> ast.Node - 0, // 68: ast.ImportFrom.names:type_name -> ast.Node - 0, // 69: ast.ImportGroup.imports:type_name -> ast.Node - 0, // 70: ast.Keyword.value:type_name -> ast.Node - 0, // 71: ast.Module.body:type_name -> ast.Node - 0, // 72: ast.Return.value:type_name -> ast.Node - 26, // 73: ast.Subscript.value:type_name -> ast.Name - 0, // 74: ast.Subscript.slice:type_name -> ast.Node - 0, // 75: ast.Yield.value:type_name -> ast.Node - 76, // [76:76] is the sub-list for method output_type - 76, // [76:76] is the sub-list for method input_type - 76, // [76:76] is the sub-list for extension type_name - 76, // [76:76] is the sub-list for extension extendee - 0, // [0:76] is the sub-list for field type_name + 0, // 34: ast.AnnAssign.value:type_name -> ast.Node + 0, // 35: ast.Arg.annotation:type_name -> ast.Node + 5, // 36: ast.Arguments.args:type_name -> ast.Arg + 5, // 37: ast.Arguments.kw_only_args:type_name -> ast.Arg + 0, // 38: ast.AsyncFor.target:type_name -> ast.Node + 0, // 39: ast.AsyncFor.iter:type_name -> ast.Node + 0, // 40: ast.AsyncFor.body:type_name -> ast.Node + 6, // 41: ast.AsyncFunctionDef.Args:type_name -> ast.Arguments + 0, // 42: ast.AsyncFunctionDef.body:type_name -> ast.Node + 0, // 43: ast.AsyncFunctionDef.returns:type_name -> ast.Node + 0, // 44: ast.Assign.targets:type_name -> ast.Node + 0, // 45: ast.Assign.value:type_name -> ast.Node + 0, // 46: ast.Call.func:type_name -> ast.Node + 0, // 47: ast.Call.args:type_name -> ast.Node + 24, // 48: ast.Call.keywords:type_name -> ast.Keyword + 0, // 49: ast.ClassDef.bases:type_name -> ast.Node + 0, // 50: ast.ClassDef.keywords:type_name -> ast.Node + 0, // 51: ast.ClassDef.body:type_name -> ast.Node + 0, // 52: ast.ClassDef.decorator_list:type_name -> ast.Node + 0, // 53: ast.Compare.left:type_name -> ast.Node + 0, // 54: ast.Compare.ops:type_name -> ast.Node + 0, // 55: ast.Compare.comparators:type_name -> ast.Node + 0, // 56: ast.Dict.keys:type_name -> ast.Node + 0, // 57: ast.Dict.values:type_name -> ast.Node + 0, // 58: ast.Expr.value:type_name -> ast.Node + 0, // 59: ast.For.target:type_name -> ast.Node + 0, // 60: ast.For.iter:type_name -> ast.Node + 0, // 61: ast.For.body:type_name -> ast.Node + 6, // 62: ast.FunctionDef.Args:type_name -> ast.Arguments + 0, // 63: ast.FunctionDef.body:type_name -> ast.Node + 0, // 64: ast.FunctionDef.returns:type_name -> ast.Node + 0, // 65: ast.If.test:type_name -> ast.Node + 0, // 66: ast.If.body:type_name -> ast.Node + 0, // 67: ast.If.or_else:type_name -> ast.Node + 0, // 68: ast.Import.names:type_name -> ast.Node + 0, // 69: ast.ImportFrom.names:type_name -> ast.Node + 0, // 70: ast.ImportGroup.imports:type_name -> ast.Node + 0, // 71: ast.Keyword.value:type_name -> ast.Node + 0, // 72: ast.Module.body:type_name -> ast.Node + 0, // 73: ast.Return.value:type_name -> ast.Node + 26, // 74: ast.Subscript.value:type_name -> ast.Name + 0, // 75: ast.Subscript.slice:type_name -> ast.Node + 0, // 76: ast.Yield.value:type_name -> ast.Node + 77, // [77:77] is the sub-list for method output_type + 77, // [77:77] is the sub-list for method input_type + 77, // [77:77] is the sub-list for extension type_name + 77, // [77:77] is the sub-list for extension extendee + 0, // [0:77] is the sub-list for field type_name } func init() { file_ast_ast_proto_init() } diff --git a/internal/gen.go b/internal/gen.go index 66863ce..d9ae54d 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -686,7 +686,6 @@ func pydanticNode(name string) *pyast.ClassDef { } func fieldNode(f Field, defaultNone bool) *pyast.Node { - // TODO: Current AST is showing limitation as annotated assign does not support a value :'(, manually edited :'( var value *pyast.Node = nil if defaultNone && f.Type.IsNull { value = &pyast.Node{ diff --git a/protos/ast/ast.proto b/protos/ast/ast.proto index a8daa62..4188f36 100644 --- a/protos/ast/ast.proto +++ b/protos/ast/ast.proto @@ -59,8 +59,9 @@ message AnnAssign { Name target = 1 [json_name="target"]; Node annotation = 2 [json_name="annotation"]; - int32 simple = 3 [json_name="simple"]; - string Comment = 4 [json_name="comment"]; + Node value = 3 [json_name="value"]; + int32 simple = 4 [json_name="simple"]; + string Comment = 5 [json_name="comment"]; } message Arg From 3d5cd342d23ae143ed892170ffecda20ff9a1958 Mon Sep 17 00:00:00 2001 From: Kevin Valk Date: Wed, 21 Feb 2024 12:05:34 +0100 Subject: [PATCH 18/18] feat: automatic release of the plugin on tagging --- .github/workflows/ci.yml | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6542dc3..f6f780e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,8 @@ name: go on: push: + tags: + - v* branches: - main pull_request: @@ -22,7 +24,30 @@ jobs: - run: sqlc diff working-directory: examples - - uses: actions/upload-artifact@v3 + - name: Generate checksum + id: checksum + run: |- + echo "sha256=$(sha256sum bin/sqlc-gen-python.wasm | awk '{ print $1 }')" >> $GITHUB_OUTPUT + + - uses: actions/upload-artifact@v4 with: name: sqlc-gen-python - path: plugin/sqlc-gen-python.wasm \ No newline at end of file + path: bin/sqlc-gen-python.wasm + + - name: Release the build on tag + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + body: | + # Configuration + ```yaml + version: '2' + plugins: + - name: py + wasm: + url: ${{ github.server_url }}/${{ github.repository}}/releases/download/${{ github.ref_name }}/sqlc-gen-python.wasm + sha256: ${{ steps.checksum.outputs.sha256 }} + ``` + generate_release_notes: true + files: | + bin/sqlc-gen-python.wasm \ No newline at end of file pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy