{"id":28955835,"url":"https://github.com/albertprz/logograph","last_synced_at":"2025-06-23T20:10:15.047Z","repository":{"id":57732296,"uuid":"317879039","full_name":"albertprz/logograph","owner":"albertprz","description":"Minimalistic typesafe SQL DSL for Scala","archived":false,"fork":false,"pushed_at":"2025-01-30T18:27:51.000Z","size":235,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-01-30T18:32:48.281Z","etag":null,"topics":["dsl","metaprogramming","scala2","scala3","sql"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-2.1","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/albertprz.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":"2020-12-02T13:58:30.000Z","updated_at":"2025-01-30T18:27:55.000Z","dependencies_parsed_at":"2025-01-14T12:44:39.164Z","dependency_job_id":"6a8de22d-6ee8-4a86-be76-a21182c38554","html_url":"https://github.com/albertprz/logograph","commit_stats":null,"previous_names":["alberto-perez-1994/scalaql"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/albertprz/logograph","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/albertprz%2Flogograph","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/albertprz%2Flogograph/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/albertprz%2Flogograph/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/albertprz%2Flogograph/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/albertprz","download_url":"https://codeload.github.com/albertprz/logograph/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/albertprz%2Flogograph/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261548741,"owners_count":23175499,"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":["dsl","metaprogramming","scala2","scala3","sql"],"created_at":"2025-06-23T20:10:14.382Z","updated_at":"2025-06-23T20:10:15.032Z","avatar_url":"https://github.com/albertprz.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Logograph\n\n## Summary\nThis library provides a typesafe Scala DSL for generating SQL queries and statements\u003cbr\u003e\nThese statements can then be executed against a Database using the ***Logograph Context***, \u003cbr\u003e\nwhich uses JDBC for connecting to the Database, or any other Database connection middleware\u003cbr\u003e provided in the client application\u003cbr\u003e\n\n\nBy using Scala macros in order to generate the SQL statetements at compile-time,\u003cbr\u003e\nSQL generation provides a useful abstraction while having no performance \u003cbr\u003e\noverhead at runtime compared to running a raw SQL statement.\n\nThese SQL generation macros also perform validation on the SQL statements,\u003cbr\u003e\nso the library user can detect and fix several kinds of errors without the need to execute \u003cbr\u003e\nthe statement against the Database. This also means that most SQL syntax errors are guaranteed\u003cbr\u003e\nto never happen in production code.\n\nThe generated SQL statements can embed runtime values and are fully parameterised,\u003cbr\u003e\nso there is no risk of SQL Injection attacks.\n\nThe DSL works in a similar fashion to some other SQL compile-time DSLs available for Scala,\u003cbr\u003e\nfor instance [Quill](https://github.com/getquill/quill),\nbut it aims to provide a streamlined API, with a focus on simplicity\u003cbr\u003e\nfrom the user standpoint, leaving some advanced functionality aside, in order to adapt better\u003cbr\u003e\nto the most common business use cases, following a convention over configuration approac.\u003cbr\u003e\n\nThere is some in-project customization around both global naming conventions as well as explicit table and column names via the use of an optional ***logograph.conf*** file in the project. You can see an example [here](https://github.com/albertprz/logograph/blob/main/src/test/resources/logograph.conf)\n\nFor some examples of statements serialisation and execution (such as complex nested queries or query unions \u0026 intersections), you can check the [unit](https://github.com/albertprz/logograph/tree/main/src/test/scala) or [integration](https://github.com/albertprz/logograph/tree/main/src/it/scala) test folders. \n\n## Usage\n\nIn order to use this library, you would need to add it to the dependencies in build.sbt:\u003cbr\u003e\nAll the releases are cross-compiled and therefore available for both ***scala 3*** \u0026 ***scala 2.13***. \u003cbr\u003e All the tests also run for both Scala versions, since the macro implementations differ between the language versions.\n\n```scala\nlibraryDependencies += \"io.github.albertprz\" % \"logograph\" %% \"0.1.0\")\n```\n\nLogograph DSL aim is to reflect as closely as possible the underlying SQL representation,\u003cbr\u003e\nso the API is very SQL like:\n\n```scala\nimport com.albertprz.logograph._\n\nval qry = from[(Person, Address, Telephone)].select {\n  case (p, a, t) ⇒ Query(\n    Select          (Result (p.name, p.age, a.street, t.number))\n    Where           (a.street like \"%Baker St%\",\n                      p.name in names,\n                      coalesce (p.isEmployer, false)),\n    OrderBy         (desc (p.age)),\n    LeftJoin (a)    (a.id === p.addressId),\n    LeftJoin (t)    (t.id === p.telephoneId))\n  }\n```\n\nThe DSL is very concise and uses the same operator and functions that the SQL equivalent.\u003cbr\u003e\nThe SQL output for this query would be the following:\n\n```sql\nSELECT      p.[name], p.[age], a.[street], t.[number]\nFROM        [Person] AS p\nLEFT JOIN   [Address] AS a ON a.[id] = p.[addressId]\nLEFT JOIN   [Telephone] AS t ON t.[id] = p.[telephoneId]\nWHERE       (a.[street] like '%Baker St%') AND\n            (p.[name] in (?, ?, ?)) AND\n            (coalesce (p.[isEmployer], 0))\nORDER BY    p.[age] desc\n```\n\nAnd the parameters that were used in this query, for the runtime values are:\n\n```scala\n{@Application.names -\u003e [John, Mark, Thomas]}\n```\n\nThe query is generated in a fully typesafe manner. The query input tables must be specified by\u003cbr\u003e\ncase classes that extend the ***DbTable*** trait and the query result type must be a case class\u003cbr\u003e\nthat extends either the ***DbTable*** or the ***DbResult*** traits.\u003cbr\u003e\nThe qry value will be an object of type ***SelectStatement[Result]***, in this case.\n\n```scala\n// Database Table Models\ncase class Person (name: String, age: Int, isEmployer: Boolean, addressId: Int, telephoneId: Int)\n                    extends DbTable\ncase class Address (id: Int, street: String) extends DbTable\ncase class Telephone (id: Int, number: String) extends DbTable\n\n\n// Query Result Model\ncase class Result (name: String, age: Int, street: String, telephoneNumber: String) extends DbResult\n```\n\nAdditionally the SQL Statement API methods ending in ***Debug***, can be used in order to generate\u003cbr\u003e\na compile time error that will expose the SQL statement at compile time as well as the internal AST,\u003cbr\u003e\nthat was used to generate the SQL:\n\n```scala\nQueryClause (\n  SelectClause ([Field (p, name), Field (p, age), Field (a, street), Field (t, number)]),\n  FromClause ({p -\u003e Person}), [\n  LeftJoinClause (Address, a, [\n    Operation (===, [Field (a, id), Field (p, addressId)])]),\n  LeftJoinClause (Telephone, t, [\n    Operation (===, [Field (t, id), Field (p, telephoneId)])])],\n  WhereClause ([\n    Operation (like, [Field (a, street),\n      LiteralVal (\"%Baker St%\")]),\n    Operation (in, [Field (p, name), Identity (?)]),\n    Operation (coalesce, [Field (p, isEmployer), LiteralVal (0)])]),\n  OrderByClause ([\n    Operation (desc, [Field (p, age)])]))\n```\n\nThe API also exposes ***Insert***, ***Update*** \u0026 ***Delete*** statements, which have a common trait\n(***SQLStatefulStament***):\n\n```scala\nval stmts = Seq(insert(john),\n\n                insert(johnAddress),\n\n                update[Person] (p =\u003e (Map(p.name -\u003e \"Mark\",\n                                          p.age  -\u003e 50),\n                                      Where(p.age \u003e= 10))),\n\n                delete[Address] (a =\u003e Where(a.street \u003c\u003e \"Baker Street\"))\n```\n\nThese statements will generate the following SQL output:\n\n```sql\nINSERT INTO [Person] ([name], [age], [isEmployer], [addressId], [telephoneId])\nVALUES      (?, ?, ?, ?, ?)\n\nINSERT INTO [Address] ([id], [street])\nVALUES      (?, ?)\n\nUPDATE      [Person]\nSET         [name] = 'Mark',\n            [age] = 50\nWHERE       [age] \u003e= 10\n\nDELETE FROM [Address]\nWHERE       [street] \u003c\u003e 'Baker Street'\n```\n\nThe raw SQL and runtime params from any statement can be obtained at runtime \u003cbr\u003e\nby accessing the ***sql*** and ***params*** fields:\n\n```scala\n(qry.sql, qry.params)\n```\n\nAt last, the statements can be run against a Database by using implicitly / explicitly \u003cbr\u003e a in-scope ***LogographContext*** instance, using the appropiate JDBC connection object\u003cbr\u003e\n\n```scala\nval conn: Connection\nval qry: SelectStatement[Person]\nval stmt: UpdateStatement[Address]\n\nimplicit val context = new LogographContext[IO](conn)\nval resultsIO: IO[Seq[Person]] = qry.run()\nval operationIO: IO[Unit] = stmt.run()\n```\nor \n\n```scala\nval conn: Connection\nval qry: SelectStatement[Person]\nval stmts: List[SQLStatefulStatements]\n\nval context = new LogographContext[IO](conn)\nval resultsIO: IO[Seq[Person]] = context.run(qry)\nval operationsIO: IO[Unit] = context.run(stmts:_*)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falbertprz%2Flogograph","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falbertprz%2Flogograph","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falbertprz%2Flogograph/lists"}