Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: mysql support #25

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
23 changes: 23 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
MIT License

Copyright (c) 2023 zztkm

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


Below is the original software license.

Copyright (c) 2022 Kyle Conroy

Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down
46 changes: 40 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,42 @@
all: sqlc-gen-python sqlc-gen-python.wasm
VERSION := $$(make -s show-version)
CURRENT_REVISION := $(shell git rev-parse --short HEAD)
BUILD_LDFLAGS := "-s -w -X main.revision=$(CURRENT_REVISION)"
GOBIN ?= $(shell go env GOPATH)/bin

sqlc-gen-python:
cd plugin && go build -o ~/bin/sqlc-gen-python ./main.go
.PHONY: show-version
show-version: $(GOBIN)/gobump
@gobump show -r cmd/sqlc-gen-python-orm

sqlc-gen-python.wasm:
cd plugin && GOOS=wasip1 GOARCH=wasm go build -o sqlc-gen-python.wasm main.go
openssl sha256 plugin/sqlc-gen-python.wasm
$(GOBIN)/gobump:
@go install github.com/x-motemen/gobump/cmd/gobump@latest

.PHONY: compile
compile:
sqlc compile

.PHONY: generate
generate: sqlc.yaml
sqlc generate


.PHONY: release
release: dist/sqlc-gen-python-orm.wasm dist/sqlc-gen-python-orm.wasm.sha256
gh release create "v${VERSION}" dist/sqlc-gen-python-orm.wasm dist/sqlc-gen-python-orm.wasm.sha256

.PHONY: release
release-overwrite: dist/sqlc-gen-python-orm.wasm dist/sqlc-gen-python-orm.wasm.sha256
gh release delete -y --cleanup-tag "v${VERSION}"
gh release create "v${VERSION}" dist/sqlc-gen-python-orm.wasm dist/sqlc-gen-python-orm.wasm.sha256

.PHONY: clean
clean:
rm -rf ./_examples/gen

sqlc.yaml: dist/sqlc-gen-python-orm.wasm.sha256 _sqlc.yaml
cat _sqlc.yaml | WASM_SHA256=$$(cat $<) envsubst > $@

dist/sqlc-gen-python-orm.wasm.sha256: dist/sqlc-gen-python-orm.wasm
openssl sha256 $< | awk '{print $$2}' > $@

