{"id":46742046,"url":"https://github.com/williabk198/jagsqlb","last_synced_at":"2026-03-09T17:36:52.543Z","repository":{"id":294894715,"uuid":"983198701","full_name":"williabk198/jagsqlb","owner":"williabk198","description":"Just Another Go(JAG) SQL Builder is a Go libarary aimed to give easy to use, ORM-like functions to aid in building SQL queries","archived":false,"fork":false,"pushed_at":"2025-12-05T04:32:05.000Z","size":158,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-08T12:08:49.396Z","etag":null,"topics":["go","golang","sql","sql-builder"],"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/williabk198.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-05-14T03:11:13.000Z","updated_at":"2025-12-05T04:29:41.000Z","dependencies_parsed_at":"2025-06-11T18:34:27.983Z","dependency_job_id":"5d0983fa-8d36-448a-a89e-267b3c83dcbb","html_url":"https://github.com/williabk198/jagsqlb","commit_stats":null,"previous_names":["williabk198/jagsqlb"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/williabk198/jagsqlb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williabk198%2Fjagsqlb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williabk198%2Fjagsqlb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williabk198%2Fjagsqlb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williabk198%2Fjagsqlb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/williabk198","download_url":"https://codeload.github.com/williabk198/jagsqlb/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/williabk198%2Fjagsqlb/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30304730,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T17:35:44.120Z","status":"ssl_error","status_checked_at":"2026-03-09T17:35:43.707Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["go","golang","sql","sql-builder"],"created_at":"2026-03-09T17:36:51.307Z","updated_at":"2026-03-09T17:36:52.537Z","avatar_url":"https://github.com/williabk198.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![go.mod](https://img.shields.io/github/go-mod/go-version/williabk198/jagsqlb)](go.mod)\n[![go report](https://goreportcard.com/badge/github.com/williabk198/jagsqlb)](https://goreportcard.com/report/github.com/williabk198/jagsqlb)\n[![test status](https://github.com/williabk198/jagsqlb/workflows/test/badge.svg)](https://github.com/williabk198/jagsqlb/actions/workflows/test.yaml)\n[![LICENSE](https://img.shields.io/github/license/williabk198/jagsqlb)](LICENSE) \n\n# Just Another Go(JAG) SQL Builder\n\nThis library aims to provide an easy and simple way to build SQL queries that can be used with `database/sql`, or similar packages.\n\n\n## Table of Contents\n\n* [Usage](#usage)\n  * [Select Builder](#select-builder)\n  * [Insert Builder](#insert-builder)\n  * [Update Builder](#update-builder)\n  * [Delete Builder](#delete-builder)\n* [Struct Tags](#struct-tags)\n* [Marshalling Values](#marshalling-values)\n\n## Usage\n\nTo create a new SQL builder it's as simple as this:\n\n```go\nimport \"github.com/williabk198/jagsqlb\"\n\n// ...\n\nsqlBuilder := jagsqlb.NewSqlBuilder()\n```\n\nThe value returned by `jagsqlb.NewSqlBuilder` can be reused as many times as you would like \nif multiple queries are required to be built.\n\n### Select Builder\n\n*__IMPORTANT:__* Wrapping columns in functions (e.g. `SUM(col1)`) is not supported. Which also means,\n`GROUP BY` is also not supported in this version either. Additionally, type casting using `::` is also unsupported in this version. \n\nTo create a simple `SELECT` statement like this: `SELECT * FROM \"customers\";` All you would need to write is this:\n\n```go\nqueryStr, queryParams, err := sqlBuilder.Select(\"customers\", \"*\").Build()\n```\n\nIn this example, the returned values for `Build` would be `SELECT * FROM \"customers\";`, `nil` and `nil` respectively.\n\nIf you are using an SQL dialect that supports providing schema names in the table specifier (e.g. PostgreSQL),\nthat is also supported here:\n\n```go\nsqlBuilder.Select(\"public.customers\", \"*\").Build()\n```\n\nAlso, if you want to select a subset of columns, we can handle that too:\n\n```go\nsqlBuilder.Select(\"customers\", \"name\", \"street\", \"city\", \"state\").Build()\n```\n\nOn top of that, you can also query from multiple tables like so:\n\n```go\nqueryStr, queryParams, err := sqlBuilder.Select(\"customers\", \"*\").Table(\"orders\", \"*\").Build()\n```\n\nThis will result in a `queryStr` value of: \n```sql\nSELECT \"customers\".*, \"orders\".* FROM \"customers\", \"orders\";\n```\n\nAliasing table specifiers and column specifiers are also possible as well:\n\n```go\nqueryStr, queryParams, err := sqlBuilder.Select(\n  \"persons AS p\",\n  \"given_name AS first_name\",\n  \"family_name AS last_name\",\n).Table(\"contact_info AS ci\", \"*\").Build()\n```\n\nThis will result in the following `queryStr` value:\n\n```sql\nSELECT \"p\".\"given_name\" AS \"first_name\", \"p\".\"family_name\" AS \"last_name\", \"ci\".* FROM \"persons\" AS \"p\", \"contact_info\" AS \"ci\";\n``` \n\nBy itself like this, this isn't too useful, but comes in rather handy when dealing with Joins.\n\n*__IMPORTANT:__* `AS` _MUST_ be all uppercase. Otherwise, an error will be returned. This may change in future versions.\n\n*__IMPORTANT:__* Alias names must not have any spaces in them. Even if quoted. An error will be returned otherwise.\nThis may change in future versions.\n\n#### Join Clause\n\nThe `SELECT` statement builder can also handle creating joins. So, if you wanted to create a query like this:\n```sql\nSELECT \"i\".*, \"s\".\"date\" AS \"sales_date\" \nFROM \"inventory\" AS \"i\" \nLEFT JOIN \"sales\" AS \"s\" ON \"i\".\"id\" = \"s\".\"inventory_id\";\n```\n\nThen you would write the following code:\n```go\n// package \u0026 other imports...\nimport (\n  \"github.com/williabk198/jagsqlb/condition\"\n  \"github.com/williabk198/jagsqlb/join\"\n)\n\n// Other code...\n\nqueryStr, queryParams, err := sqlBuilder.Select(\"inventory AS i\", \"*\").Join(join.TypeLeft, \"sales AS s\", join.On(\n  condition.Equals(\"i.id\", condition.ColumnValue(\"s.inventory_id\")),\n), \"date AS sales_date\").Build()\n```\n\nHopefully it's easy to tell what's going on with the `Join` function. In the case that it isn't, here is a break down:\n\n* The first parameter represents the type of join to perform, in which there are constants in the `join` package that\nrepresent the most of the available join types. \n\n* The second parameter is the table to join. An alias can be provided like shown above and follows the same rules\n  as aliases mentioned in the previous section.\n\n* The third parameter represent how to join the tables. There are two functions in the `join` that will handle\n  populating this parameter for you: `On` and `Using`.\n\n* The forth parameter and beyond represent the columns from the table provided in parameter #2 \n  to include in the result set of the query. Aliases can be provided like shown above and follows the same rules\n  as aliases mentioned in the previous section.\n\nHere's another example of building a query with a join. This time utilizing the `join.Using` function\n\n```go\nqueryStr, queryParams, err := sqlBuilder.Select(\"inventory AS i\", \"*\").Join(join.TypeLeft, \"sales AS s\", join.Using(\n  \"inventory_id\",\n), \"date AS sales_date\").Build()\n```\n\n#### Where Clause\n\nYou can also add a `WHERE` clause using the `SELECT` builder as well. That can be done like so:\n\n```go\n// package \u0026 other imports...\nimport \"github.com/williabk198/jagsqlb/condition\"\n\n// Other code...\nqueryStr, queryParams, err := sqlBuilder.Select(\"person\", \"*\").Where(\n  condition.Between(\"dob\", time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC))\n  condition.GroupedOr(\n    condition.Equals(\"family_name\", \"Smith\"),\n    condition.Equals(\"family_name\", \"Lee\"),\n  )\n).Or(condition.LessThan(\"dob\", time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC))).Build()\n```\n\nThis code will produce the following for `queryStr` and `queryParams` values respectively: \n```sql\nSELECT * FROM person WHERE \"dob\" BETWEEN $1 AND $2 AND (\"family_name\" = $3 OR \"family_name\" = $4) OR \"dob\" \u003c $5;\n```\n\n```\n[]any{\n  time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),\n  time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC),\n  \"Smith\",\n  \"Lee\",\n  time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC),\n}\n```\n\nYou can also define a `WHERE` condition after a `JOIN` as well!\n```go\nqueryStr, queryParams, err := sqlBuilder.Select(\"customers AS c\", \"*\").Join(\n  join.TypeInner,\n  \"subscriptions AS s\",\n  join.Using(\"customer_id\"),\n  \"ends AS payment_due\"\n).Where(\n  condition.LessThan(\"s.ends\", time.Now().Truncate(24*time.Hour)),\n  condition.GreaterThan(\"c.last_active\", condition.ColumnValue(\"s.ends\"))\n).Build()\n```\n\nThis code will have the following as the value for `queryStr` after execution(minus any new-line characters):\n```sql\nSELECT \"c\".*, \"s\".\"ends\" AS \"payment_due\" FROM \"customer\" AS \"c\"\nINNER JOIN \"subscriptions\" AS \"s\" USING(\"customer_id\")\nWHERE \"s\".\"ends\" \u003c $1 AND \"c\".\"last_active\" \u003e \"s\".\"ends\"\n```\n\nWith `queryParams` looking like this:\n```\n[]any{\n  time.Now().Truncate(24*time.Hour) // The evaluation of this expression, of course.\n}\n```\n\nAs you can see, if you want to compare two columns, then you will need to use `condition.ColumnValue`. Otherwise, it will\nget parameterized as a string value, which will cause erroneous behavior.\n\n### Insert Builder\n\n*__IMPORTANT:__* Using Select statements inside of the Insert Builder is not supported in this version.\n\nThere are a couple of ways that you can build and insert statement with this library.\n\nLike this:\n\n```go\nqueryStr, queryParams, err :=  sqlBuilder.Insert(\"inventory\").Columns(\"name\", \"price\").Values(\n  []any{\"Car\", 18365.0},\n).Returning(\"id\").Build()\n```\n\nOr like this:\n\n```go\ntype Inventory struct {\n  ID int `jagsqlb:\"id;omit\"`\n  ProductName string `jagsqlb:\"name\"`\n  Price float64 `jagsqlb:\"price\"`\n}\n\ncar := Inventory{\n  ProductName: \"Car\"\n  Price: 18365.0\n}\n\nqueryStr, queryParams, err :=  sqlBuilder.Insert(\"inventory\").Data(car).Returning(\"id\").Build()\n```\n\nBoth of the above code snippets will result in the same result in the same `queryStr` and `queryParams` result,\nrespectively:\n\n```sql\nINSERT INTO \"inventory\" (\"name\", \"price\") VALUES ($1, $2) RETURNING \"id\";\n```\n\n```go\n[]any{\"Car\", 18365.0}\n```\n\n### Update Builder\n\nLike the Insert Builder, the Update Builder also has two ways that to build out the query.\n\n```go\nqueryStr, queryParams, err :=  sqlBuilder.Update(\"inventory\").SetMap(map[string]any{\n  \"price\": 19.99\n}).Where(\n  condition.Between(\"price\", 19.75, 20.25),\n).Build()\n```\n\n```go\ntype UpdateInvPrice struct {\n  Price float64 `jagsqlb:\"price\"`\n}\n\nnewInvPrice := UpdateInvPrice{\n  Price: 19.99\n}\n\nqueryStr, queryParams, err :=  sqlBuilder.Update(\"inventory\").SetStruct(newInvPrice).Where(\n  condition.Between(\"price\", 19.75, 20.25),\n).Build()\n```\n\nBoth of the above code snippets will result in the same result in the same `queryStr` and `queryParams` result,\nrespectively:\n\n```sql\nUPDATE \"inventory\" SET \"price\"=$1 WHERE \"price\" BETWEEN $2 AND $3\n```\n\n```go\n[]any{19.99, 19.75, 20.25}\n```\n\n### Delete Builder\n\nTo create a `DELETE` simple delete statement, all you'll need is this:\n\n```go\nqueryStr, queryParams, err := sqlBuilder.Delete(\"customers\").Build()\n```\n\nA `WHERE` clause can also be added like so:\n\n```go\nqueryStr, queryParams, err := sqlBuilder.Delete(\"customers\").Where(condition.Equals(\"id\", customerID)).Build()\n```\n\nAdditionally, a `USING` clause can be used as will which acts just like using `FROM` in a `SELECT` statement.\n\n```go\nqueryStr, queryParams, err := sqlBuilder.Delete(\"customers AS c\").Using(\"metadata.customers AS mc\").Where(\n  condition.Equals(\"c.id\", condition.ColumnValue(\"mc.customerID\")),\n  condition.LessThan(\"mc.last_login\", twoYearsAgo),\n).Build()\n```\n\n## Struct Tags\n\nAs a part of this package, struct tags were added to make things easier to build `INSERT` and `UPDATE` queries.\n\nTo correlate a field to column name with a struct tag works very similarly to other ORM/ORM-like packages:\n\n```go\ntype Person struct {\n  DateOfBirth time.Time `jagsqlb:\"dob\"`\n}\n```\n\n*__NOTE:__* If a field does not have a column name defined with the `jagsqlb` tag, then the name of field\nwill be used as the column name by default.\n\n### Omitting Fields\n\nIf there are fields that you don't want to be included during the execution of the insert or update process,\nthen you annotate the field(s) in question by adding `;omit` after the column name like so:\n\n```go\ntype TestData struct {\n  ID          uuid.UUID `jagsqlb:\";omit\"`\n  DateOfBirth time.Time `jagsqlb:\"dob\"`\n}\n```\n\n*__NOTE:__* As you can see with the above example, you do not have to provide a column name if so desired.\n\n#### Omitting on Insert Only\n\nIf there is a property that you do not want to be included while inserting but should be included while updating,\nyou can use the `omit-insert` tag like so:\n\n```go\ntype TestData[T any] struct {\n  SomeValue T `jagsqlb:\"some_val;omit-insert\"`\n}\n```\n\n#### Omitting on Update Only\n\nThere is also an option to omit a value while updating, but include it for inserting. This is particularly useful\nif there is a foreign key value that should not be updated.\n\n```go\ntype Person struct {\n  ID          uuid.UUID `jagsqlb:\";omit\"`\n  Name        string    `jagsqlb:\"full_name\"`\n  DateOfBirth time.Time `jagsqlb:\"dob\"`\n}\n\ntype Address struct {\n  ID       uuid.UUID `jagsqlb:\";omit\"`\n  PersonID uuid.UUID `jagsqlb:\"person_id;omit-update\"`\n  // ...\n}\n\n```\n\n### Inlining Nested Structs\n\nIf the struct that you are using to insert or update entries in the database has a nested struct within it that\nrepresents other columns within the table, you can use the `;inline` tag to ensure that those values are\nproperly included in the query.\n\nFor example:\n\n```go\ntype NameData struct {\n  GivenName  string `jagsqlb:\"given_name\"`\n  FamilyName string `jagsqlb:\"family_name\"`\n}\n\ntype Person struct {\n  ID          uuid.UUID `jagsqlb:\";omit\"`\n  Name        NameData  `jagsqlb:\";inline\"`\n  DateOfBirth time.Time `jagsqlb:\"dob\"`\n}\n\nperson := Person{\n  Name: NameData{\n    GivenName: \"Some\",\n    FamilyName: \"Guy\"\n  },\n  DateOfBirth: time.Unix(0, 0),\n}\n\nqueryStr, queryParams, err := sqlBuilder.Insert(\"person\").Data(person).Build()\n```\n\nThis will result in the following value for `queryStr`\n\n```sql\nINSERT INTO \"person\" (\"given_name\", \"family_name\", \"dob\") VALUES ($1, $2, $3);\n```\n\n## Marshalling Values\n\nWhen using the Insert or Update Builders, if the value you want to store in the database is slightly different\nfrom what is stored in the provided struct field, then you will need to implement the `QueryMarshaler` interface\nfor that value.\n\nThe `QueryMarshaler` interface that specifies that a `MarshalQuery` function to be implemented which takes in no arguments\nand returns a `string` and an `error`.\n\nHere's an example:\n\n```go\ntype PronounData struct {\n  Subject string\n  Object  string\n}\n\nfunc (pd PronounData) MarshalQuery() (string, error) {\n  return fmt.Sprintf(\"%s/%s\", pd.Subject, pd.Object), nil\n}\n\ntype NameData struct {\n  GivenName  string `jagsqlb:\"given_name\"`\n  FamilyName string `jagsqlb:\"family_name\"`\n}\n\ntype Person struct {\n  ID          uuid.UUID   `jagsqlb:\";omit\"`\n  Name        NameData    `jagsqlb:\";inline\"`\n  DateOfBirth time.Time   `jagsqlb:\"dob\"`\n  Pronouns    PronounData `jagsqlb:\"pronouns\"`\n}\n\nexamplePerson := Person{\n  Name: NameData{\n    GivenName:  \"Testy\",\n    FamilyName: \"McTesterson\",\n  }\n  DateOfBirth: time.Unix(0, 0),\n  Pronouns: PronounData{\n    Subject: \"they\"\n    Object:  \"them\"\n  }\n}\n\nqueryStr, queryParams, err := sqlBuilder.Insert(\"persons\").Data(examplePerson).Build()\n```\n\nThe `queryStr` and `queryParams` values will be the following, respectively:\n\n```sql\nINSERT INTO \"persons\" (\"given_name\", \"family_name\", \"dob\", \"pronouns\") VALUES ($1, $2, $3, $4);\n```\n\n```go\n[]any{\n  \"Testy\",\n  \"McTesterson\",\n  time.Date(1970, time.January, 1, 0, 0, 0, 0, time.Local),\n  \"they/them\",\n}\n```\n\n*__IMPORTANT:__* `QueryMarshaler` can only convert a type to a `string` value. If you wish to convert something\ninto a non-string type, then you will need to do the conversion yourself and store the value as the appropriate\ntype within the struct.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilliabk198%2Fjagsqlb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwilliabk198%2Fjagsqlb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilliabk198%2Fjagsqlb/lists"}