Skip to content

Commit 1e40dc8

Browse files
authored
Python query param limit (#1530)
* python query param limit support * refactor DefaultQueryParameterLimit update pluginPythonCode unexported default limit * refactor to optional query_parameter_limit proto * 0 has no limit, <0 invalid * revert to default python qpl of 4
1 parent 7331b9d commit 1e40dc8

File tree

25 files changed

+555
-188
lines changed

25 files changed

+555
-188
lines changed

examples/python/sqlc.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"out": "src/authors",
1111
"package": "authors",
1212
"emit_sync_querier": true,
13-
"emit_async_querier": true
13+
"emit_async_querier": true,
14+
"query_parameter_limit": 5
1415
}
1516
}
1617
},
@@ -22,7 +23,8 @@
2223
"python": {
2324
"out": "src/booktest",
2425
"package": "booktest",
25-
"emit_async_querier": true
26+
"emit_async_querier": true,
27+
"query_parameter_limit": 5
2628
}
2729
}
2830
},
@@ -34,7 +36,8 @@
3436
"python": {
3537
"out": "src/jets",
3638
"package": "jets",
37-
"emit_async_querier": true
39+
"emit_async_querier": true,
40+
"query_parameter_limit": 5
3841
}
3942
}
4043
},
@@ -46,7 +49,8 @@
4649
"python": {
4750
"out": "src/ondeck",
4851
"package": "ondeck",
49-
"emit_async_querier": true
52+
"emit_async_querier": true,
53+
"query_parameter_limit": 5
5054
}
5155
}
5256
}

internal/cmd/shim.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ func pluginPythonCode(s config.SQLPython) *plugin.PythonCode {
8585
EmitSyncQuerier: s.EmitSyncQuerier,
8686
EmitAsyncQuerier: s.EmitAsyncQuerier,
8787
EmitPydanticModels: s.EmitPydanticModels,
88+
QueryParameterLimit: s.QueryParameterLimit,
8889
}
8990
}
9091

internal/codegen/python/gen.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,11 @@ func buildQueries(req *plugin.CodeGenRequest, structs []Struct) ([]Query, error)
400400
SourceName: query.Filename,
401401
}
402402