dist/sqlc-gen-python-orm.wasm: internal/*
GOOS=wasip1 GOARCH=wasm go build -o $@ ./cmd/sqlc-gen-python-orm/main.go
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
# sqlc-gen-python-orm

sqlc-gen-python-orm is a plugin for [sqlc](https://sqlc.dev/) that generates an ORM (now, support SQLAlchemy only) for Python.

This softwaer forked from [sqlc-gen-python](https://github.com/sqlc-dev/sqlc-gen-python) and modified to generate ORM.

## Usage

get sha256 hash of wasm file

```bash
curl -sSL https://github.com/veltiosoft/sqlc-gen-python-orm/releases/download/v0.0.1/sqlc-gen-python-orm.wasm.sha256
```

add plugin to sqlc.yaml
```yaml
version: '2'
plugins:
- name: py
wasm:
url: https://downloads.sqlc.dev/plugin/sqlc-gen-python_1.1.0.wasm
sha256: ef58f143a8c116781091441770c7166caaf361dd645f62b8f05f462e9f95c3b2
url: https://github.com/veltiosoft/sqlc-gen-python-orm/releases/download/v0.0.1/sqlc-gen-python-orm.wasm
sha256: <sha256 hash>
sql:
- schema: "schema.sql"
queries: "query.sql"
Expand All @@ -15,7 +28,11 @@ sql:
- out: src/authors
plugin: py
options:
package: authors
package: .
emit_sync_querier: true
emit_async_querier: true
```

## Refs

- [sqlc plugin を書こう - 薄いブログ](https://orisano.hatenablog.com/entry/2023/09/06/010926)
13 changes: 13 additions & 0 deletions _examples/gen/sqlc/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Code generated by sqlc. DO NOT EDIT.
# versions:
# sqlc v1.20.0
import pydantic
from typing import Optional


class Author(pydantic.BaseModel):
id: int
name: str
age: int
bio: Optional[str]
is_active: bool
18 changes: 18 additions & 0 deletions _examples/gen/sqlc/orm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Code generated by sqlc. DO NOT EDIT.
# versions:
# sqlc v1.20.0
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from typing import Optional


class Base(DeclarativeBase):
pass


class Author(Base):
__tablename__ = "authors"
id: Mapped[int]
name: Mapped[str]
age: Mapped[int]
bio: Mapped[Optional[str]]
is_active: Mapped[bool]
198 changes: 198 additions & 0 deletions _examples/gen/sqlc/query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# Code generated by sqlc. DO NOT EDIT.
# versions:
# sqlc v1.20.0
# source: query.sql
from typing import AsyncIterator, Iterator, Optional

import sqlalchemy
import sqlalchemy.ext.asyncio

from . import models


CREATE_AUTHOR = """-- name: create_author \\:one
insert into authors (
name, bio, age
) values (
:p1, :p2, :p3
)
returning id, name, age, bio, is_active
"""


DELETE_AUTHOR = """-- name: delete_author \\:exec
delete from authors
where id = :p1
"""


GET_AUTHOR = """-- name: get_author \\:one
select id, name, age, bio, is_active from authors
where id = :p1 LIMIT 1
"""


LIST_AUTHORS = """-- name: list_authors \\:many
select id, name, age, bio, is_active from authors
order by name
"""


LOCK_AUTHOR = """-- name: lock_author \\:one
select id, name, age, bio, is_active from authors
where id = :p1 LIMIT 1 FOR UPDATE NOWAIT
"""


UPDATE_AUTHOR = """-- name: update_author \\:one
update authors
set name = :p2,
bio = :p3,
age = :p4
where id = :p1
returning id, name, age, bio, is_active
"""


class Querier:
def __init__(self, conn: sqlalchemy.engine.Connection):
self._conn = conn

def create_author(self, *, name: str, bio: Optional[str], age: int) -> Optional[models.Author]:
row = self._conn.execute(sqlalchemy.text(CREATE_AUTHOR), {"p1": name, "p2": bio, "p3": age}).first()
if row is None:
return None
return models.Author(
id=row[0],
name=row[1],
age=row[2],
bio=row[3],
is_active=row[4],
)

def delete_author(self, *, id: int) -> None:
self._conn.execute(sqlalchemy.text(DELETE_AUTHOR), {"p1": id})

def get_author(self, *, id: int) -> Optional[models.Author]:
row = self._conn.execute(sqlalchemy.text(GET_AUTHOR), {"p1": id}).first()
if row is None:
return None
return models.Author(
id=row[0],
name=row[1],
age=row[2],
bio=row[3],
is_active=row[4],
)

def list_authors(self) -> Iterator[models.Author]:
result = self._conn.execute(sqlalchemy.text(LIST_AUTHORS))
for row in result:
yield models.Author(
id=row[0],
name=row[1],
age=row[2],
bio=row[3],
is_active=row[4],
)

def lock_author(self, *, id: int) -> Optional[models.Author]:
row = self._conn.execute(sqlalchemy.text(LOCK_AUTHOR), {"p1": id}).first()
if row is None:
return None
return models.Author(
id=row[0],
name=row[1],
age=row[2],
bio=row[3],
is_active=row[4],
)

def update_author(self, *, id: int, name: str, bio: Optional[str], age: int) -> Optional[models.Author]:
row = self._conn.execute(sqlalchemy.text(UPDATE_AUTHOR), {
"p1": id,
"p2": name,
"p3": bio,
"p4": age,
}).first()
if row is None:
return None
return models.Author(
id=row[0],
name=row[1],
age=row[2],
bio=row[3],
is_active=row[4],
)


class AsyncQuerier:
def __init__(self, conn: sqlalchemy.ext.asyncio.AsyncConnection):
self._conn = conn

async def create_author(self, *, name: str, bio: Optional[str], age: int) -> Optional[models.Author]:
row = (await self._conn.execute(sqlalchemy.text(CREATE_AUTHOR), {"p1": name, "p2": bio, "p3": age})).first()
if row is None:
return None
return models.Author(
id=row[0],
name=row[1],
age=row[2],
bio=row[3],
is_active=row[4],
)

async def delete_author(self, *, id: int) -> None:
await self._conn.execute(sqlalchemy.text(DELETE_AUTHOR), {"p1": id})

async def get_author(self, *, id: int) -> Optional[models.Author]:
row = (await self._conn.execute(sqlalchemy.text(GET_AUTHOR), {"p1": id})).first()
if row is None:
return None
return models.Author(
id=row[0],
name=row[1],
age=row[2],
bio=row[3],
is_active=row[4],
)

async def list_authors(self) -> AsyncIterator[models.Author]:
result = await self._conn.stream(sqlalchemy.text(LIST_AUTHORS))
async for row in result:
yield models.Author(
id=row[0],
name=row[1],
age=row[2],
bio=row[3],
is_active=row[4],
)

async def lock_author(self, *, id: int) -> Optional[models.Author]:
row = (await self._conn.execute(sqlalchemy.text(LOCK_AUTHOR), {"p1": id})).first()
if row is None:
return None
return models.Author(
id=row[0],
name=row[1],
age=row[2],
bio=row[3],
is_active=row[4],
)

async def update_author(self, *, id: int, name: str, bio: Optional[str], age: int) -> Optional[models.Author]:
row = (await self._conn.execute(sqlalchemy.text(UPDATE_AUTHOR), {
"p1": id,
"p2": name,
"p3": bio,
"p4": age,
})).first()
if row is None:
return None
return models.Author(
id=row[0],
name=row[1],
age=row[2],
bio=row[3],
is_active=row[4],
)
31 changes: 31 additions & 0 deletions _examples/query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-- name: GetAuthor :one
select * from authors
where id = $1 LIMIT 1;

-- name: LockAuthor :one
select * from authors
where id = $1 LIMIT 1 FOR UPDATE NOWAIT;

-- name: ListAuthors :many
select * from authors
order by name;

-- name: CreateAuthor :one
insert into authors (
name, bio, age
) values (
$1, $2, $3
)
returning *;

-- name: UpdateAuthor :one
update authors
set name = $2,
bio = $3,
age = $4
where id = $1
returning *;

-- name: DeleteAuthor :exec
delete from authors
where id = $1;
7 changes: 7 additions & 0 deletions _examples/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
create table authors (
id BIGSERIAL PRIMARY KEY,
name text not null,
age int not null default 0,
bio text,
is_active boolean not null default true
);
Loading