{"id":20269263,"url":"https://github.com/wroge/esperanto","last_synced_at":"2025-04-11T04:01:24.259Z","repository":{"id":59045308,"uuid":"530348980","full_name":"wroge/esperanto","owner":"wroge","description":"SQL Database Access Layer for multiple Dialects","archived":false,"fork":false,"pushed_at":"2023-05-03T07:55:33.000Z","size":99,"stargazers_count":4,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-19T23:10:03.580Z","etag":null,"topics":["mysql","no-orm","oracle","postgres","sql","sqlite","sqlserver"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wroge.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}},"created_at":"2022-08-29T18:41:58.000Z","updated_at":"2022-09-15T17:22:00.000Z","dependencies_parsed_at":"2024-06-20T02:51:03.074Z","dependency_job_id":"984fccf5-d067-4bf4-9504-af7f4f941e25","html_url":"https://github.com/wroge/esperanto","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wroge%2Fesperanto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wroge%2Fesperanto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wroge%2Fesperanto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wroge%2Fesperanto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wroge","download_url":"https://codeload.github.com/wroge/esperanto/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248339256,"owners_count":21087214,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["mysql","no-orm","oracle","postgres","sql","sqlite","sqlserver"],"created_at":"2024-11-14T12:24:13.648Z","updated_at":"2025-04-11T04:01:24.192Z","avatar_url":"https://github.com/wroge.png","language":"Go","readme":"# esperanto\n\n[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go\u0026logoColor=white)](https://pkg.go.dev/github.com/wroge/esperanto)\n[![Go Report Card](https://goreportcard.com/badge/github.com/wroge/esperanto)](https://goreportcard.com/report/github.com/wroge/esperanto)\n![golangci-lint](https://github.com/wroge/esperanto/workflows/golangci-lint/badge.svg)\n[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/wroge/esperanto.svg?style=social)](https://github.com/wroge/esperanto/tags)\n\nesperanto is a database access layer. It is based upon ...\n\n- [wroge/superbasic](https://github.com/wroge/superbasic)\n- [wroge/scan](https://github.com/wroge/scan)\n\nThis module can help you better organize your queries, especially if you need support for multiple dialects.\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"fmt\"\n\n\t\"github.com/wroge/esperanto\"\n\t\"github.com/wroge/scan\"\n\t\"github.com/wroge/superbasic\"\n\n\t_ \"modernc.org/sqlite\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tstdDB, err := sql.Open(\"sqlite\", \":memory:?_pragma=foreign_keys(1)\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tdb := esperanto.StdDB{\n\t\tPlaceholder: \"?\",\n\t\tDB:          stdDB,\n\t}\n\n\terr = esperanto.Exec(ctx, db, esperanto.Sqlite, DropPostAuthors, DropPosts, DropAuthors, CreateAuthors, CreatePosts, CreatePostAuthors)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tauthorEntities, err := esperanto.Query(ctx, db, esperanto.Sqlite, AuthorInsert, []string{\"Jim\", \"Tim\", \"Tom\"})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(authorEntities)\n\t// [{1 Jim} {2 Tim} {3 Tom}]\n\n\tpostEntities, err := esperanto.QueryAndExec(ctx, db, esperanto.Sqlite, PostInsert, []InsertPostOptions{\n\t\t{\n\t\t\tTitle:   \"Post One\",\n\t\t\tAuthors: []int64{1, 2},\n\t\t},\n\t}, PostAuthorsInsert)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(postEntities)\n\t// [{1 Post One}]\n\n\tpostTwo, err := esperanto.QueryAndExecOne(ctx, db, esperanto.Sqlite, PostInsertOne, InsertPostOptions{\n\t\tTitle:   \"Post Two\",\n\t\tAuthors: []int64{2, 3},\n\t}, PostAuthorsInsertOne)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(postTwo)\n\t// {2 Post Two []}\n\n\tauthors, err := esperanto.Query(ctx, db, esperanto.Sqlite, AuthorQuery, QueryAuthorOptions{})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(authors)\n\t// [{1 Jim [{1 Post One}]} {2 Tim [{1 Post One} {2 Post Two}]} {3 Tom [{2 Post Two}]}]\n\n\tposts, err := esperanto.Query(ctx, db, esperanto.Sqlite, PostQuery, QueryPostOptions{})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(posts)\n\t// [{1 Post One [{1 Jim} {2 Tim}]} {2 Post Two [{2 Tim} {3 Tom}]}]\n}\n\ntype PostEntity struct {\n\tID    int64\n\tTitle string\n}\n\ntype Post struct {\n\tID      int64\n\tTitle   string\n\tAuthors []AuthorEntity\n}\n\ntype AuthorEntity struct {\n\tID   int64\n\tName string\n}\n\ntype Author struct {\n\tID    int64\n\tName  string\n\tPosts []PostEntity\n}\n\nfunc DropAuthors(dialect esperanto.Dialect) superbasic.Expression {\n\treturn superbasic.SQL(\"DROP TABLE IF EXISTS authors\")\n}\n\nfunc CreateAuthors(dialect esperanto.Dialect) superbasic.Expression {\n\treturn superbasic.Compile(\"CREATE TABLE IF NOT EXISTS authors (\\n\\t?\\n)\",\n\t\tsuperbasic.Join(\",\\n\\t\",\n\t\t\tsuperbasic.Switch(dialect,\n\t\t\t\tsuperbasic.Case(esperanto.Postgres, superbasic.SQL(\"id SERIAL PRIMARY KEY\")),\n\t\t\t\tsuperbasic.Case(esperanto.Sqlite, superbasic.SQL(\"id INTEGER PRIMARY KEY AUTOINCREMENT\")),\n\t\t\t),\n\t\t\tsuperbasic.SQL(\"name TEXT NOT NULL\"),\n\t\t),\n\t)\n}\n\ntype QueryAuthorOptions struct{}\n\nfunc AuthorQuery(dialect esperanto.Dialect, options QueryAuthorOptions) (superbasic.Expression, []scan.Column[Author]) {\n\treturn superbasic.Compile(`\n\tSELECT authors.id, authors.name, ? AS posts\n\tFROM authors\n\tLEFT JOIN post_authors ON post_authors.author_id = authors.id\n\tLEFT JOIN posts ON posts.id = post_authors.post_id\n\tGROUP BY authors.id, authors.name`,\n\t\t\tsuperbasic.Switch(dialect,\n\t\t\t\tsuperbasic.Case(esperanto.Postgres, superbasic.SQL(\"JSON_AGG(JSON_BUILD_OBJECT('id', posts.id, 'title', posts.title))\")),\n\t\t\t\tsuperbasic.Case(esperanto.Sqlite, superbasic.SQL(`CASE WHEN posts.id IS NULL THEN '[]' \n\t\t\t\tELSE JSON_GROUP_ARRAY(JSON_OBJECT('id', posts.id, 'title', posts.title)) END`)),\n\t\t\t),\n\t\t),\n\t\t[]scan.Column[Author]{\n\t\t\tscan.Any(func(author *Author, id int64) { author.ID = id }),\n\t\t\tscan.Any(func(author *Author, name string) { author.Name = name }),\n\t\t\tscan.AnyErr(func(author *Author, posts []byte) error { return json.Unmarshal(posts, \u0026author.Posts) }),\n\t\t}\n}\n\nfunc AuthorInsert(dialect esperanto.Dialect, names []string) (superbasic.Expression, []scan.Column[AuthorEntity]) {\n\treturn superbasic.Compile(\"INSERT INTO authors (name) VALUES ? RETURNING id, name\", superbasic.Join(\", \", superbasic.Map(names, func(_ int, name string) superbasic.Expression {\n\t\t\treturn superbasic.Values{name}\n\t\t})...)),\n\t\t[]scan.Column[AuthorEntity]{\n\t\t\tscan.Any(func(author *AuthorEntity, id int64) { author.ID = id }),\n\t\t\tscan.Any(func(author *AuthorEntity, name string) { author.Name = name }),\n\t\t}\n}\n\nfunc AuthorInsertOne(dialect esperanto.Dialect, name string) (superbasic.Expression, []scan.Column[AuthorEntity]) {\n\treturn superbasic.SQL(\"INSERT INTO authors (name) VALUES (?) RETURNING id, name\", name),\n\t\t[]scan.Column[AuthorEntity]{\n\t\t\tscan.Any(func(author *AuthorEntity, id int64) { author.ID = id }),\n\t\t\tscan.Any(func(author *AuthorEntity, name string) { author.Name = name }),\n\t\t}\n}\n\nfunc DropPosts(dialect esperanto.Dialect) superbasic.Expression {\n\treturn superbasic.SQL(\"DROP TABLE IF EXISTS posts\")\n}\n\nfunc DropPostAuthors(dialect esperanto.Dialect) superbasic.Expression {\n\treturn superbasic.SQL(\"DROP TABLE IF EXISTS post_authors\")\n}\n\nfunc CreatePosts(dialect esperanto.Dialect) superbasic.Expression {\n\treturn superbasic.Compile(\"CREATE TABLE IF NOT EXISTS posts (\\n\\t?\\n)\",\n\t\tsuperbasic.Join(\",\\n\\t\",\n\t\t\tsuperbasic.Switch(dialect,\n\t\t\t\tsuperbasic.Case(esperanto.Postgres, superbasic.SQL(\"id SERIAL PRIMARY KEY\")),\n\t\t\t\tsuperbasic.Case(esperanto.Sqlite, superbasic.SQL(\"id INTEGER PRIMARY KEY AUTOINCREMENT\")),\n\t\t\t),\n\t\t\tsuperbasic.SQL(\"title TEXT NOT NULL\"),\n\t\t))\n}\n\nfunc CreatePostAuthors(dialect esperanto.Dialect) superbasic.Expression {\n\treturn superbasic.Compile(\"CREATE TABLE IF NOT EXISTS post_authors (\\n\\t?\\n)\",\n\t\tsuperbasic.Join(\",\\n\\t\",\n\t\t\tsuperbasic.SQL(\"post_id INTEGER REFERENCES posts (id) ON DELETE CASCADE\"),\n\t\t\tsuperbasic.SQL(\"author_id INTEGER REFERENCES authors (id) ON DELETE RESTRICT\"),\n\t\t\tsuperbasic.SQL(\"PRIMARY KEY (post_id, author_id)\"),\n\t\t),\n\t)\n}\n\ntype QueryPostOptions struct{}\n\nfunc PostQuery(dialect esperanto.Dialect, options QueryPostOptions) (superbasic.Expression, []scan.Column[Post]) {\n\treturn superbasic.Compile(`\n\tSELECT posts.id, posts.title, ? AS authors\n\tFROM posts\n\tLEFT JOIN post_authors ON post_authors.post_id = posts.id\n\tLEFT JOIN authors ON authors.id = post_authors.author_id\n\tGROUP BY posts.id, posts.title`,\n\t\t\tsuperbasic.Switch(dialect,\n\t\t\t\tsuperbasic.Case(esperanto.Postgres, superbasic.SQL(\"JSON_AGG(JSON_BUILD_OBJECT('id', authors.id, 'name', authors.name))\")),\n\t\t\t\tsuperbasic.Case(esperanto.Sqlite, superbasic.SQL(`CASE WHEN authors.id IS NULL THEN '[]' \n\t\tELSE JSON_GROUP_ARRAY(JSON_OBJECT('id', authors.id, 'name', authors.name)) END`)),\n\t\t\t)),\n\t\t[]scan.Column[Post]{\n\t\t\tscan.Any(func(post *Post, id int64) { post.ID = id }),\n\t\t\tscan.Any(func(post *Post, title string) { post.Title = title }),\n\t\t\tscan.AnyErr(func(post *Post, authors []byte) error { return json.Unmarshal(authors, \u0026post.Authors) }),\n\t\t}\n}\n\ntype InsertPostOptions struct {\n\tTitle   string\n\tAuthors []int64\n}\n\nfunc PostInsert(dialect esperanto.Dialect, options []InsertPostOptions) (superbasic.Expression, []scan.Column[PostEntity]) {\n\treturn superbasic.Compile(\"INSERT INTO posts (title) VALUES ? RETURNING id, title\", superbasic.Join(\", \", superbasic.Map(options, func(_ int, option InsertPostOptions) superbasic.Expression {\n\t\t\treturn superbasic.Values{option.Title}\n\t\t})...)),\n\t\t[]scan.Column[PostEntity]{\n\t\t\tscan.Any(func(post *PostEntity, id int64) { post.ID = id }),\n\t\t\tscan.Any(func(post *PostEntity, title string) { post.Title = title }),\n\t\t}\n}\n\nfunc PostAuthorsInsert(dialect esperanto.Dialect, options []InsertPostOptions, entities []PostEntity) superbasic.Expression {\n\treturn superbasic.Compile(\"INSERT INTO post_authors (post_id, author_id) VALUES ?\",\n\t\tsuperbasic.Join(\", \", superbasic.Map(entities, func(index int, entity PostEntity) superbasic.Expression {\n\t\t\treturn superbasic.Join(\", \", superbasic.Map(options[index].Authors, func(_ int, author int64) superbasic.Expression {\n\t\t\t\treturn superbasic.Values{entity.ID, author}\n\t\t\t})...)\n\t\t})...),\n\t)\n}\n\nfunc PostInsertOne(dialect esperanto.Dialect, options InsertPostOptions) (superbasic.Expression, []scan.Column[Post]) {\n\treturn superbasic.SQL(\"INSERT INTO posts (title) VALUES (?) RETURNING id, title\", options.Title),\n\t\t[]scan.Column[Post]{\n\t\t\tscan.Any(func(post *Post, id int64) { post.ID = id }),\n\t\t\tscan.Any(func(post *Post, title string) { post.Title = title }),\n\t\t}\n}\n\nfunc PostAuthorsInsertOne(dialect esperanto.Dialect, insert InsertPostOptions, post Post) superbasic.Expression {\n\treturn superbasic.Compile(\"INSERT INTO post_authors (post_id, author_id) VALUES ?\",\n\t\tsuperbasic.Join(\", \", superbasic.Map(insert.Authors, func(_ int, author int64) superbasic.Expression {\n\t\t\treturn superbasic.Values{post.ID, author}\n\t\t})...),\n\t)\n}\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwroge%2Fesperanto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwroge%2Fesperanto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwroge%2Fesperanto/lists"}