{"id":13619111,"url":"https://github.com/aldy505/bob","last_synced_at":"2025-05-06T19:14:05.434Z","repository":{"id":45496101,"uuid":"379844399","full_name":"aldy505/bob","owner":"aldy505","description":"SQL query builder for Go. Might also be used as an extension for Squirrel.","archived":false,"fork":false,"pushed_at":"2024-12-09T14:00:11.000Z","size":72,"stargazers_count":18,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-06T19:13:58.595Z","etag":null,"topics":["go","golang","query-builder","sql","sql-query"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/aldy505/bob","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/aldy505.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":"aldy505","ko_fi":"aldy505","liberapay":"aldy505"}},"created_at":"2021-06-24T07:43:08.000Z","updated_at":"2025-04-30T05:11:26.000Z","dependencies_parsed_at":"2024-06-21T03:52:13.032Z","dependency_job_id":"237bde73-eb6c-407f-875c-539d6d4bc049","html_url":"https://github.com/aldy505/bob","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aldy505%2Fbob","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aldy505%2Fbob/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aldy505%2Fbob/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aldy505%2Fbob/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aldy505","download_url":"https://codeload.github.com/aldy505/bob/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252752059,"owners_count":21798723,"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":["go","golang","query-builder","sql","sql-query"],"created_at":"2024-08-01T21:00:34.884Z","updated_at":"2025-05-06T19:14:05.409Z","avatar_url":"https://github.com/aldy505.png","language":"Go","readme":"# Bob - SQL Query Builder\n\n[![Go Reference][pkg-go-dev-badge]][pkg-go-dev-link]\n[![Go Report Card][go-report-badge]][go-report-link]\n![GitHub][license-badge]\n[![CodeFactor][codefactor-badge]][codefactor-link]\n[![codecov][codecov-badge]][codecov-link]\n[![Codacy Badge][codacy-badge]][codacy-link]\n[![Test and coverage][actions-badge]][actions-link]\n\nBob is an SQL builder library initially made as an extension for [Squirrel][squirrel-url]\nwith functionality like [Knex][knex-url] (from the Node.js world). Squirrel itself\ndoesn't provide other types of queries for creating a table, upsert,\nand some other things. Bob is meant to fill those gaps.\n\nThe difference between Bob and Squirrel is that Bob is solely a query builder.\nThe users have to execute and manage the SQL connection themselves.\nMeaning there is no ExecWith() function implemented in Bob, as you can\nfind it on Squirrel.\n\nThe purpose of an SQL query builder is to prevent any typo or mistypes\non the SQL queries. Although also with that reason, Bob might not always\nhave the right query for you, depending on what you are doing with the\nSQL query. It might sometimes be better for you to write the SQL query\nyourself, if your problem is specific and needs some micro-tweaks.\n\nWith that being said, I hope you enjoy using Bob and consider starring or\nreporting any issues regarding the usage of Bob in your projects.\n\nOh, and of course, heavily inspired by [Bob the Builder][bob-wikipedia].\n\n## Usage\n\n```go\nimport \"github.com/aldy505/bob\"\n```\n\nLike any other Go projects when you're using Go modules, just put that\ntext right there on the top of your projects, do `go mod tidy` and\nyou are good to go.\n\nEither way, I'm not 100% confident enough to say that this thing is\nproduction ready. But, the way I see it, it's good enough to be used\non a production-level application. In fact, I'm using it on one of my\ncurrent projects that's getting around 100-200 hits per day.\n\nIf you have any feature request or improvement ideas for the project,\nplease kindly open an issue\n\n### Create a table\n\n```go\nimport \"github.com/aldy505/bob\"\n\nfunc main() {\n  // Note that CREATE TABLE doesn't returns args params.\n  sql, _, err := bob.\n    CreateTable(\"tableName\").\n    // The first parameter is the column's name.\n    // The second parameter and so on forth are extras.\n    StringColumn(\"id\", \"NOT NULL\", \"PRIMARY KEY\", \"AUTOINCREMENT\").\n    StringColumn(\"email\", \"NOT NULL\", \"UNIQUE\").\n    // See the list of available column definition types through pkg.go.dev or scroll down below.\n    TextColumn(\"password\").\n    // Or add your custom type.\n    AddColumn(bob.ColumnDef{Name: \"tableName\", Type: \"customType\", Extras: []string{\"NOT NULL\"}}).\n    ToSql()\n  if err != nil {\n    // handle your error\n  }\n}\n```\n\nAvailable column definition types (please be aware that some only works on certain database):\n\n- `StringColumn()` - Default to `VARCHAR(255)`\n- `TextColumn()` - Default to `TEXT`\n- `UUIDColumn()` - Defaults to `UUID`\n- `BooleanColumn()` - Defaults to `BOOLEAN`\n- `IntegerColumn()` - Defaults to `INTEGER`. Postgres and SQLite only.\n- `IntColumn()` - Defaults to `INT`. MySQL and MSSQL only.\n- `RealColumn()` - Defaults to `REAL`. Postgres, MSSQL, and SQLite only.\n- `FloatColumn()` - Defaults to `FLOAT`. Postgres and SQLite only.\n- `DateTimeColumn()` - Defaults to `DATETIME`.\n- `TimeStampColumn()` - Defaults to `TIMESTAMP`.\n- `TimeColumn()` - Defaults to `TIME`.\n- `DateColumn()` - Defaults to `DATE`.\n- `JSONColumn()` - Defaults to `JSON`. MySQL and Postgres only.\n- `JSONBColumn()` - Defaults to `JSONB`. Postgres only.\n- `BlobColumn()` - Defaults to `BLOB`. MySQL and SQLite only.\n\nFor any other types, please use `AddColumn()`.\n\nAnother builder of `bob.CreateTableIfNotExists()` is also available.\n\n### Create index\n\n```go\nfunc main() {\n  sql, _, err := bob.\n    CreateIndex(\"idx_email\").\n    On(\"users\").\n    // To create a CREATE UNIQUE INDEX ...\n    Unique().\n    // Method \"Spatial()\" and \"FullText()\" are also available.\n    // You can specify as many columns as you like.\n    Columns(bob.IndexColumn{Name: \"email\", Collate: \"DEFAULT\", Extras: []string{\"ASC\"}}).\n    ToSql()\n  if err != nil {\n    log.Fatal(err)\n  }\n}\n```\n\nAnother builder of `bob.CreateIndexIfNotExists()` is also available.\n\n### Check if a table exists\n\n```go\nfunc main() {\n  sql, args, err := bob.HasTable(\"users\").ToSql()\n  if err != nil {\n    log.Fatal(err)\n  }\n}\n```\n\n### Check if a column exists\n\n```go\nfunc main() {\n  sql, args, err := bob.HasColumn(\"email\").ToSql()\n  if err != nil {\n    log.Fatal(err)\n  }\n}\n```\n\n### Drop table\n\n```go\nfunc main() {\n  sql, _, err := bob.DropTable(\"users\").ToSql()\n  if err != nil {\n    log.Fatal(err)\n  }\n  // sql = \"DROP TABLE users;\"\n\n  sql, _, err = bob.DropTableIfExists(\"users\").ToSql()\n  if err != nil {\n    log.Fatal(err)\n  }\n  // sql = \"DROP TABLE IF EXISTS users;\"\n\n  sql, _, err = bob.DropTable(\"users\").Cascade().ToSql()\n  if err != nil {\n    log.Fatal(err)\n  }\n  // sql = \"DROP TABLE users CASCADE;\"\n\n  sql, _, err = bob.DropTable(\"users\").Restrict().ToSql()\n  if err != nil {\n    log.Fatal(err)\n  }\n  // sql = \"DROP TABLE users RESTRICT;\"\n}\n```\n\n### Truncate table\n\n```go\nfunc main() {\n  sql, _, err := bob.Truncate(\"users\").ToSql()\n  if err != nil {\n    log.Fatal(err)\n  }\n}\n```\n\n### Rename table\n\n```go\nfunc main() {\n  sql, _, err := bob.RenameTable(\"users\", \"people\").ToSql()\n  if err != nil {\n    log.Fatal(err)\n  }\n}\n```\n\n### Upsert\n\n```go\nfunc main() {\n  sql, args, err := bob.\n    // Notice that you should give database dialect on the second params.\n    // Available database dialect are MySQL, PostgreSQL, SQLite, and MSSQL.\n    Upsert(\"users\", bob.MySQL).\n    Columns(\"name\", \"email\", \"age\").\n    // You could do multiple Values() call, but I'd suggest to not do it.\n    // Because this is an upsert function, not an insert one.\n    Values(\"Thomas Mueler\", \"tmueler@something.com\", 25).\n    Replace(\"age\", 25).\n    ToSql()\n\n  // Another example for PostgreSQL\n  sql, args, err = bob.\n    Upsert(\"users\", bob.PostgreSQL).\n    Columns(\"name\", \"email\", \"age\").\n    Values(\"Billy Urtha\", \"billu@something.com\", 30).\n    Key(\"email\").\n    Replace(\"age\", 40).\n    ToSql()\n\n  // One more time, for MSSQL / SQL Server.\n  sql, args, err = bob.\n    Upsert(\"users\", bob.MSSQL).\n    Columns(\"name\", \"email\", \"age\").\n    Values(\"George Rust\", \"georgee@something.com\", 19).\n    Key(\"email\", \"georgee@something.com\").\n    Replace(\"age\", 18).\n    ToSql()\n}\n```\n\n### Placeholder format / Dialect\n\nDefault placeholder is a question mark (MySQL-like). If you want to change it, simply use something like this:\n\n```go\nfunc main() {\n  // Option 1\n  sql, args, err := bob.HasTable(\"users\").PlaceholderFormat(bob.Dollar).ToSql()\n  if err != nil {\n    log.Fatal(err)\n  }\n\n  // Option 2\n  sql, args, err = bob.HasTable(\"users\").ToSql()\n  if err != nil {\n    log.Fatal(err)\n  }\n  correctPlaceholder := bob.ReplacePlaceholder(sql, bob.Dollar)\n}\n```\n\nAvailable placeholder formats:\n\n- `bob.Question` - `INSERT INTO \"users\" (name) VALUES (?)`\n- `bob.Dollar` - `INSERT INTO \"users\" (name) VALUES ($1)`\n- `bob.Colon` - `INSERT INTO \"users\" (name) VALUES (:1)`\n- `bob.AtP` - `INSERT INTO \"users\" (name) VALUES (@p1)`\n\n### With pgx (PostgreSQL)\n\n```go\nimport (\n  \"context\"\n  \"log\"\n  \"strings\"\n\n  \"github.com/aldy505/bob\"\n  \"github.com/jackc/pgx/v4\"\n)\n\nfunc main() {\n  db := pgx.Connect()\n\n  // Check if a table exists\n  sql, args, err = bob.HasTable(\"users\").PlaceholderFormat(bob.Dollar).ToSql()\n  if err != nil {\n    log.Fatal(err)\n  }\n\n  var hasTableUsers bool\n  err = db.QueryRow(context.Background(), sql, args...).Scan(\u0026hasTableUsers)\n  if err != nil {\n    if err == bob.ErrEmptyTablePg {\n      hasTableUsers = false\n    } else {\n      log.Fatal(err)\n    }\n  }\n\n  if !hasTableUsers {\n    // Create \"users\" table\n    sql, _, err := bob.\n      CreateTable(\"users\").\n      IntegerColumn(\"id\", \"PRIMARY KEY\", \"SERIAL\").\n      StringColumn(\"name\", \"NOT NULL\").\n      TextColumn(\"password\", \"NOT NULL\").\n      DateColumn(\"created_at\").\n      ToSql()\n    if err != nil {\n      log.Fatal(err)\n    }\n\n    _, err = db.Query(context.Background(), splitQuery[i])\n    if err != nil {\n      log.Fatal(err)\n    }\n\n    // Create another table, this time with CREATE TABLE IF NOT EXISTS\n    sql, _, err := bob.\n      CreateTableIfNotExists(\"inventory\").\n      UUIDColumn(\"id\", \"PRIMARY KEY\").\n      IntegerColumn(\"userID\", \"FOREIGN KEY REFERENCES users(id)\").\n      JSONColumn(\"items\").\n      IntegerColumn(\"quantity\").\n      ToSql()\n    if err != nil {\n      log.Fatal(err)\n    }\n\n    _, err = db.Query(context.Background(), inventoryQuery[i])\n    if err != nil {\n      log.Fatal(err)\n    }\n  }\n}\n```\n\n## Features\n\n- `bob.CreateTable(tableName)` - Basic SQL create table\n- `bob.CreateTableIfNotExists(tableName)` - Create table if not exists\n- `bob.CreateIndex(indexName)` - Basic SQL create index\n- `bob.CreateIndexIfNotExists(tableName)` - Create index if not exists\n- `bob.HasTable(tableName)` - Checks if column exists (return error if false, check example above for error handling)\n- `bob.HasColumn(columnName)` - Check if a column exists on current table\n- `bob.DropTable(tableName)` - Drop a table (`drop table \"users\"`)\n- `bob.DropTableIfExists(tableName)` - Drop a table if exists (`drop table if exists \"users\"`)\n- `bob.RenameTable(currentTable, desiredName)` - Rename a table (`rename table \"users\" to \"people\"`)\n- `bob.Truncate(tableName)` - Truncate a table (`truncate \"users\"`)\n- `bob.Upsert(tableName, dialect)` - UPSERT function (`insert into \"users\" (\"name\", \"email\") values (?, ?) on duplicate key update email = ?`)\n\n## Contributing\n\nContributions are always welcome! As long as you add a test for your changes.\n\n## License\n\nBob is licensed under [MIT license](./LICENSE)\n\n[squirrel-url]: https://github.com/Masterminds/squirrel\n[knex-url]: https://knexjs.org/\n[bob-wikipedia]: https://en.wikipedia.org/wiki/Bob_the_Builder\n[pkg-go-dev-badge]: https://pkg.go.dev/badge/github.com/aldy505/bob.svg\n[pkg-go-dev-link]: https://pkg.go.dev/github.com/aldy505/bob\n[go-report-badge]: https://goreportcard.com/badge/github.com/aldy505/bob\n[go-report-link]: https://goreportcard.com/report/github.com/aldy505/bob\n[license-badge]: https://img.shields.io/github/license/aldy505/bob\n[codefactor-link]: https://www.codefactor.io/repository/github/aldy505/bob\n[codefactor-badge]: https://www.codefactor.io/repository/github/aldy505/bob/badge\n[codecov-badge]: https://codecov.io/gh/aldy505/bob/branch/master/graph/badge.svg?token=Noeexg5xEJ\n[codecov-link]: https://codecov.io/gh/aldy505/bob\n[codacy-badge]: https://app.codacy.com/project/badge/Grade/9b78970127c74c1a923533e05f65848d\n[codacy-link]: https://www.codacy.com/gh/aldy505/bob/dashboard?utm_source=github.com\u0026utm_medium=referral\u0026utm_content=aldy505/bob\u0026utm_campaign=Badge_Grade\n[actions-badge]: https://github.com/aldy505/bob/actions/workflows/coverage.yml/badge.svg\n[actions-link]: https://github.com/aldy505/bob/actions/workflows/coverage.yml\n","funding_links":["https://github.com/sponsors/aldy505","https://ko-fi.com/aldy505","https://liberapay.com/aldy505"],"categories":["B"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faldy505%2Fbob","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faldy505%2Fbob","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faldy505%2Fbob/lists"}