403-
if len(query.Params) > 4 {
403+
qpl := 4
404+
if req.Settings.Python.QueryParameterLimit != nil {
405+
qpl = int(*req.Settings.Python.QueryParameterLimit)
406+
}
407+
if len(query.Params) > qpl || qpl == 0 {
404408
var cols []pyColumn
405409
for _, p := range query.Params {
406410
cols = append(cols, pyColumn{

internal/config/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ type SQLPython struct {
169169
Out string `json:"out" yaml:"out"`
170170
Overrides []Override `json:"overrides,omitempty" yaml:"overrides"`
171171
EmitPydanticModels bool `json:"emit_pydantic_models,omitempty" yaml:"emit_pydantic_models"`
172+
QueryParameterLimit *int32 `json:"query_parameter_limit,omitempty" yaml:"query_parameter_limit"`
172173
}
173174

174175
type SQLJSON struct {
@@ -195,6 +196,8 @@ var ErrPluginNoType = errors.New("plugin: field `process` or `wasm` required")
195196
var ErrPluginBothTypes = errors.New("plugin: both `process` and `wasm` cannot both be defined")
196197
var ErrPluginProcessNoCmd = errors.New("plugin: missing process command")
197198

199+
var ErrInvalidQueryParameterLimit = errors.New("invalid query parameter limit")
200+
198201
func ParseConfig(rd io.Reader) (Config, error) {
199202
var buf bytes.Buffer
200203
var config Config

internal/config/v_one.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ func v1ParseConfig(rd io.Reader) (Config, error) {
8484
if settings.Packages[j].Engine == "" {
8585
settings.Packages[j].Engine = EnginePostgreSQL
8686
}
87+
8788
}
89+
8890
return settings.Translate(), nil
8991
}
9092

internal/config/v_two.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ func v2ParseConfig(rd io.Reader) (Config, error) {
3636
}
3737
// TODO: Store built-in plugins somewhere else
3838
builtins := map[string]struct{}{
39-
"go": struct{}{},
40-
"json": struct{}{},
41-
"kotlin": struct{}{},
42-
"python": struct{}{},
39+
"go": {},
40+
"json": {},
41+
"kotlin": {},
42+
"python": {},
4343
}
4444
plugins := map[string]struct{}{}
4545
for i := range conf.Plugins {
@@ -91,6 +91,11 @@ func v2ParseConfig(rd io.Reader) (Config, error) {
9191
}
9292
}
9393
if conf.SQL[j].Gen.Python != nil {
94+
if conf.SQL[j].Gen.Python.QueryParameterLimit != nil {
95+
if *conf.SQL[j].Gen.Python.QueryParameterLimit < 0 {
96+
return conf, ErrInvalidQueryParameterLimit
97+
}
98+
}
9499
if conf.SQL[j].Gen.Python.Out == "" {
95100
return conf, ErrNoOutPath
96101
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CREATE TABLE bar (id serial not null, name text not null, primary key (id));
2+
3+
-- name: DeleteBarByID :execrows
4+
DELETE FROM bar WHERE id = $1;
5+
6+
-- name: DeleteBarByIDAndName :execrows
7+
DELETE FROM bar WHERE id = $1 AND name = $2;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "2",
3+
"sql": [
4+
{
5+
"schema": "query.sql",
6+
"queries": "query.sql",
7+
"engine": "postgresql",
8+
"gen": {
9+
"python": {
10+
"out": "python",
11+
"package": "querytest",
12+
"emit_sync_querier": true,
13+
"emit_async_querier": true,
14+
"query_parameter_limit": -1
15+
}
16+
}
17+
}
18+
]
19+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
error parsing sqlc.json: invalid query parameter limit
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Code generated by sqlc. DO NOT EDIT.
2+
# versions:
3+
# sqlc v1.15.0
4+
import dataclasses
5+
6+
7+
@dataclasses.dataclass()
8+
class Bar:
9+
id: int
10+
name: str
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Code generated by sqlc. DO NOT EDIT.
2+
# versions:
3+
# sqlc v1.15.0
4+
# source: query.sql
5+
import dataclasses
6+
7+
import sqlalchemy
8+
import sqlalchemy.ext.asyncio
9+
10+
from querytest import models
11+
12+
13+
DELETE_BAR_BY_ID = """-- name: delete_bar_by_id \\:execrows
14+
DELETE FROM bar WHERE id = :p1
15+
"""
16+
17+
18+
@dataclasses.dataclass()
19+
class DeleteBarByIDParams:
20+
id: int
21+
22+
23+
DELETE_BAR_BY_ID_AND_NAME = """-- name: delete_bar_by_id_and_name \\:execrows
24+
DELETE FROM bar WHERE id = :p1 AND name = :p2
25+
"""
26+
27+
28+
@dataclasses.dataclass()
29+
class DeleteBarByIDAndNameParams:
30+
id: int
31+
name: str
32+
33+
34+
class Querier:
35+
def __init__(self, conn: sqlalchemy.engine.Connection):
36+
self._conn = conn
37+
38+
def delete_bar_by_id(self, arg: DeleteBarByIDParams) -> int:
39+
result = self._conn.execute(sqlalchemy.text(DELETE_BAR_BY_ID), {"p1": arg.id})
40+
return result.rowcount
41+
42+
def delete_bar_by_id_and_name(self, arg: DeleteBarByIDAndNameParams) -> int:
43+
result = self._conn.execute(sqlalchemy.text(DELETE_BAR_BY_ID_AND_NAME), {"p1": arg.id, "p2": arg.name})
44+
return result.rowcount
45+
46+
47+
class AsyncQuerier:
48+
def __init__(self, conn: sqlalchemy.ext.asyncio.AsyncConnection):
49+
self._conn = conn
50+
51+
async def delete_bar_by_id(self, arg: DeleteBarByIDParams) -> int:
52+
result = await self._conn.execute(sqlalchemy.text(DELETE_BAR_BY_ID), {"p1": arg.id})
53+
return result.rowcount
54+
55+
async def delete_bar_by_id_and_name(self, arg: DeleteBarByIDAndNameParams) -> int:
56+
result = await self._conn.execute(sqlalchemy.text(DELETE_BAR_BY_ID_AND_NAME), {"p1": arg.id, "p2": arg.name})
57+
return result.rowcount
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CREATE TABLE bar (id serial not null, name text not null, primary key (id));
2+
3+
-- name: DeleteBarByID :execrows
4+
DELETE FROM bar WHERE id = $1;
5+
6+
-- name: DeleteBarByIDAndName :execrows
7+
DELETE FROM bar WHERE id = $1 AND name = $2;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "2",
3+
"sql": [
4+
{
5+
"schema": "query.sql",
6+
"queries": "query.sql",
7+
"engine": "postgresql",
8+
"gen": {
9+
"python": {
10+
"out": "python",
11+
"package": "querytest",
12+
"emit_sync_querier": true,
13+
"emit_async_querier": true,
14+
"query_parameter_limit": 0
15+
}
16+
}
17+
}
18+
]
19+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Code generated by sqlc. DO NOT EDIT.
2+
# versions:
3+
# sqlc v1.15.0
4+
import dataclasses
5+
6+
7+
@dataclasses.dataclass()
8+
class Bar:
9+
id: int
10+
name: str
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Code generated by sqlc. DO NOT EDIT.
2+
# versions:
3+
# sqlc v1.15.0
4+
# source: query.sql
5+
import sqlalchemy
6+
import sqlalchemy.ext.asyncio
7+
8+
from querytest import models
9+
10+
11+
DELETE_BAR_BY_ID = """-- name: delete_bar_by_id \\:execrows
12+
DELETE FROM bar WHERE id = :p1
13+
"""
14+
15+
16+
DELETE_BAR_BY_ID_AND_NAME = """-- name: delete_bar_by_id_and_name \\:execrows
17+
DELETE FROM bar WHERE id = :p1 AND name = :p2
18+
"""
19+
20+
21+
class Querier:
22+
def __init__(self, conn: sqlalchemy.engine.Connection):
23+
self._conn = conn
24+
25+
def delete_bar_by_id(self, *, id: int) -> int:
26+
result = self._conn.execute(sqlalchemy.text(DELETE_BAR_BY_ID), {"p1": id})
27+
return result.rowcount
28+
29+
def delete_bar_by_id_and_name(self, *, id: int, name: str) -> int:
30+
result = self._conn.execute(sqlalchemy.text(DELETE_BAR_BY_ID_AND_NAME), {"p1": id, "p2": name})
31+
return result.rowcount
32+
33+
34+
class AsyncQuerier:
35+
def __init__(self, conn: sqlalchemy.ext.asyncio.AsyncConnection):
36+
self._conn = conn
37+
38+
async def delete_bar_by_id(self, *, id: int) -> int:
39+
result = await self._conn.execute(sqlalchemy.text(DELETE_BAR_BY_ID), {"p1": id})
40+
return result.rowcount
41+
42+
async def delete_bar_by_id_and_name(self, *, id: int, name: str) -> int:
43+
result = await self._conn.execute(sqlalchemy.text(DELETE_BAR_BY_ID_AND_NAME), {"p1": id, "p2": name})
44+
return result.rowcount
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CREATE TABLE bar (id serial not null, name text not null, primary key (id));
2+
3+
-- name: DeleteBarByID :execrows
4+
DELETE FROM bar WHERE id = $1;
5+
6+
-- name: DeleteBarByIDAndName :execrows
7+
DELETE FROM bar WHERE id = $1 AND name = $2;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "2",
3+
"sql": [
4+
{
5+
"schema": "query.sql",
6+
"queries": "query.sql",
7+
"engine": "postgresql",
8+
"gen": {
9+
"python": {
10+
"out": "python",
11+
"package": "querytest",
12+
"emit_sync_querier": true,
13+
"emit_async_querier": true,
14+
"query_parameter_limit": 2
15+
}
16+
}
17+
}
18+
]
19+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Code generated by sqlc. DO NOT EDIT.
2+
# versions:
3+
# sqlc v1.15.0
4+
import dataclasses
5+
6+
7+
@dataclasses.dataclass()
8+
class Bar:
9+
id: int
10+
name1: str
11+
name2: str
12+
name3: str
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Code generated by sqlc. DO NOT EDIT.
2+
# versions:
3+
# sqlc v1.15.0
4+
# source: query.sql
5+
import sqlalchemy
6+
import sqlalchemy.ext.asyncio
7+
8+
from querytest import models
9+
10+
11+
DELETE_BAR_BY_ID = """-- name: delete_bar_by_id \\:execrows
12+
DELETE FROM bar WHERE id = :p1
13+
"""
14+
15+
16+
DELETE_BAR_BY_ID_AND_NAME = """-- name: delete_bar_by_id_and_name \\:execrows
17+
DELETE FROM bar
18+
WHERE id = :p1
19+
AND name1 = :p2
20+
AND name2 = :p3
21+
AND name3 = :p4
22+
"""
23+
24+
25+
class Querier:
26+
def __init__(self, conn: sqlalchemy.engine.Connection):
27+
self._conn = conn
28+
29+
def delete_bar_by_id(self, *, id: int) -> int:
30+
result = self._conn.execute(sqlalchemy.text(DELETE_BAR_BY_ID), {"p1": id})
31+
return result.rowcount
32+
33+
def delete_bar_by_id_and_name(self, *, id: int, name1: str, name2: str, name3: str) -> int:
34+
result = self._conn.execute(sqlalchemy.text(DELETE_BAR_BY_ID_AND_NAME), {
35+
"p1": id,
36+
"p2": name1,
37+
"p3": name2,
38+
"p4": name3,
39+
})
40+
return result.rowcount
41+
42+
43+
class AsyncQuerier:
44+
def __init__(self, conn: sqlalchemy.ext.asyncio.AsyncConnection):
45+
self._conn = conn
46+
47+
async def delete_bar_by_id(self, *, id: int) -> int:
48+
result = await self._conn.execute(sqlalchemy.text(DELETE_BAR_BY_ID), {"p1": id})
49+
return result.rowcount
50+
51+
async def delete_bar_by_id_and_name(self, *, id: int, name1: str, name2: str, name3: str) -> int:
52+
result = await self._conn.execute(sqlalchemy.text(DELETE_BAR_BY_ID_AND_NAME), {
53+
"p1": id,
54+
"p2": name1,
55+
"p3": name2,
56+
"p4": name3,
57+
})
58+
return result.rowcount

0 commit comments

Comments
 (0)