{"id":33919065,"url":"https://github.com/kosame-rs/kosame","last_synced_at":"2026-01-11T05:02:37.225Z","repository":{"id":318922042,"uuid":"1061762038","full_name":"kosame-rs/kosame","owner":"kosame-rs","description":"Macro-based Rust ORM focused on developer ergonomics, inspired by Prisma and Drizzle","archived":false,"fork":false,"pushed_at":"2025-12-21T18:29:00.000Z","size":849,"stargazers_count":85,"open_issues_count":2,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-21T19:56:32.855Z","etag":null,"topics":["database","drizzle-orm","orm","postgresql","prisma","rust"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/kosame","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kosame-rs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-22T10:53:29.000Z","updated_at":"2025-12-20T18:52:28.000Z","dependencies_parsed_at":"2025-10-17T07:32:50.900Z","dependency_job_id":"9903ad8d-6e3c-48f7-8818-41a562a92f5b","html_url":"https://github.com/kosame-rs/kosame","commit_stats":null,"previous_names":["pikaju/kosame","kosame-orm/kosame","kosame-rs/kosame"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/kosame-rs/kosame","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kosame-rs%2Fkosame","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kosame-rs%2Fkosame/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kosame-rs%2Fkosame/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kosame-rs%2Fkosame/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kosame-rs","download_url":"https://codeload.github.com/kosame-rs/kosame/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kosame-rs%2Fkosame/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28287018,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T04:44:51.577Z","status":"ssl_error","status_checked_at":"2026-01-11T04:44:44.232Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","drizzle-orm","orm","postgresql","prisma","rust"],"created_at":"2025-12-12T08:40:15.431Z","updated_at":"2026-01-11T05:02:37.213Z","avatar_url":"https://github.com/kosame-rs.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n    \u003cpicture\u003e\n        \u003csource srcset=\"https://raw.githubusercontent.com/kosame-orm/kosame/refs/heads/main/misc/readme/logo-white.svg\" media=\"(prefers-color-scheme: dark)\"\u003e\n        \u003cimg width=\"256\" src=\"https://raw.githubusercontent.com/kosame-orm/kosame/refs/heads/main/misc/readme/logo-black.svg\" alt=\"Kosame Logo\"\u003e\n    \u003c/picture\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n    \u003ch3\u003eMacro-based Rust ORM focused on developer ergonomics\u003c/h3\u003e \n\n[![Crates.io](https://img.shields.io/crates/v/kosame.svg?style=flat-square)](https://crates.io/crates/kosame)\n[![Docs.rs](https://img.shields.io/badge/Docs-kosame-66c2a5?style=flat-square\u0026labelColor=555\u0026logo=rust\u0026logoColor=white)](https://docs.rs/kosame)\n[![GitHub](https://img.shields.io/badge/GitHub-kosame--orm%2Fkosame-blue?style=flat-square\u0026logo=github)](https://github.com/kosame-orm/kosame)\n[![License](https://img.shields.io/crates/l/kosame.svg?style=flat-square)](https://crates.io/crates/kosame)\n\n\u003c/div\u003e\n\n\u003cbr /\u003e\n\nKosame (小雨, Japanese for \"light rain\" or \"drizzle\") is a Rust ORM inspired by [Prisma](https://github.com/prisma/prisma) and [Drizzle](https://github.com/drizzle-team/drizzle-orm).\n\nSome TypeScript ORMs like Prisma can infer the result type of a database query based solely on the database schema and the query itself. Conversely, most Rust ORMs require developers to manually define a struct for the query's results, even though this type is tightly coupled to the query itself. Kosame was born out of a desire to have this level of developer ergonomics in Rust, using macro magic. Kosame also offers relational queries, allowing you to fetch multiple nested 1:N relationships in a single statement.\n\nKosame requires no active database connection during development and has no build step. Despite this, Kosame offers strong typing and rust-analyzer auto-completion.\n\n**Kosame is currently a prototype and not recommended for production use.**\n\n- [Showcase](#showcase)\n- [Planned features](#planned-features)\n- [Declaring the schema](#declaring-the-schema)\n   * [Column renaming and type overrides](#column-renaming-and-type-overrides)\n   * [Relations](#relations)\n- [Queries](#queries)\n   * [Columns and relations](#columns-and-relations)\n   * [Aliases and type overrides](#aliases-and-type-overrides)\n   * [Attributes](#attributes)\n   * [Expressions](#expressions)\n   * [Bind parameters](#bind-parameters)\n   * [`where`, `order by`, `limit`, and `offset`](#where-order-by-limit-and-offset)\n   * [Named vs. anonymous queries](#named-vs-anonymous-queries)\n- [Statements](#statements)\n   * [`select`](#select)\n   * [`insert`](#insert)\n   * [`update`](#update)\n   * [`delete`](#delete)\n- [Kosame CLI](#kosame-cli)\n   * [Formatting Kosame macros](#formatting-kosame-macros)\n      + [Editor integration](#editor-integration)\n         - [Neovim with conform.nvim](#neovim-with-conformnvim)\n- [Can Kosame handle all use cases well?](#can-kosame-handle-all-use-cases-well)\n\n## Showcase\n\n```rust\nuse kosame::prelude::*;\n\n// Declare your database schema. You may split the schema into multiple Rust modules.\nmod schema {\n    use kosame::pg_table;\n\n    pg_table! {\n        // Kosame uses the familiar SQL syntax to declare tables.\n        create table posts (\n            id int primary key,\n\n            // Kosame converts database identifiers to snake_case automatically and\n            // has a default Rust type for most well known database types. You can\n            // rename them or specify a different type if you prefer.\n            #[kosame(rename = renamed_title, ty = ::std::string::String)]\n            title text not null,\n\n            content text, // Trailing commas are allowed.\n        );\n\n        // Define a relation to another table. This enables relational queries.\n        comments: (id) \u003c= comments (post_id),\n    }\n\n    pg_table! {\n        create table comments (\n            id int primary key,\n            post_id int not null,\n            content text not null,\n            upvotes int not null default 0,\n        );\n\n        // You may also define the inverse relation if you need it.\n        post: (post_id) =\u003e posts (id),\n    }\n\n    // The `kosame::pg_table!` macro is a shorthand for `kosame::table!` with the driver\n    // attribute `#![kosame(driver = \"tokio-postgres\")]` prefilled. The same applies to\n    // `kosame::pg_statement!` and `kosame::pg_query!`.\n}\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let mut client = connect().await;\n\n    // Let's start by clearing the tables using `DELETE FROM` statements.\n    kosame::pg_statement! { delete from schema::posts }\n        .exec(\u0026mut client)\n        .await?;\n    kosame::pg_statement! { delete from schema::comments }\n        .exec(\u0026mut client)\n        .await?;\n\n    // Insert some demo data using `INSERT INTO`.\n    kosame::pg_statement! {\n        insert into\n            schema::posts\n        values\n            (0, \"my post\", \"hi, this is a post\"),\n            (1, \"another post\", \"very interesting content\"),\n            (2, \"post without content\", null),\n    }\n    .exec(\u0026mut client)\n    .await?;\n    kosame::pg_statement! {\n        insert into\n            schema::comments\n        values\n            (0, 2, \"wow very insightful\"),\n            (1, 1, \"nice\"),\n            (2, 1, \"didn't read lol\"),\n    }\n    .exec(\u0026mut client)\n    .await?;\n\n    // Upvote a comment using `UPDATE`.\n    let comment_id = 2;\n    let new_upvotes = kosame::pg_statement! {\n        update\n            schema::comments\n        set\n            upvotes = upvotes + 1,\n        where\n            // The `comment_id` variable above is used as a bind parameter in this expression.\n            id = :comment_id\n        returning\n            // We can return the updated value. Kosame infers the result type of this statement\n            // to be `struct Row { new_upvotes: i32 }` without a database connection.\n            comments.upvotes as new_upvotes,\n    }\n    // With the `RETURNING` clause we can now use `query` instead of `exec` and retrieve data.\n    .query_one(\u0026mut client)\n    .await?\n    .new_upvotes;\n\n    println!(\"{new_upvotes}\");\n    // 1\n\n    // Now let's perform a relational query. Relational queries fetch arbitrarily nested 1:N\n    // (or 1:1) relationships in the requested shape, so that you do not need to manually\n    // convert flat SQL tables into a struct hierarchy.\n    // We want to read the post with ID = 1, together with its top five comments.\n    let post_id = 1;\n    let rows = kosame::pg_query! {\n        // Attributes appearing here will be applied to all generated result structs.\n        #[derive(Clone)]\n        schema::posts {\n            *, // Select all fields from the posts table.\n\n            // Query related comments according to the relation defined in the schema.\n            comments {\n                // To save bandwidth, we select only the fields we need here.\n                id,\n\n                // Attributes on fields will also be applied to the result type fields.\n                #[serde(rename = \"serdeContent\")]\n                content,\n\n                /// This triple-slash documentation comment will appear in your IDE on the\n                /// generated type's `upvotes` field.\n                upvotes,\n\n                // Relational queries also use the familiar SQL-like syntax for\n                // `where`, `order by`, `limit` and `offset`.\n                order by\n                    upvotes desc,\n                limit\n                    5\n            },\n\n            // We can also query arbitrary SQL-like expressions, but we need to specify a name and\n            // Rust type at the end as they cannot be inferred by Kosame.\n            content is not null as has_content: bool,\n\n            where\n                id = :post_id\n        }\n    }\n    .query_opt(\u0026mut client)\n    .await?;\n\n    println!(\"{rows:#?}\");\n    // Some(\n    //     Row {\n    //         id: 1,\n    //         renamed_title: \"another post\",\n    //         content: Some(\n    //             \"very interesting content\",\n    //         ),\n    //         comments: Many(\n    //             [\n    //                 RowComments {\n    //                     id: 2,\n    //                     content: \"didn't read lol\",\n    //                     upvotes: 1,\n    //                 },\n    //                 RowComments {\n    //                     id: 1,\n    //                     content: \"nice\",\n    //                     upvotes: 0,\n    //                 },\n    //             ],\n    //         ),\n    //         has_content: true,\n    //     },\n    // )\n\n    // Relational queries are not well suited to every use case. To squeeze maximum performance and\n    // flexibility out of your database, you may want to write SQL `SELECT` statements directly.\n    // Kosame supports an SQL-like syntax with basic type inference for this scenario.\n    let rows = kosame::pg_statement! {\n        // A common table expression of all posts with non-null content.\n        with\n            posts_with_content as (\n                select\n                    posts.id,\n                from\n                    schema::posts\n                where\n                    content is not null\n            ),\n        select\n            // The type of this field is inferred as `i32`.\n            posts_with_content.id,\n            // This field would also be `i32`. However, because of the `left join`, Kosame knows it\n            // may be null and thus infers the field type to be `Option\u003ci32\u003e`.\n            top_comment.id as top_comment_id,\n            // Kosame cannot currently infer the name and type of this expressions, so we must\n            // declare them manually.\n            coalesce(sum(comments.upvotes), 0) as total_upvotes: i64,\n            // The $\"...\" syntax allows you inline raw SQL text into expressions, which can be\n            // helpful for syntax that Kosame does not yet support.\n            $\"'[1, 2, 3]'::jsonb @\u003e '[1, 3]'::jsonb\" as raw_sql: bool,\n        from\n            posts_with_content\n            left join schema::comments on posts_with_content.id = comments.post_id\n            // Kosame supports subqueries, including `lateral` ones.\n            left join lateral (\n                select\n                    comments.id,\n                from\n                    schema::comments\n                where\n                    // We can access `posts_with_content` from the higher up scope here.\n                    post_id = posts_with_content.id\n                order by\n                    upvotes desc,\n                limit\n                    1\n            ) as top_comment on true\n        group by\n            posts_with_content.id,\n            top_comment.id,\n    }\n    .query_vec(\u0026mut client)\n    .await?;\n    // The query above is of course inefficient and solely meant for demonstration purposes.\n\n    println!(\"{rows:#?}\");\n    // [\n    //     Row {\n    //         id: 0,\n    //         top_comment_id: None,\n    //         total_upvotes: 0,\n    //         raw_sql: true,\n    //     },\n    //     Row {\n    //         id: 1,\n    //         top_comment_id: Some(\n    //             2,\n    //         ),\n    //         total_upvotes: 1,\n    //         raw_sql: true,\n    //     },\n    // ]\n\n    // With the \"serde\" feature enabled, the result of a statement or query can be serialized.\n    println!(\"{}\", serde_json::to_string(\u0026rows).unwrap());\n    // [{\"id\":0,\"top_comment_id\":null,\"total_upvotes\":0,\"raw_sql\":true},{\"id\":1,\"top_comment_id\":2,\"total_upvotes\":1,\"raw_sql\":true}]\n\n    Ok(())\n}\n\n// This function connects to a database using tokio-postgres.\nasync fn connect() -\u003e tokio_postgres::Client {\n    let (client, connection) = tokio_postgres::connect(\n        \"postgres://postgres:postgres@localhost:5432/postgres\",\n        tokio_postgres::NoTls,\n    )\n    .await\n    .unwrap();\n\n    tokio::spawn(async move {\n        if let Err(e) = connection.await {\n            eprintln!(\"connection error: {e}\");\n        }\n    });\n\n    client\n}\n```\n\n## Planned features\n\nKosame is an early prototype. There are many features and performance optimizations left to implement, including but not limited to:\n\n* Support for other database management systems. Currently, only PostgreSQL (using [`tokio_postgres`](https://docs.rs/tokio-postgres/latest/tokio_postgres/)) is supported.\n* CLI for generating database migrations based on changes in the Kosame schema.\n* CLI for generating a Kosame schema by introspecting a database.\n* Support for more SQL expression syntax.\n* Alternative query runners, similar to the [`relationLoadStrategy` that Prisma offers](https://www.prisma.io/blog/prisma-orm-now-lets-you-choose-the-best-join-strategy-preview).\n* Type inference for bind parameters.\n\n## Declaring the schema\n\nBefore you can write queries with Kosame, you must declare your database schema. Instead of inventing a new syntax, Kosame tries to follow the existing `CREATE TABLE` syntax closely.\n\n```rust\nkosame::pg_table! {\n    create table posts (\n        id uuid primary key default uuidv7(),\n        title text not null,\n        content text, // trailing comma is allowed\n    );\n}\n```\n\nThis means declaring your schema may be as simple as copying a `pg_dump` into the Kosame macro. However, to enforce consistency, all SQL keywords must be lowercase. Kosame has a basic SQL expression parser, which allows you to define the `default` expression of a column.\n\n### Column renaming and type overrides\n\nKosame converts database identifiers to snake_case by default. If you want to refer to a database column by a different name in Rust, you can rename it:\n\n```rust\nkosame::pg_table! {\n    create table my_table (\n        #[kosame(rename = cool_column)]\n        my_column text not null,\n    );\n}\n```\n\nKosame attempts to guess the Rust type of a column based on its database type. For example, a PostgreSQL column of type `text` will be represented by a Rust `String`. If you want to use a different type, or if the database type is unknown to Kosame (e.g., for PostgreSQL custom types), you can specify a type override:\n\n```rust\nuse smol_str;\n\nkosame::pg_table! {\n    create table my_table (\n        #[kosame(ty = smol_str::SmolStr)]\n        my_column text not null,\n    );\n}\n```\n\nNote that the specified type must either be declared or `use`d in the scope of the `kosame::pg_table!` call or be a fully qualified path (e.g., `crate::MyType` or `::std::string::String`).\n\n### Relations\n\nDiverging from regular SQL syntax, you can declare relation fields. Relations tell Kosame how different tables can be queried together.\n\n```rust\nkosame::pg_table! {\n    create table posts_table (\n        id uuid primary key default uuidv7(),\n        content text not null,\n    );\n\n    comments: (id) \u003c= my_module::comments_table (post_id),\n}\n\nmod my_module {\n    kosame::pg_table! {\n        create table comments_table (\n            id uuid primary key default uuidv7(),\n            post_id int not null,\n            content text not null,\n        );\n\n        post: (post_id) =\u003e super::posts_table (id),\n    }\n}\n```\n\nIn this example, we have a `posts_table` and a `comments_table`. For each row in `posts_table`, we expect there to be zero or more comments. Conversely, each row in the `comments_table` has exactly one post associated with it, as defined by the `post_id` column.\n\nThe relation field declaration\n\n```\ncomments: (id) \u003c= my_module::comments_table (post_id)\n```\n\ndescribes a relation called `comments`. It specifies that the `post_id` column in `my_module::comments_table` \"points to\" the `id` column of `posts_table`. Although a Kosame relation does not have to map to a database foreign key, you can think of the `\u003c=` as pointing in the direction of the foreign key \"pointer\". With this relation field, we can query all comments associated with a given post:\n\n```rust\nkosame::pg_query! {\n    posts_table {\n        id,\n        content,\n        comments {\n            id,\n            content,\n        }\n    }\n}\n```\n\nIn the comments table, we have the inverse relation:\n\n```\npost: (post_id) =\u003e super::posts_table (id),\n```\n\nThis states that `post` is a row in `super::posts_table`, and it is linked by matching the `comments_table`'s `post_id` column with the `posts_table`'s `id` column. Note that the arrow (`=\u003e`) points in the other direction here. In this case, Kosame expects there to be at most one post per comment.\n\n```rust\nkosame::pg_query! {\n    my_module::comments_table {\n        id,\n        content,\n        post {\n            id,\n            content,\n        }\n    }\n}\n```\n\n## Queries\n\n### Columns and relations\n\nA basic Kosame query starts by defining the root table you want to read from. This can be a relative or absolute path to your table's declaration.\n\n```rust\npub mod schema {\n    ...\n}\n\nkosame::pg_query! {\n    schema::posts {\n        ...\n    }\n}\n\n// or\n\nkosame::pg_query! {\n    crate::schema::posts {\n        ...\n    }\n}\n```\n\nIn the query, you can list the column and relation fields you want to read. Relations can be nested as often as desired.\n\n```rust\nkosame::pg_query! {\n    schema::posts {\n        id,\n        title,\n\n        // `comments` is a relation, as indicated by the curly braces.\n        comments {\n            id,\n            content,\n            \n            author {\n                name,\n                email,\n            }  \n        },\n\n        // You can mix the order of columns and relations.\n        content,\n    }\n}\n```\n\nInstead of listing each column manually, you can also use `*` to select all columns of a table.\n\n```rust\nkosame::pg_query! {\n    schema::posts {\n        *,\n        comments {\n            *,\n            author { * }  \n        },\n    }\n}\n```\n\n### Aliases and type overrides\n\nYou can rename column or relation fields for each query using `as ...`. You can also change the Rust type of a column using `: ...`.\n\n```rust\nkosame::pg_query! {\n    schema::posts {\n        id as my_id,\n        title: ::smol_str::SmolStr,\n        content as my_content: ::std::string::String,\n        comments {\n            *\n        } as all_comments,\n    }\n}\n```\n\nThe row structs generated by Kosame will use the new aliases and data types.\n\n### Attributes\n\nKosame allows you to annotate your query and its fields with Rust attributes. Attributes assigned to the top-level table will be applied to _all_ generated row structs, including those representing nested relations. Attributes above column or relation fields will be assigned only to the row struct field they correspond to. It is also possible to document your query with documentation comments. Enable the `serde` feature for automatic `serde` derives on all row structs.\n\n```rust\nkosame::pg_query! {\n    #[serde(rename_all = \"camelCase\")]\n    schema::posts {\n        id as my_id,\n        \n        /// Rust documentation comments, like this one, are also attributes.\n        /// This means you can easily document your query and its fields!\n        content,\n\n        comments {\n            id as my_id,\n\n            #[serde(rename = \"cool_content\")]\n            content as comment_content,\n        }\n    }\n}\n```\n\nSerializing the result of the query above using `serde_json` returns the following JSON string:\n\n```json\n{\n  \"myId\": 5,\n  \"content\": \"hi this is a post\",\n  \"comments\": [\n    {\n      \"myId\": 19,\n      \"cool_content\": \"im another comment\"\n    },\n    {\n      \"myId\": 18,\n      \"cool_content\": \"im commenting something\"\n    }\n  ]\n}\n```\n\n### Expressions\n\nKosame can parse basic SQL expressions. Expressions can be used in various places, one of which is an expression field in your query:\n\n```rust\nkosame::pg_query! {\n    posts {\n        id,\n        upvotes + 1 as reddit_upvotes: i32,\n        cast(now() as text) as current_time: String,\n        title is not null or content is not null as has_content: bool,\n    }\n}\n```\n\nLike in the table definition, SQL keywords must be lowercase. Expression fields in a query **must** be aliased **and** given a type override. Kosame makes no attempt to deduce the name or type of an expression automatically.\n\nThe main difference between the syntax of Kosame expressions and SQL expressions is the handling of string literals and identifiers. Unlike in PostgreSQL, you do not need to use double-quotes to make your identifiers case-sensitive. Strings are written using double-quoted Rust strings, as opposed to single quotes:\n\n```rust\nkosame::pg_query! {\n    my_table {\n        \"Hello world!\" as hello_world: ::std::string::String,\n    }\n}\n```\n\n### Bind parameters\n\nKosame uses the `:param_name` syntax for using bind parameters in expressions:\n\n```rust\nkosame::pg_query! {\n    my_table {\n        :my_param + 5 as add_5: i32,\n    }\n}\n```\n\nKosame generates a `Params` struct containing a borrowed field for each parameter referenced in your query. When executing the query, the bind parameters are converted to the respective database management system's parameter syntax (e.g., `$1`, `$2`, etc., for PostgreSQL).\n\n### `where`, `order by`, `limit`, and `offset`\n\nKosame uses the familiar syntax for `where`, `order by`, `limit`, and `offset`. You can use expressions for each of these:\n\n```rust\nkosame::pg_query! {\n    posts {\n        id,\n        content,\n        comments {\n            content,\n            \n            order by upvotes desc, id asc nulls last\n            limit 5\n        },\n\n        where title = :title and content is not null\n        limit :page_size\n        offset :page * :page_size\n    }\n}\n```\n\n`where`, `order by`, `limit`, and `offset` must be specified in this order. They must come at the end of a block in a query. Make sure your last query field has a trailing comma.\n\n### Named vs. anonymous queries\n\nKosame supports both named and anonymous queries. Anonymous queries are defined inline and act as a Rust expression that can be executed immediately. They also allow capturing variables from the surrounding scope as bind parameters for the query (`:id` in this example):\n\n```rust\nlet id = 5;\n\nlet rows = kosame::pg_query! {\n    posts {\n        content,\n        where id = :id\n    }\n}\n.exec(client, \u0026mut RecordArrayRunner {})\n.await?;\n```\n\nWhile they are concise, anonymous queries have the drawback that the row types generated by Kosame cannot be named. This makes it difficult to specify concrete return types. We can only resort to the `impl Trait` syntax.\n\n```rust\nasync fn fetch_row(\n    client: \u0026mut tokio_postgres::Client,\n    id: i32,\n) -\u003e Result\u003cVec\u003cimpl serde::Serialize + Debug\u003e, Box\u003cdyn Error\u003e\u003e {\n    let rows = kosame::pg_query! {\n        posts {\n            content,\n            where id = :id\n        }\n    }\n    .query_vec(client)\n    .await?;\n\n    Ok(rows)\n}\n```\n\nNamed queries solve this problem by declaring the query upfront. To do this, give your query an alias that will be used as the module name generated by Kosame:\n\n```rust\nkosame::pg_query! {\n    posts {\n        content,\n        where id = :id\n    }\n    as my_query\n}\n```\n\nYou can now refer to all generated types by name:\n\n```rust\nasync fn fetch_row(\n    client: \u0026mut tokio_postgres::Client,\n    id: i32,\n) -\u003e Result\u003cVec\u003cmy_query::Row\u003e, Box\u003cdyn Error\u003e\u003e {\n    let rows = my_query::Query::new(my_query::Params { id: \u0026id })\n        .query_vec(client)\n        .await?;\n\n    Ok(rows)\n}\n```\n\n## Statements\n\nKosame also supports an SQL-like syntax for `SELECT`, `INSERT`, `UPDATE`, and `DELETE` queries which make database mutations possible and allow for greater oversight and flexibility over what exactly your database does.\n\n### `select`\n\nA simple `select` statement works without a `from` clause.\n```rust\nlet rows = kosame::pg_statement! {\n    select\n        5 as my_column: i32\n}\n.query_one(\u0026mut client)\n.await?;\n```\n\nYou can also buid more complex queries with `where`, `group by`, `having`, `order by`, `limit`, and `offset`.\n```rust\nlet rows = kosame::pg_statement! {\n    select\n        // Name and type of this column are inferred.\n        posts.id,\n        sum(comments.upvotes) as total_upvotes: i64,\n    from\n        schema::posts\n        inner join schema::comments on posts.id = comments.post_id\n    where\n        comments.upvotes \u003e 0\n    group by\n        posts.id\n    having\n        count(1) \u003e 0\n    order by\n        posts.id\n    limit\n        5\n}\n.query_vec(\u0026mut client)\n.await?;\n```\n\nCommon table expressions and (lateral) subqueries are also supported:\n```rust\nlet rows = kosame::pg_statement! {\n    // A common table expression of all posts with non-null content.\n    with posts_with_content as (\n        select\n            posts.id\n        from\n            schema::posts\n        where\n            content is not null\n    )\n    select\n        // The type of this field is inferred as `i32`.\n        posts_with_content.id,\n        // This field would also be `i32`. However, because of the `left join`, Kosame knows it\n        // may be null and thus infers the field type to be `Option\u003ci32\u003e`.\n        top_comment.id as top_comment_id,\n        // Kosame cannot currently infer the name and type of this expressions, so we must\n        // declare them manually.\n        coalesce(sum(comments.upvotes), 0) as total_upvotes: i64,\n        // The $\"...\" syntax allows you inline raw SQL text into expressions, which can be\n        // helpful for syntax that Kosame does not yet support.\n        $\"'[1, 2, 3]'::jsonb @\u003e '[1, 3]'::jsonb\" as raw_sql: bool,\n    from\n        posts_with_content\n        left join schema::comments on posts_with_content.id = comments.post_id\n        // Kosame supports subqueries, including `lateral` ones.\n        left join lateral (\n            select\n                comments.id\n            from\n                schema::comments\n            where\n                // We can access `posts_with_content` from the higher up scope here.\n                post_id = posts_with_content.id\n            order by\n                    upvotes desc\n            limit 1\n        ) as top_comment on true\n    group by\n        posts_with_content.id, top_comment.id\n}\n.query_vec(\u0026mut client)\n.await?;\n```\n\nKosame also supports set operations for combining multiple `select` statements:\n```rust\nlet rows = kosame::pg_statement! {\n    select\n        comments.content,\n    from\n        schema::comments\n    union all\n        select\n            posts.content,\n        from\n            schema::posts\n    order by\n        1 desc,\n    limit\n        20\n}\n.query_vec(\u0026mut client)\n.await?;\n```\n\nThe following set operations are supported:\n- `union` - Combines results from multiple queries, removing duplicates\n- `union all` - Combines results from multiple queries, keeping duplicates\n- `intersect` - Returns only rows that appear in both queries\n- `intersect all` - Returns rows that appear in both queries, keeping duplicates\n- `except` - Returns rows from the first query that don't appear in the second\n- `except all` - Returns rows from the first query that don't appear in the second, keeping duplicates\n\nThe Rust type is inferred from the first set of `select` fields in the chain of operations.\n\n### `insert`\n\n```rust\nlet new_post_ids = kosame::pg_statement! {\n    insert into schema::posts\n    values\n        (0, \"my post\", \"hi, this is a post\"),\n        (1, \"another post\", \"very interesting content\"),\n        (2, \"post without content\", null),\n    returning\n        posts.id\n}\n// With the `RETURNING` clause we can now use `query` instead of `exec` and retrieve data.\n.query_vec(\u0026mut client)\n.await?;\n```\n\n### `update`\n\n```rust\nlet new_upvotes = kosame::pg_statement! {\n    update\n        schema::comments\n    set\n        upvotes = upvotes + 1\n    where\n        id = 5\n    returning\n        comments.upvotes as new_upvotes\n}\n.query_one(\u0026mut client)\n.await?;\n```\n\n### `delete`\n\n```rust\nkosame::pg_statement! {\n    delete from\n        schema::posts\n    using\n        schema::comments\n    where\n        posts.id = comments.post_id\n}\n.exec(\u0026mut client)\n.await?;\n```\n\n## Kosame CLI\n\nKosame provides a command-line tool for code formatting. In the future, it will also be used for database migrations and introspection. Install the CLI tool using:\n\n```bash\ncargo install kosame_cli\n```\n\nAnd make sure your `PATH` environment variable is configured correctly (see https://rust-lang.org/tools/install/).\nCommands can be run either through the `kosame` binary or using `cargo kosame ...`.\n\n### Formatting Kosame macros\n\nThe CLI includes a formatter that automatically reformats only the contents of `pg_table!`, `pg_query!`, and `pg_statement!` macros (and their non-`pg_` variants) with proper indentation and structure.\n\n```bash\n# Format a single file\nkosame fmt src/main.rs\n\n# Format multiple files\nkosame fmt src/main.rs src/lib.rs\n\n# Format all Rust files in a directory recursively\nkosame fmt src/\n\n# Use glob patterns\nkosame fmt \"src/**/*.rs\"\n\n# Read from stdin and write to stdout (useful for editor integrations)\nkosame fmt --stdin \u003c src/main.rs\n```\n\n#### Editor integration\n\n##### Neovim with conform.nvim\n\nCreate a `Kosame.toml` file at the root of your Rust project. Then add the following `conform.nvim` setup to your Neovim configuration:\n\n```lua\nrequire(\"conform\").setup({\n    formatters = {\n        kosame = {\n            command = \"kosame\",\n            args = { \"fmt\", \"--stdin\" },\n            require_cwd = true,\n            cwd = function(self, ctx)\n                return require(\"conform.util\").root_file({ \"Kosame.toml\" })(self, ctx)\n            end,\n        },\n    }\n    formatters_by_ft = {\n        rust = { \"kosame\", lsp_format = \"first\" },\n    },\n})\n```\n\n## Can Kosame handle all use cases well?\n\nNo. Writing raw SQL directly will always give you more flexibility and control over what your database does, which may also allow you to optimize performance beyond what the Kosame supports. But that's okay! You can combine Kosame with another method to access the database. Use Kosame for situations in which you benefit from the relational query syntax and type inference. In more demanding situations, consider using a crate like [`sqlx`](https://github.com/launchbadge/sqlx).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkosame-rs%2Fkosame","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkosame-rs%2Fkosame","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkosame-rs%2Fkosame/lists"}