Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/wookay/octo.jl

Octo.jl 🐙 is an SQL Query DSL in Julia
https://github.com/wookay/octo.jl

dsl julia query repo sql

Last synced: 1 day ago
JSON representation

Octo.jl 🐙 is an SQL Query DSL in Julia

Awesome Lists containing this project

README

        

# Octo.jl

| **Documentation** | **Build Status** |
|:-----------------------------------------:|:------------------------------------------------------------------:|
| [![][docs-latest-img]][docs-latest-url] | [![][actions-img]][actions-url] [![][codecov-img]][codecov-url] |

`Octo.jl` is an SQL Query DSL in [Julia](https://julialang.org).
It also comes with a very useful tool called [Repo](https://github.com/wookay/Octo.jl#repo).
You could `Repo.get`, `Repo.insert!` `Repo.update!` `Repo.delete!` for many database drivers without hand-written SQL.

It's influenced by [Ecto](https://github.com/elixir-ecto/ecto).

* ☕️ You can [make a donation](https://wookay.github.io/donate/) to support this project.

## SQL Query DSL

```julia
julia> using Octo.Adapters.SQL

julia> struct User
end

julia> Schema.model(User, table_name="users")
| primary_key | table_name |
| ------------- | ------------ |
| id | users |

julia> u = from(User)
FromItem users

julia> [SELECT * FROM u]
SELECT * FROM users

julia> [SELECT (u.name, u.salary) FROM u]
SELECT name, salary FROM users

julia> [SELECT * FROM u WHERE u.id == 2]
SELECT * FROM users WHERE id = 2

julia> to_sql([SELECT * FROM u WHERE u.id == 2])
"SELECT * FROM users WHERE id = 2"
```

![structured.svg](https://wookay.github.io/docs/Octo.jl/assets/octo/structured.svg)

## Repo

Current supported database drivers:
- PostgreSQL (via [LibPQ.jl](https://github.com/invenia/LibPQ.jl))
- SQLite (via [SQLite.jl](https://github.com/JuliaDatabases/SQLite.jl))
- MySQL (via [MySQL.jl](https://github.com/JuliaDatabases/MySQL.jl))
- DuckDB (via [duckdb/tools/juliapkg](https://github.com/duckdb/duckdb/tree/main/tools/juliapkg))

```julia
julia> using Octo.Adapters.PostgreSQL

julia> Repo.debug_sql()
LogLevelDebugSQL::RepoLogLevel = -1

julia> Repo.connect(
adapter = Octo.Adapters.PostgreSQL,
dbname = "postgresqltest",
user = "postgres",
)
Octo.Repo.Connection(false, "postgresqltest", Main.PostgreSQLLoader, PostgreSQL connection (CONNECTION_OK) with parameters:
user = postgres
passfile = /Users/wookyoung/.pgpass
dbname = postgresqltest
port = 5432
client_encoding = UTF8
options = -c DateStyle=ISO,YMD -c IntervalStyle=iso_8601 -c TimeZone=UTC
application_name = LibPQ.jl
sslmode = prefer
sslcompression = 0
gssencmode = disable
target_session_attrs = any)

julia> struct Employee
end

julia> Schema.model(Employee, table_name="Employee", primary_key="ID")
| primary_key | table_name |
| ------------- | ------------ |
| ID | Employee |

julia> Repo.execute([DROP TABLE IF EXISTS Employee])
[ Info: DROP TABLE IF EXISTS Employee

julia> Repo.execute(Raw("""
CREATE TABLE Employee (
ID SERIAL,
Name VARCHAR(255),
Salary FLOAT(8),
PRIMARY KEY (ID)
)"""))
┌ Info: CREATE TABLE Employee (
│ ID SERIAL,
│ Name VARCHAR(255),
│ Salary FLOAT(8),
│ PRIMARY KEY (ID)
└ )

julia> Repo.insert!(Employee, [
(Name="Jeremy", Salary=10000.50),
(Name="Cloris", Salary=20000.50),
(Name="John", Salary=30000.50),
(Name="Hyunden", Salary=40000.50),
(Name="Justin", Salary=50000.50),
(Name="Tom", Salary=60000.50),
])
[ Info: INSERT INTO Employee (Name, Salary) VALUES ($1, $2) RETURNING ID (Name = "Jeremy", Salary = 10000.5), (Name = "Cloris", Salary = 20000.5), (Name = "John", Salary = 30000.5), (Name = "Hyunden", Salary = 40000.5), (Name = "Justin", Salary = 50000.5), (Name = "Tom", Salary = 60000.5)
| id | num_affected_rows |
| ---- | ------------------- |
| 6 | 6 |

julia> Repo.get(Employee, 2)
[ Info: SELECT * FROM Employee WHERE ID = 2
| id | name | salary |
| ---- | -------- | --------- |
| 2 | Cloris | 20000.5 |
1 row.

julia> Repo.get(Employee, 2:5)
[ Info: SELECT * FROM Employee WHERE ID BETWEEN 2 AND 5
| id | name | salary |
| ---- | --------- | --------- |
| 2 | Cloris | 20000.5 |
| 3 | John | 30000.5 |
| 4 | Hyunden | 40000.5 |
| 5 | Justin | 50000.5 |
4 rows.

julia> Repo.get(Employee, (Name="Jeremy",))
[ Info: SELECT * FROM Employee WHERE Name = 'Jeremy'
| id | name | salary |
| ---- | -------- | --------- |
| 1 | Jeremy | 10000.5 |
1 row.

julia> Repo.query(Employee)
[ Info: SELECT * FROM Employee
| id | name | salary |
| ---- | --------- | --------- |
| 1 | Jeremy | 10000.5 |
| 2 | Cloris | 20000.5 |
| 3 | John | 30000.5 |
| 4 | Hyunden | 40000.5 |
| 5 | Justin | 50000.5 |
| 6 | Tom | 60000.5 |
6 rows.

julia> Repo.insert!(Employee, (Name="Jessica", Salary=70000.50))
[ Info: INSERT INTO Employee (Name, Salary) VALUES ($1, $2) RETURNING ID (Name = "Jessica", Salary = 70000.5)
| id | num_affected_rows |
| ---- | ------------------- |
| 7 | 1 |

julia> Repo.update!(Employee, (ID=2, Salary=85000))
[ Info: UPDATE Employee SET Salary = $1 WHERE ID = 2 85000
| num_affected_rows |
| ------------------- |
| 1 |

julia> Repo.delete!(Employee, (ID=3,))
[ Info: DELETE FROM Employee WHERE ID = 3
| num_affected_rows |
| ------------------- |
| 1 |

julia> Repo.delete!(Employee, 3:5)
[ Info: DELETE FROM Employee WHERE ID BETWEEN 3 AND 5
| num_affected_rows |
| ------------------- |
| 2 |

julia> em = from(Employee)
FromItem Employee

julia> Repo.query(em)
[ Info: SELECT * FROM Employee
| id | name | salary |
| ---- | --------- | --------- |
| 1 | Jeremy | 10000.5 |
| 6 | Tom | 60000.5 |
| 7 | Jessica | 70000.5 |
| 2 | Cloris | 85000.0 |
4 rows.

julia> Repo.query([SELECT * FROM em WHERE em.Name == "Cloris"])
[ Info: SELECT * FROM Employee WHERE Name = 'Cloris'
| id | name | salary |
| ---- | -------- | --------- |
| 2 | Cloris | 85000.0 |
1 row.

julia> Repo.query(em, (Name="Cloris",))
[ Info: SELECT * FROM Employee WHERE Name = 'Cloris'
| id | name | salary |
| ---- | -------- | --------- |
| 2 | Cloris | 85000.0 |
1 row.

julia> ❓ = Octo.PlaceHolder
PlaceHolder

julia> Repo.query([SELECT * FROM em WHERE em.Name == ❓], ["Cloris"])
[ Info: SELECT * FROM Employee WHERE Name = $1 "Cloris"
| id | name | salary |
| ---- | -------- | --------- |
| 2 | Cloris | 85000.0 |
1 row.
```

### Subqueries

```julia
julia> sub = from([SELECT * FROM em WHERE em.Salary > 30000], :sub)
SubQuery (SELECT * FROM Employee WHERE Salary > 30000) AS sub

julia> Repo.query(sub)
[ Info: SELECT * FROM Employee WHERE Salary > 30000
| id | name | salary |
| ---- | --------- | --------- |
| 6 | Tom | 60000.5 |
| 7 | Jessica | 70000.5 |
| 2 | Cloris | 85000.0 |
3 rows.

julia> Repo.query([SELECT sub.Name FROM sub])
[ Info: SELECT sub.Name FROM (SELECT * FROM Employee WHERE Salary > 30000) AS sub
| name |
| --------- |
| Tom |
| Jessica |
| Cloris |
3 rows.
```

## Colored SQL statements

![colored_sql_statements.png](https://raw.github.com/wookay/Octo.jl/master/docs/images/colored_sql_statements.png)

## Requirements

You need [Julia](https://julialang.org/downloads/).

`julia>` type `]` key

```julia
(v1.8) pkg> add Octo
```

```julia
(v1.8) pkg> add LibPQ # for PostgreSQL (depends on LibPQ.jl 1.6, 1.7)
(v1.8) pkg> add SQLite # for SQLite (depends on SQLite.jl 1.6)
(v1.8) pkg> add MySQL # for MySQL (depends on MySQL.jl 1.1, 1.4)
(v1.8) pkg> add DuckDB # for DuckDB (depends on DuckDB.jl 1.0)
```

See also [DBInterface.jl](https://github.com/JuliaDatabases/DBInterface.jl).

[docs-latest-img]: https://img.shields.io/badge/docs-latest-blue.svg
[docs-latest-url]: https://wookay.github.io/docs/Octo.jl/

[actions-img]: https://github.com/wookay/Octo.jl/workflows/CI/badge.svg
[actions-url]: https://github.com/wookay/Octo.jl/actions

[codecov-img]: https://codecov.io/gh/wookay/Octo.jl/branch/master/graph/badge.svg
[codecov-url]: https://codecov.io/gh/wookay/Octo.jl