Skip to content
This repository was archived by the owner on Sep 23, 2024. It is now read-only.
Merged
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
42 changes: 36 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
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-ts-d1.wasm dist/sqlc-gen-ts-d1.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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 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.

## Usage

```yaml
Expand All @@ -19,3 +23,7 @@ sql:
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
);
22 changes: 22 additions & 0 deletions _sqlc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: '2'
plugins:
- name: py
wasm:
url: file://dist/sqlc-gen-python-orm.wasm
sha256: $WASM_SHA256
sql:
- schema: "_examples/schema.sql"
queries: "_examples/query.sql"
engine: postgresql
gen:
json:
out: "."
codegen:
- out: "_examples/gen/sqlc"
plugin: py
options:
package: .
emit_sync_querier: true
emit_async_querier: true
emit_pydantic_models: true
emit_sqlalchemy_models: true
4 changes: 3 additions & 1 deletion plugin/main.go → cmd/sqlc-gen-python-orm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package main
import (
"github.com/sqlc-dev/sqlc-go/codegen"

python "github.com/sqlc-dev/sqlc-gen-python/internal"
python "github.com/zztkm/sqlc-gen-python-orm/internal"
)

const version = "0.0.1"

func main() {
codegen.Run(python.Generate)
}
Loading