Skip to content
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
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ runtime-tokio = ["sqlx?/runtime-tokio"]
runtime-tokio-native-tls = ["sqlx?/runtime-tokio-native-tls", "runtime-tokio"]
runtime-tokio-rustls = ["sqlx?/runtime-tokio-rustls", "runtime-tokio"]
rusqlite = []
cockroachdb = [
"sqlx-postgres",
"sea-query/backend-cockroach",
]
schema-sync = ["sea-schema"]
sea-orm-internal = []
seaography = ["sea-orm-macros/seaography"]
Expand Down Expand Up @@ -212,5 +216,5 @@ with-uuid = ["uuid", "sea-query/with-uuid", "sea-query-sqlx?/with-uuid"]

# This allows us to develop using a local version of sea-query
[patch.crates-io]
# sea-query = { path = "../sea-query" }
sea-query = { path = "../sea-query" }
# sea-query = { git = "https://github.com/SeaQL/sea-query", branch = "master" }
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,55 @@ let user: Option<user::Model> = user::Entity::find()

See the [quickstart example](https://github.com/SeaQL/sea-orm/blob/master/sea-orm-sync/examples/quickstart/src/main.rs) for usage.

## CockroachDB Support

SeaORM supports CockroachDB via the `cockroachdb` feature flag. CockroachDB is wire-compatible with PostgreSQL, so most functionality works seamlessly.

### Enabling CockroachDB Support

```toml
# Cargo.toml
[dependencies]
sea-orm = { version = "2.0", features = ["cockroachdb", "runtime-tokio-native-tls"] }
```

### Connecting to CockroachDB

Use a standard PostgreSQL connection string:

```rust
use sea_orm::DbConn;

let db: DbConn = sea_orm::Connect::connect("postgres://user:password@host:26257/database?sslmode=require")
.await?;
```

Or with CockroachDB specific scheme:

```rust
let db: DbConn = sea_orm::Connect::connect("cockroachdb://user:password@host:26257/database?sslmode=require")
.await?;
```

### Schema Generation

CockroachDB doesn't support PostgreSQL's `SERIAL` pseudo-type. SeaORM automatically generates `GENERATED BY DEFAULT AS IDENTITY` for auto-increment columns when using CockroachDB:

```sql
-- PostgreSQL
"id" SERIAL PRIMARY KEY

-- CockroachDB (SeaORM default)
"id" INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
```

### Feature Flags

| Feature | Description |
|---------|-------------|
| `cockroachdb` | Enable CockroachDB support |
| `cockroachdb-use-identity-pk` | Use IDENTITY columns for primary keys |

## Basics

### Select
Expand Down
38 changes: 38 additions & 0 deletions pr_body.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## Summary

This PR adds CockroachDB support to SeaORM. CockroachDB is wire-compatible with PostgreSQL, so most functionality works with the existing sqlx-postgres driver.

## Changes

- Added `cockroachdb` feature flag that enables:
- `DatabaseBackend::Cockroach` variant
- CockroachDB-specific URL scheme handling (`cockroachdb://`)
- Proper schema generation using IDENTITY columns (not SERIAL)
- Full RETURNING support

## Key Design Decisions

1. **Wire Compatibility**: CockroachDB uses the same protocol as PostgreSQL, so we alias `CockroachQueryBuilder` to `PostgresQueryBuilder` in sea-query
2. **IDENTITY vs SERIAL**: CockroachDB doesn't support PostgreSQL's SERIAL pseudo-type. The implementation generates `GENERATED BY DEFAULT AS IDENTITY` by default.

## Feature Flags

| Feature | Description |
|---------|-------------|
| `cockroachdb` | Enable CockroachDB support |
| `cockroachdb-use-identity-pk` | Use IDENTITY columns for primary keys |

## Testing

Added `tests/cockroachdb_tests.rs` with tests for:
- Backend detection with various URL schemes
- Schema generation using IDENTITY columns
- Boolean value conversion

## Breaking Changes

None - all changes are gated behind the new `cockroachdb` feature flag.

---

Related sea-query PR: https://github.com/SeaQL/sea-query/pull/329
1 change: 1 addition & 0 deletions sea-orm-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,7 @@ pub fn test(_: TokenStream, input: TokenStream) -> TokenStream {
feature = "sqlx-mysql",
feature = "sqlx-sqlite",
feature = "sqlx-postgres",
feature = "cockroachdb",
))]
#(#attrs)*
fn #name() #ret {
Expand Down
11 changes: 10 additions & 1 deletion src/database/db_connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ pub enum DatabaseBackend {
MySql,
/// A PostgreSQL backend
Postgres,
/// A CockroachDB backend
Cockroach,
/// A SQLite backend
Sqlite,
}
Expand Down Expand Up @@ -759,6 +761,11 @@ impl DbBackend {
Self::Postgres => {
base_url_parsed.scheme() == "postgres" || base_url_parsed.scheme() == "postgresql"
}
Self::Cockroach => {
base_url_parsed.scheme() == "cockroachdb"
|| base_url_parsed.scheme() == "postgres"
|| base_url_parsed.scheme() == "postgresql"
}
Self::MySql => base_url_parsed.scheme() == "mysql",
Self::Sqlite => base_url_parsed.scheme() == "sqlite",
}
Expand All @@ -776,6 +783,7 @@ impl DbBackend {
pub fn support_returning(&self) -> bool {
match self {
Self::Postgres => true,
Self::Cockroach => true,
Self::Sqlite if cfg!(feature = "sqlite-use-returning-for-3_35") => true,
Self::MySql if cfg!(feature = "mariadb-use-returning") => true,
_ => false,
Expand All @@ -785,7 +793,7 @@ impl DbBackend {
/// A getter for database dependent boolean value
pub fn boolean_value(&self, boolean: bool) -> sea_query::Value {
match self {
Self::MySql | Self::Postgres | Self::Sqlite => boolean.into(),
Self::MySql | Self::Postgres | Self::Cockroach | Self::Sqlite => boolean.into(),
}
}

Expand All @@ -794,6 +802,7 @@ impl DbBackend {
match self {
DatabaseBackend::MySql => "MySql",
DatabaseBackend::Postgres => "Postgres",
DatabaseBackend::Cockroach => "Cockroach",
DatabaseBackend::Sqlite => "Sqlite",
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/database/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ impl Database {
proxy_func_arc.to_owned(),
);
}
DbBackend::Cockroach => {
return crate::ProxyDatabaseConnector::connect(
DbBackend::Cockroach,
proxy_func_arc.to_owned(),
);
}
DbBackend::Sqlite => {
return crate::ProxyDatabaseConnector::connect(
DbBackend::Sqlite,
Expand Down
18 changes: 18 additions & 0 deletions src/database/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use crate::DbBackend;
#[cfg(feature = "rbac")]
pub use sea_query::audit::{AuditTrait, Error as AuditError, QueryAccessAudit};
use sea_query::{MysqlQueryBuilder, PostgresQueryBuilder, SqliteQueryBuilder, inject_parameters};
#[cfg(feature = "cockroachdb")]
use sea_query::CockroachQueryBuilder;
pub use sea_query::{Value, Values};
use std::fmt;

Expand Down Expand Up @@ -71,6 +73,14 @@ impl fmt::Display for Statement {
DbBackend::Postgres => {
inject_parameters(&self.sql, &values.0, &PostgresQueryBuilder)
}
#[cfg(feature = "cockroachdb")]
DbBackend::Cockroach => {
inject_parameters(&self.sql, &values.0, &CockroachQueryBuilder)
}
#[cfg(not(feature = "cockroachdb"))]
DbBackend::Cockroach => {
panic!("CockroachDB feature not enabled")
}
DbBackend::Sqlite => {
inject_parameters(&self.sql, &values.0, &SqliteQueryBuilder)
}
Expand All @@ -89,6 +99,10 @@ macro_rules! build_any_stmt {
match $db_backend {
DbBackend::MySql => $stmt.build(MysqlQueryBuilder),
DbBackend::Postgres => $stmt.build(PostgresQueryBuilder),
#[cfg(feature = "cockroachdb")]
DbBackend::Cockroach => $stmt.build(CockroachQueryBuilder),
#[cfg(not(feature = "cockroachdb"))]
DbBackend::Cockroach => panic!("CockroachDB feature not enabled"),
DbBackend::Sqlite => $stmt.build(SqliteQueryBuilder),
}
};
Expand All @@ -98,6 +112,10 @@ macro_rules! build_postgres_stmt {
($stmt: expr, $db_backend: expr) => {
match $db_backend {
DbBackend::Postgres => $stmt.to_string(PostgresQueryBuilder),
#[cfg(feature = "cockroachdb")]
DbBackend::Cockroach => $stmt.to_string(CockroachQueryBuilder),
#[cfg(not(feature = "cockroachdb"))]
DbBackend::Cockroach => panic!("CockroachDB feature not enabled"),
DbBackend::MySql | DbBackend::Sqlite => unimplemented!(),
}
};
Expand Down
1 change: 1 addition & 0 deletions src/database/tracing_spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ mod inner {
pub(crate) fn db_system_name(backend: DbBackend) -> &'static str {
match backend {
DbBackend::Postgres => "postgresql",
DbBackend::Cockroach => "cockroachdb",
DbBackend::MySql => "mysql",
DbBackend::Sqlite => "sqlite",
}
Expand Down
4 changes: 3 additions & 1 deletion src/executor/paginator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ where
None => return Ok(0),
};
let num_items = match db_backend {
DbBackend::Postgres => result.try_get::<i64>("", "num_items")? as u64,
DbBackend::Postgres | DbBackend::Cockroach => {
result.try_get::<i64>("", "num_items")? as u64
}
_ => result.try_get::<i32>("", "num_items")? as u64,
};
Ok(num_items)
Expand Down
2 changes: 1 addition & 1 deletion src/schema/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl SchemaBuilder {
}
}
#[cfg(feature = "sqlx-postgres")]
DbBackend::Postgres => {
DbBackend::Postgres | DbBackend::Cockroach => {
use sea_schema::{postgres::discovery::SchemaDiscovery, probe::SchemaProbe};

let current_schema: String = db
Expand Down
2 changes: 1 addition & 1 deletion src/schema/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ where
let variants: Vec<String> = variants.iter().map(|v| v.to_string()).collect();
ColumnType::custom(format!("ENUM('{}')", variants.join("', '")))
}
DbBackend::Postgres => ColumnType::Custom(name.clone()),
DbBackend::Postgres | DbBackend::Cockroach => ColumnType::Custom(name.clone()),
DbBackend::Sqlite => orm_column_def.col_type,
},
_ => orm_column_def.col_type,
Expand Down
Loading